mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9adcef7ef8 | ||
|
|
664ad1c001 | ||
|
|
0400fb91e6 | ||
|
|
dd81eae663 | ||
|
|
61720850f6 | ||
|
|
e86f6eea0a | ||
|
|
b1845f43e7 | ||
|
|
958b4c9117 | ||
|
|
385412b6c0 | ||
|
|
e38e83169a | ||
|
|
f2a0fa2640 | ||
|
|
5de4fff167 | ||
|
|
9f33284d23 | ||
|
|
fb6f3b6771 | ||
|
|
b4242884d8 | ||
|
|
96687f030c | ||
|
|
3327adcc72 | ||
|
|
f63f26f842 | ||
|
|
46e923f671 | ||
|
|
b623dca531 | ||
|
|
6189230202 | ||
|
|
10a12b2a6f | ||
|
|
915c90ec0e | ||
|
|
b27c79a010 | ||
|
|
70f7fc3250 | ||
|
|
2955407c3a | ||
|
|
6e0597951e | ||
|
|
8c1f5c254f | ||
|
|
0ccaba2a05 | ||
|
|
4646cd45c4 | ||
|
|
2d6abcade4 | ||
|
|
0b129d1f7d | ||
|
|
58ef1ca899 | ||
|
|
13712712de | ||
|
|
ab8129ab8a | ||
|
|
87f35fbd3e | ||
|
|
9e58371114 | ||
|
|
1d4dc5eb22 | ||
|
|
b54951489c | ||
|
|
e8727ff6e0 | ||
|
|
a6e1cf9d20 | ||
|
|
ded1d5a1af | ||
|
|
dc0bbf78f7 | ||
|
|
b634fd7162 | ||
|
|
22f277e034 | ||
|
|
7e62c5d67a | ||
|
|
2014cdf2d0 | ||
|
|
459e00b365 | ||
|
|
49412d93e2 | ||
|
|
5a8e732594 | ||
|
|
f2e68d2bec | ||
|
|
b07369836e | ||
|
|
f3dc93bcf1 | ||
|
|
30a6d4e3ed | ||
|
|
6d29d8b3fa | ||
|
|
1530f38baa | ||
|
|
2b621c1f83 | ||
|
|
71acb4c122 | ||
|
|
ebbce5b10d | ||
|
|
fa586740e3 | ||
|
|
4623a5521f | ||
|
|
6df24fcffc | ||
|
|
8a1c34903c | ||
|
|
48fc484950 | ||
|
|
1265ed7abb | ||
|
|
95c5e2dc6e | ||
|
|
8d7edd5908 | ||
|
|
b6f6b73a9c |
No files matched your search
+1
-2
@@ -41,7 +41,6 @@ package/
|
||||
.phutil_module_cache
|
||||
unity.a
|
||||
tags
|
||||
etags
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
@@ -60,7 +59,7 @@ rocksdb.h
|
||||
unity.cc
|
||||
java/crossbuild/.vagrant
|
||||
.vagrant/
|
||||
java/**/*.asc
|
||||
java/**.asc
|
||||
java/javadoc
|
||||
|
||||
scan_build_report/
|
||||
|
||||
+2
-11
@@ -16,7 +16,7 @@ cache:
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||
packages: ['clang-3.6' , 'g++-6', 'zlib1g-dev', 'libbz2-dev', 'libsnappy-dev', 'curl', 'libgflags-dev', 'mingw-w64']
|
||||
packages: ['clang-3.6' , 'g++-6', 'zlib1g-dev', 'libbz2-dev', 'libsnappy-dev', 'curl', 'libgflags-dev']
|
||||
env:
|
||||
- TEST_GROUP=platform_dependent1
|
||||
- TEST_GROUP=platform_dependent2
|
||||
@@ -29,8 +29,6 @@ env:
|
||||
- JOB_NAME=lite_build
|
||||
# Build examples
|
||||
- JOB_NAME=examples
|
||||
- JOB_NAME=cmake
|
||||
- JOB_NAME=cmake-mingw
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
@@ -40,11 +38,6 @@ matrix:
|
||||
env: TEST_GROUP=1
|
||||
- os: osx
|
||||
env: TEST_GROUP=2
|
||||
- os : osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
env: JOB_NAME=cmake-mingw
|
||||
compiler: clang
|
||||
|
||||
before_script:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'linux' && "${CXX}" == 'clang++' ]]; then CXX=clang++-3.6; fi
|
||||
@@ -60,11 +53,9 @@ script:
|
||||
- if [[ "${TEST_GROUP}" == 'platform_dependent3' ]]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=checkpoint_test ROCKSDBTESTS_END=db_block_cache_test make -j4 check_some; fi
|
||||
- if [[ "${TEST_GROUP}" == '1' ]]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=comparator_db_test make -j4 check_some; fi
|
||||
- if [[ "${TEST_GROUP}" == '2' ]]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=comparator_db_test make -j4 check_some; fi
|
||||
- if [[ "${JOB_NAME}" == 'java_test' ]]; then OPT=-DTRAVIS V=1 make clean jclean && make rocksdbjava jtest; fi
|
||||
- if [[ "${JOB_NAME}" == 'java_test' ]]; then OPT=-DTRAVIS V=1 make clean jclean rocksdbjava jtest; fi
|
||||
- if [[ "${JOB_NAME}" == 'lite_build' ]]; then OPT="-DTRAVIS -DROCKSDB_LITE" V=1 make -j4 static_lib; fi
|
||||
- if [[ "${JOB_NAME}" == 'examples' ]]; then OPT=-DTRAVIS V=1 make -j4 static_lib; cd examples; make -j4; fi
|
||||
- if [[ "${JOB_NAME}" == 'cmake' ]]; then mkdir build && cd build && cmake .. && make -j4 rocksdb; fi
|
||||
- if [[ "${JOB_NAME}" == 'cmake-mingw' ]]; then mkdir build && cd build && cmake .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb; fi
|
||||
notifications:
|
||||
email:
|
||||
- leveldb@fb.com
|
||||
|
||||
+13
-27
@@ -55,7 +55,7 @@ else()
|
||||
add_definitions(-DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
@@ -66,8 +66,8 @@ else()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
execute_process(COMMAND powershell -noprofile -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -noprofile -Command "Get-Date -format HH:mm:ss" OUTPUT_VARIABLE TIME)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format HH:mm:ss" OUTPUT_VARIABLE TIME)
|
||||
string(REGEX REPLACE "(..)_(..)_..(..).*" "\\1/\\2/\\3" DATE "${DATE}")
|
||||
string(REGEX REPLACE "(..):(.....).*" " \\1:\\2" TIME "${TIME}")
|
||||
set(GIT_DATE_TIME "${DATE} ${TIME}")
|
||||
@@ -115,20 +115,6 @@ if(WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(MSVC)
|
||||
if(WITH_MD_LIBRARY)
|
||||
set(RUNTIME_LIBRARY "MD")
|
||||
else()
|
||||
set(RUNTIME_LIBRARY "MT")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
option(WITH_SSE42 "build with SSE4.2" ON)
|
||||
if(WITH_SSE42)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
@@ -147,8 +133,7 @@ else()
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-momit-leaf-frame-pointer" HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
if(HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
@@ -210,11 +195,11 @@ endif()
|
||||
if(MSVC)
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /${RUNTIME_LIBRARY}d")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /MDd")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm /${RUNTIME_LIBRARY}d")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm /MDd")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /${RUNTIME_LIBRARY}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /MD")
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
@@ -453,13 +438,12 @@ set(SOURCES
|
||||
utilities/blob_db/blob_log_reader.cc
|
||||
utilities/blob_db/blob_log_writer.cc
|
||||
utilities/blob_db/blob_log_format.cc
|
||||
utilities/checkpoint/checkpoint_impl.cc
|
||||
utilities/checkpoint/checkpoint.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/date_tiered/date_tiered_db_impl.cc
|
||||
utilities/debug.cc
|
||||
utilities/document/document_db.cc
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
@@ -514,14 +498,16 @@ else()
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_IMPORT_LIB ${ROCKSDB_SHARED_LIB})
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
set(ROCKSDB_STATIC_LIB rocksdblib${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_IMPORT_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(LIBS ${ROCKSDB_STATIC_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT} rt)
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared)
|
||||
set(ROCKSDB_IMPORT_LIB ${ROCKSDB_SHARED_LIB})
|
||||
set(LIBS ${ROCKSDB_SHARED_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
|
||||
+48
-22
@@ -1,39 +1,64 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.5.4 (07/18/2017)
|
||||
* Change License to Apache + GPLv2
|
||||
## 5.4.12 (08/21/2017)
|
||||
* Statistics::Reset() to reset user stats.
|
||||
|
||||
## 5.5.3 (07/17/2017)
|
||||
## 5.4.11 (08/17/2017)
|
||||
### Bug Fixes
|
||||
* Fix LITE build error.
|
||||
* Fix a bug in PosixWritableFile::Close()
|
||||
|
||||
## 5.5.2 (07/13/2017)
|
||||
No change from 5.5.1.
|
||||
## 5.4.10 (08/12/2017)
|
||||
### Bug Fixes
|
||||
* Fix incorrect dropping of deletions during intra-L0 compaction.
|
||||
|
||||
## 5.5.1 (06/27/2017)
|
||||
## 5.4.9 (07/20/2017)
|
||||
* Change license to Apache + GPLv2
|
||||
|
||||
## 5.4.8 (07/13/2017)
|
||||
No change from 5.4.7.
|
||||
|
||||
## 5.4.7 (06/28/2017)
|
||||
### Bug Fixes
|
||||
* Shouldn't ignore return value of fsync() in flush.
|
||||
|
||||
## 5.5.0 (05/17/2017)
|
||||
## 5.4.6 (05/30/2017)
|
||||
### Bug Fixes
|
||||
* Fix db_bench with lite build.
|
||||
|
||||
## 5.4.5 (05/19/2017)
|
||||
### Bug Fixes
|
||||
* Facility for cross-building RocksJava using Docker.
|
||||
* Build and link with ZStd when creating the static RocksJava build.
|
||||
* Fix snprintf's usage to be cross-platform.
|
||||
* Fix build error with blob DB.
|
||||
* Fix readamp test type inconsistency.
|
||||
|
||||
## 5.4.4 (05/11/2017)
|
||||
### New Features
|
||||
* FIFO compaction to support Intra L0 compaction too with CompactionOptionsFIFO.allow_compaction=true.
|
||||
* DB::ResetStats() to reset internal stats.
|
||||
* Statistics::Reset() to reset user stats.
|
||||
* ldb add option --try_load_options, which will open DB with its own option file.
|
||||
* Introduce WriteBatch::PopSavePoint to pop the most recent save point explicitly.
|
||||
* Support dynamically change `max_open_files` option via SetDBOptions()
|
||||
* Added DB::CreateColumnFamilie() and DB::DropColumnFamilies() to bulk create/drop column families.
|
||||
* Add debugging function `GetAllKeyVersions` to see internal versions of a range of keys.
|
||||
* Support file ingestion with universal compaction style
|
||||
* Support file ingestion behind with option `allow_ingest_behind`
|
||||
* New option enable_pipelined_write which may improve write throughput in case writing from multiple threads and WAL enabled.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix the bug that Direct I/O uses direct reads for non-SST file
|
||||
* Fix a manual / auto compaction data race.
|
||||
* Fix CentOS 5 cross-building of RocksJava.
|
||||
|
||||
## 5.4.3 (05/10/2017)
|
||||
### Bug Fixes
|
||||
* Fix memory alignment with logical sector size.
|
||||
* Fix alignment in ReadaheadRandomAccessFile.
|
||||
* Fix bias with read amplification stats (READ_AMP_ESTIMATE_USEFUL_BYTES and READ_AMP_TOTAL_READ_BYTES).
|
||||
|
||||
## 5.4.2 (05/08/2017)
|
||||
### Bug Fixes
|
||||
* Fix huge fallocate calls fail and make XFS unhappy.
|
||||
|
||||
## 5.4.1 (05/02/2017)
|
||||
### Bug Fxies
|
||||
* Fix WriteBatchWithIndex address use after scope error.
|
||||
* Fix WritableFile buffer size in direct IO.
|
||||
* Add prefetch to PosixRandomAccessFile in buffered io.
|
||||
* Fix PinnableSlice access invalid address when row cache is enabled.
|
||||
|
||||
## 5.4.0 (04/11/2017)
|
||||
### Public API Change
|
||||
* random_access_max_buffer_size no longer has any effect
|
||||
* Removed Env::EnableReadAhead(), Env::ShouldForwardRawRequest()
|
||||
* Support dynamically change `stats_dump_period_sec` option via SetDBOptions().
|
||||
* Added ReadOptions::max_skippable_internal_keys to set a threshold to fail a request as incomplete when too many keys are being skipped when using iterators.
|
||||
* DB::Get in place of std::string accepts PinnableSlice, which avoids the extra memcpy of value to std::string in most of cases.
|
||||
@@ -46,6 +71,7 @@ No change from 5.5.1.
|
||||
* Memtable flush can be avoided during checkpoint creation if total log file size is smaller than a threshold specified by the user.
|
||||
* Introduce level-based L0->L0 compactions to reduce file count, so write delays are incurred less often.
|
||||
* (Experimental) Partitioning filters which creates an index on the partitions. The feature can be enabled by setting partition_filters when using kFullFilter. Currently the feature also requires two-level indexing to be enabled. Number of partitions is the same as the number of partitions for indexes, which is controlled by metadata_block_size.
|
||||
* DB::ResetStats() to reset internal stats.
|
||||
|
||||
## 5.3.0 (03/08/2017)
|
||||
### Public API Change
|
||||
@@ -272,7 +298,7 @@ No change from 5.5.1.
|
||||
* Added a new way to report QPS from db_bench (check out --report_file and --report_interval_seconds)
|
||||
* Added a cache for individual rows. See DBOptions::row_cache for more info.
|
||||
* Several new features on EventListener (see include/rocksdb/listener.h):
|
||||
- OnCompationCompleted() now returns per-compaction job statistics, defined in include/rocksdb/compaction_job_stats.h.
|
||||
- OnCompationCompleted() now returns per-compaciton job statistics, defined in include/rocksdb/compaction_job_stats.h.
|
||||
- Added OnTableFileCreated() and OnTableFileDeleted().
|
||||
* Add compaction_options_universal.enable_trivial_move to true, to allow trivial move while performing universal compaction. Trivial move will happen only when all the input files are non overlapping.
|
||||
|
||||
|
||||
+11
-37
@@ -63,25 +63,26 @@ makes use of SSE4, add 'USE_SSE=1' before your make commands, like this: `USE_SS
|
||||
git checkout v2.0
|
||||
./configure && make && sudo make install
|
||||
|
||||
**Notice**: Once installed, please add the include path for gflags to your `CPATH` environment variable and the
|
||||
lib path to `LIBRARY_PATH`. If installed with default settings, the include path will be `/usr/local/include`
|
||||
and the lib path will be `/usr/local/lib`.
|
||||
**Notice**: Once installed, please add the include path for gflags to your CPATH env var and the
|
||||
lib path to LIBRARY_PATH. If installed with default settings, the lib will be /usr/local/lib
|
||||
and the include path will be /usr/local/include.
|
||||
|
||||
* Install snappy:
|
||||
|
||||
sudo yum install snappy snappy-devel
|
||||
wget https://github.com/google/snappy/releases/download/1.1.4/snappy-1.1.4.tar.gz
|
||||
tar -xzvf snappy-1.1.4.tar.gz
|
||||
cd snappy-1.1.4
|
||||
./configure && make && sudo make install
|
||||
|
||||
* Install zlib:
|
||||
|
||||
sudo yum install zlib zlib-devel
|
||||
sudo yum install zlib
|
||||
sudo yum install zlib-devel
|
||||
|
||||
* Install bzip2:
|
||||
|
||||
sudo yum install bzip2 bzip2-devel
|
||||
|
||||
* Install ASAN (optional for debugging):
|
||||
|
||||
sudo yum install libasan
|
||||
sudo yum install bzip2
|
||||
sudo yum install bzip2-devel
|
||||
|
||||
* Install zstandard:
|
||||
|
||||
@@ -105,30 +106,3 @@ makes use of SSE4, add 'USE_SSE=1' before your make commands, like this: `USE_SS
|
||||
* **Windows**:
|
||||
* For building with MS Visual Studio 13 you will need Update 4 installed.
|
||||
* Read and follow the instructions at CMakeLists.txt
|
||||
|
||||
* **AIX 6.1**
|
||||
* Install AIX Toolbox rpms with gcc
|
||||
* Use these environment variables:
|
||||
|
||||
export PORTABLE=1
|
||||
export CC=gcc
|
||||
export AR="ar -X64"
|
||||
export EXTRA_ARFLAGS=-X64
|
||||
export EXTRA_CFLAGS=-maix64
|
||||
export EXTRA_CXXFLAGS=-maix64
|
||||
export PLATFORM_LDFLAGS="-static-libstdc++ -static-libgcc"
|
||||
export LIBPATH=/opt/freeware/lib
|
||||
export JAVA_HOME=/usr/java8_64
|
||||
export PATH=/opt/freeware/bin:$PATH
|
||||
|
||||
* **Solaris Sparc**
|
||||
* Install GCC 4.8.2 and higher.
|
||||
* Use these environment variables:
|
||||
|
||||
export CC=gcc
|
||||
export EXTRA_CFLAGS=-m64
|
||||
export EXTRA_CXXFLAGS=-m64
|
||||
export EXTRA_LDFLAGS=-m64
|
||||
export PORTABLE=1
|
||||
export PLATFORM_LDFLAGS="-static-libstdc++ -static-libgcc"
|
||||
|
||||
@@ -14,8 +14,7 @@ CFLAGS += ${EXTRA_CFLAGS}
|
||||
CXXFLAGS += ${EXTRA_CXXFLAGS}
|
||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||
MACHINE ?= $(shell uname -m)
|
||||
ARFLAGS = ${EXTRA_ARFLAGS} rs
|
||||
STRIPFLAGS = -S -x
|
||||
ARFLAGS = rs
|
||||
|
||||
# Transform parallel LOG output into something more readable.
|
||||
perl_command = perl -n \
|
||||
@@ -155,9 +154,7 @@ missing_make_config_paths := $(shell \
|
||||
$(foreach path, $(missing_make_config_paths), \
|
||||
$(warning Warning: $(path) dont exist))
|
||||
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
# no debug info
|
||||
else ifneq ($(PLATFORM), IOS)
|
||||
ifneq ($(PLATFORM), IOS)
|
||||
CFLAGS += -g
|
||||
CXXFLAGS += -g
|
||||
else
|
||||
@@ -165,11 +162,6 @@ else
|
||||
OPT += -DNDEBUG
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
ARFLAGS = -X64 rs
|
||||
STRIPFLAGS = -X64 -x
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
PLATFORM_CXXFLAGS += -D _GLIBCXX_USE_C99
|
||||
endif
|
||||
@@ -192,7 +184,7 @@ endif
|
||||
# TSAN doesn't work well with jemalloc. If we're compiling with TSAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_TSAN
|
||||
DISABLE_JEMALLOC=1
|
||||
EXEC_LDFLAGS += -fsanitize=thread
|
||||
EXEC_LDFLAGS += -fsanitize=thread -pie
|
||||
PLATFORM_CCFLAGS += -fsanitize=thread -fPIC -DROCKSDB_TSAN_RUN
|
||||
PLATFORM_CXXFLAGS += -fsanitize=thread -fPIC -DROCKSDB_TSAN_RUN
|
||||
# Turn off -pg when enabling TSAN testing, because that induces
|
||||
@@ -202,11 +194,6 @@ ifdef COMPILE_WITH_TSAN
|
||||
LUA_PATH =
|
||||
endif
|
||||
|
||||
# AIX doesn't work with -pg
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PROFILING_FLAGS =
|
||||
endif
|
||||
|
||||
# USAN doesn't work well with jemalloc. If we're compiling with USAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
DISABLE_JEMALLOC=1
|
||||
@@ -228,14 +215,8 @@ endif
|
||||
export GTEST_THROW_ON_FAILURE=1
|
||||
export GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.7.0/fused-src
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(GTEST_DIR)
|
||||
PLATFORM_CXXFLAGS += -I$(GTEST_DIR)
|
||||
else
|
||||
PLATFORM_CCFLAGS += -isystem $(GTEST_DIR)
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
endif
|
||||
PLATFORM_CCFLAGS += -isystem $(GTEST_DIR)
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
|
||||
# This (the first rule) must depend on "all".
|
||||
default: all
|
||||
@@ -243,11 +224,6 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
ifndef DISABLE_WARNING_AS_ERROR
|
||||
WARNING_FLAGS += -Werror
|
||||
endif
|
||||
|
||||
|
||||
ifdef LUA_PATH
|
||||
|
||||
ifndef LUA_INCLUDE
|
||||
@@ -280,9 +256,6 @@ CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverl
|
||||
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
# If NO_UPDATE_BUILD_VERSION is set we don't update util/build_version.cc, but
|
||||
# the file needs to already exist or else the build will fail
|
||||
ifndef NO_UPDATE_BUILD_VERSION
|
||||
date := $(shell date +%F)
|
||||
ifdef FORCE_GIT_SHA
|
||||
git_sha := $(FORCE_GIT_SHA)
|
||||
@@ -296,6 +269,7 @@ gen_build_version = sed -e s/@@GIT_SHA@@/$(git_sha)/ -e s/@@GIT_DATE_TIME@@/$(da
|
||||
# as a regular source file as part of the compilation process.
|
||||
# One can run "strings executable_filename | grep _build_" to find
|
||||
# the version of the source that we used to build the executable file.
|
||||
CLEAN_FILES += util/build_version.cc:
|
||||
FORCE:
|
||||
util/build_version.cc: FORCE
|
||||
$(AM_V_GEN)rm -f $@-t
|
||||
@@ -303,7 +277,6 @@ util/build_version.cc: FORCE
|
||||
$(AM_V_at)if test -f $@; then \
|
||||
cmp -s $@-t $@ && rm -f $@-t || mv -f $@-t $@; \
|
||||
else mv -f $@-t $@; fi
|
||||
endif
|
||||
|
||||
LIBOBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
LIBOBJECTS += $(TOOL_LIB_SOURCES:.cc=.o)
|
||||
@@ -546,14 +519,9 @@ $(SHARED3): $(SHARED4)
|
||||
ln -fs $(SHARED4) $(SHARED3)
|
||||
endif
|
||||
|
||||
shared_libobjects = $(patsubst %,shared-objects/%,$(LIBOBJECTS))
|
||||
CLEAN_FILES += shared-objects
|
||||
|
||||
$(shared_libobjects): shared-objects/%.o: %.cc
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
|
||||
|
||||
$(SHARED4): $(shared_libobjects)
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(shared_libobjects) $(LDFLAGS) -o $@
|
||||
$(SHARED4):
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(LIB_SOURCES) $(TOOL_LIB_SOURCES) \
|
||||
$(LDFLAGS) -o $@
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
@@ -594,8 +562,7 @@ ifneq (,$(filter check parallel_check,$(MAKECMDGOALS)),)
|
||||
# and create a randomly-named rocksdb.XXXX directory therein.
|
||||
# We'll use that directory in the "make check" rules.
|
||||
ifeq ($(TMPD),)
|
||||
TMPDIR := $(shell echo $${TMPDIR:-/tmp})
|
||||
TMPD := $(shell f=/dev/shm; test -k $$f || f=$(TMPDIR); \
|
||||
TMPD := $(shell f=/dev/shm; test -k $$f || f=/tmp; \
|
||||
perl -le 'use File::Temp "tempdir";' \
|
||||
-e 'print tempdir("'$$f'/rocksdb.XXXX", CLEANUP => 0)')
|
||||
endif
|
||||
@@ -750,12 +717,10 @@ check: all
|
||||
echo "===== Running $$t"; ./$$t || exit 1; done; \
|
||||
fi
|
||||
rm -rf $(TMPD)
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
python tools/ldb_test.py
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
endif
|
||||
|
||||
# TODO add ldb_tests
|
||||
check_some: $(SUBSET)
|
||||
@@ -907,7 +872,6 @@ clean:
|
||||
tags:
|
||||
ctags * -R
|
||||
cscope -b `find . -name '*.cc'` `find . -name '*.h'` `find . -name '*.c'`
|
||||
ctags -e -R -o etags *
|
||||
|
||||
format:
|
||||
build_tools/format-diff.sh
|
||||
@@ -1432,18 +1396,14 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PA
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-sources.jar
|
||||
SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.4
|
||||
SNAPPY_SHA256 ?= 134bfe122fd25599bb807bb8130e7ba6d9bdb851e0b16efcb83ac4f5d0b70057
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/releases/download
|
||||
LZ4_VER ?= 1.7.5
|
||||
LZ4_SHA256 ?= 0190cacd63022ccb86f44fa5041dc6c3804407ad61550ca21c382827319e7e7e
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZLIB_VER = 1.2.11
|
||||
ZLIB_SHA256 = c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
BZIP2_VER = 1.0.6
|
||||
BZIP2_SHA256 = a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
SNAPPY_VER = 1.1.4
|
||||
SNAPPY_SHA256 = 134bfe122fd25599bb807bb8130e7ba6d9bdb851e0b16efcb83ac4f5d0b70057
|
||||
LZ4_VER = 1.7.5
|
||||
LZ4_SHA256 = 0190cacd63022ccb86f44fa5041dc6c3804407ad61550ca21c382827319e7e7e
|
||||
ZSTD_VER ?= 1.2.0
|
||||
ZSTD_SHA256 ?= 4a7e4593a3638276ca7f2a09dc4f38e674d8317bbea51626393ca73fc047cbfb
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
@@ -1469,53 +1429,47 @@ ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/solaris
|
||||
SHA256_CMD = digest -a sha256
|
||||
endif
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/aix
|
||||
ROCKSDBJNILIB = librocksdbjni-aix.so
|
||||
EXTRACT_SOURCES = gunzip < TAR_GZ | tar xvf -
|
||||
SNAPPY_MAKE_TARGET = libsnappy.la
|
||||
endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
curl -O -L ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz
|
||||
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZLIB_SHA256)" != "$$ZLIB_SHA256_ACTUAL" ]; then \
|
||||
echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf zlib-$(ZLIB_VER).tar.gz
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${EXTRA_CFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --static && make
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC' ./configure --static && make
|
||||
cp zlib-$(ZLIB_VER)/libz.a .
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
curl -O -L ${BZIP2_DOWNLOAD_BASE}/$(BZIP2_VER)/bzip2-$(BZIP2_VER).tar.gz
|
||||
curl -O -L http://www.bzip.org/$(BZIP2_VER)/bzip2-$(BZIP2_VER).tar.gz
|
||||
BZIP2_SHA256_ACTUAL=`$(SHA256_CMD) bzip2-$(BZIP2_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(BZIP2_SHA256)" != "$$BZIP2_SHA256_ACTUAL" ]; then \
|
||||
echo bzip2-$(BZIP2_VER).tar.gz checksum mismatch, expected=\"$(BZIP2_SHA256)\" actual=\"$$BZIP2_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf bzip2-$(BZIP2_VER).tar.gz
|
||||
cd bzip2-$(BZIP2_VER) && make CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 ${EXTRA_CFLAGS}' AR='ar ${EXTRA_ARFLAGS}'
|
||||
cd bzip2-$(BZIP2_VER) && make CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64'
|
||||
cp bzip2-$(BZIP2_VER)/libbz2.a .
|
||||
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
curl -O -L ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER)/snappy-$(SNAPPY_VER).tar.gz
|
||||
curl -O -L https://github.com/google/snappy/releases/download/$(SNAPPY_VER)/snappy-$(SNAPPY_VER).tar.gz
|
||||
SNAPPY_SHA256_ACTUAL=`$(SHA256_CMD) snappy-$(SNAPPY_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(SNAPPY_SHA256)" != "$$SNAPPY_SHA256_ACTUAL" ]; then \
|
||||
echo snappy-$(SNAPPY_VER).tar.gz checksum mismatch, expected=\"$(SNAPPY_SHA256)\" actual=\"$$SNAPPY_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
cd snappy-$(SNAPPY_VER) && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --with-pic --enable-static --disable-shared
|
||||
cd snappy-$(SNAPPY_VER) && make ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER) && ./configure --with-pic --enable-static
|
||||
cd snappy-$(SNAPPY_VER) && make
|
||||
cp snappy-$(SNAPPY_VER)/.libs/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
curl -O -L ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
curl -O -L https://github.com/lz4/lz4/archive/v$(LZ4_VER).tar.gz
|
||||
mv v$(LZ4_VER).tar.gz lz4-$(LZ4_VER).tar.gz
|
||||
LZ4_SHA256_ACTUAL=`$(SHA256_CMD) lz4-$(LZ4_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(LZ4_SHA256)" != "$$LZ4_SHA256_ACTUAL" ]; then \
|
||||
@@ -1523,7 +1477,7 @@ liblz4.a:
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf lz4-$(LZ4_VER).tar.gz
|
||||
cd lz4-$(LZ4_VER)/lib && make CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' all
|
||||
cd lz4-$(LZ4_VER)/lib && make CFLAGS='-fPIC' all
|
||||
cp lz4-$(LZ4_VER)/lib/liblz4.a .
|
||||
|
||||
libzstd.a:
|
||||
@@ -1560,7 +1514,7 @@ rocksdbjavastatic: $(java_static_libobjects)
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) \
|
||||
$(java_static_libobjects) $(COVERAGEFLAGS) \
|
||||
$(JAVA_COMPRESSIONS) $(JAVA_STATIC_LDFLAGS)
|
||||
cd java/target;strip $(STRIPFLAGS) $(ROCKSDBJNILIB)
|
||||
cd java/target;strip -S -x $(ROCKSDBJNILIB)
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
@@ -1576,12 +1530,12 @@ rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastatic
|
||||
DOCKER_LINUX_X64_CONTAINER=`docker ps -aqf name=rocksdb_linux_x64-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X64_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x64-be evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x64-be evolvedbinary/rocksjava:centos5_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x64-be
|
||||
DOCKER_LINUX_X86_CONTAINER=`docker ps -aqf name=rocksdb_linux_x86-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X86_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x86-be evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x86-be evolvedbinary/rocksjava:centos5_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x86-be
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
|
||||
@@ -9,8 +9,6 @@ rocksdb_compiler_flags = [
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
@@ -203,11 +201,10 @@ cpp_library(
|
||||
"utilities/blob_db/blob_log_reader.cc",
|
||||
"utilities/blob_db/blob_log_writer.cc",
|
||||
"utilities/blob_db/blob_log_format.cc",
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/checkpoint/checkpoint.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/date_tiered/date_tiered_db_impl.cc",
|
||||
"utilities/debug.cc",
|
||||
"utilities/document/document_db.cc",
|
||||
"utilities/document/json_document.cc",
|
||||
"utilities/document/json_document_builder.cc",
|
||||
@@ -247,7 +244,6 @@ cpp_library(
|
||||
"tools/ldb_cmd.cc",
|
||||
"tools/ldb_tool.cc",
|
||||
"tools/sst_dump_tool.cc",
|
||||
"utilities/blob_db/blob_dump_tool.cc",
|
||||
],
|
||||
deps = [],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
@@ -303,7 +299,7 @@ ROCKS_TESTS = [['merger_test', 'table/merger_test.cc', 'serial'],
|
||||
['cache_test', 'cache/cache_test.cc', 'serial'],
|
||||
['options_file_test', 'db/options_file_test.cc', 'serial'],
|
||||
['compaction_picker_test', 'db/compaction_picker_test.cc', 'serial'],
|
||||
['db_log_iter_test', 'db/db_log_iter_test.cc', 'serial'],
|
||||
['corruption_test', 'db/corruption_test.cc', 'serial'],
|
||||
['thread_list_test', 'util/thread_list_test.cc', 'serial'],
|
||||
['table_properties_collector_test',
|
||||
'db/table_properties_collector_test.cc',
|
||||
@@ -406,7 +402,6 @@ ROCKS_TESTS = [['merger_test', 'table/merger_test.cc', 'serial'],
|
||||
['ttl_test', 'utilities/ttl/ttl_test.cc', 'serial'],
|
||||
['merge_helper_test', 'db/merge_helper_test.cc', 'serial'],
|
||||
['file_indexer_test', 'db/file_indexer_test.cc', 'serial'],
|
||||
['db_statistics_test', 'db/db_statistics_test.cc', 'serial'],
|
||||
['memory_test', 'utilities/memory/memory_test.cc', 'serial'],
|
||||
['log_test', 'db/log_test.cc', 'serial'],
|
||||
['env_timed_test', 'utilities/env_timed_test.cc', 'serial'],
|
||||
@@ -430,13 +425,12 @@ ROCKS_TESTS = [['merger_test', 'table/merger_test.cc', 'serial'],
|
||||
['delete_scheduler_test', 'util/delete_scheduler_test.cc', 'serial'],
|
||||
['transaction_test', 'utilities/transactions/transaction_test.cc', 'serial'],
|
||||
['db_io_failure_test', 'db/db_io_failure_test.cc', 'serial'],
|
||||
['corruption_test', 'db/corruption_test.cc', 'serial'],
|
||||
['db_log_iter_test', 'db/db_log_iter_test.cc', 'serial'],
|
||||
['compact_on_deletion_collector_test',
|
||||
'utilities/table_properties_collectors/compact_on_deletion_collector_test.cc',
|
||||
'serial'],
|
||||
['env_test', 'env/env_test.cc', 'serial'],
|
||||
['db_wal_test', 'db/db_wal_test.cc', 'parallel'],
|
||||
['timer_queue_test', 'util/timer_queue_test.cc', 'serial'],
|
||||
['sim_cache_test', 'utilities/simulator_cache/sim_cache_test.cc', 'serial'],
|
||||
['db_memtable_test', 'db/db_memtable_test.cc', 'serial'],
|
||||
['db_universal_compaction_test',
|
||||
@@ -455,7 +449,8 @@ ROCKS_TESTS = [['merger_test', 'table/merger_test.cc', 'serial'],
|
||||
['version_edit_test', 'db/version_edit_test.cc', 'serial'],
|
||||
['skiplist_test', 'memtable/skiplist_test.cc', 'serial'],
|
||||
['lru_cache_test', 'cache/lru_cache_test.cc', 'serial'],
|
||||
['plain_table_db_test', 'db/plain_table_db_test.cc', 'serial']]
|
||||
['plain_table_db_test', 'db/plain_table_db_test.cc', 'serial'],
|
||||
['db_statistics_test', 'db/db_statistics_test.cc', 'serial']]
|
||||
|
||||
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
phutil_register_library('arcanist_util', __FILE__);
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is automatically generated. Use 'arc liberate' to rebuild it.
|
||||
* @generated
|
||||
* @phutil-library-version 2
|
||||
*/
|
||||
|
||||
if (class_exists('ArcanistWorkflow')) {
|
||||
phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
'BaseDirectoryScopedFormatLinter' => 'cpp_linter/BaseDirectoryScopedFormatLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookFbcodeUnitTestEngine.php',
|
||||
'FacebookHowtoevenLintEngine' => 'lint_engine/FacebookHowtoevenLintEngine.php',
|
||||
'FacebookHowtoevenLinter' => 'cpp_linter/FacebookHowtoevenLinter.php',
|
||||
'FbcodeClangFormatLinter' => 'cpp_linter/FbcodeClangFormatLinter.php',
|
||||
'FbcodeCppLinter' => 'cpp_linter/FbcodeCppLinter.php',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
),
|
||||
'xmap' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'ArcanistLinter',
|
||||
'BaseDirectoryScopedFormatLinter' => 'ArcanistLinter',
|
||||
'FacebookArcanistConfiguration' => 'ArcanistConfiguration',
|
||||
'FacebookFbcodeLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookFbcodeUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
|
||||
'FacebookHowtoevenLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookHowtoevenLinter' => 'ArcanistLinter',
|
||||
'FbcodeClangFormatLinter' => 'BaseDirectoryScopedFormatLinter',
|
||||
'FbcodeCppLinter' => 'ArcanistLinter',
|
||||
),
|
||||
));
|
||||
} else {
|
||||
phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
'BaseDirectoryScopedFormatLinter' => 'cpp_linter/BaseDirectoryScopedFormatLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookOldArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookOldFbcodeUnitTestEngine.php',
|
||||
'FacebookHowtoevenLintEngine' => 'lint_engine/FacebookHowtoevenLintEngine.php',
|
||||
'FacebookHowtoevenLinter' => 'cpp_linter/FacebookHowtoevenLinter.php',
|
||||
'FbcodeClangFormatLinter' => 'cpp_linter/FbcodeClangFormatLinter.php',
|
||||
'FbcodeCppLinter' => 'cpp_linter/FbcodeCppLinter.php',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
),
|
||||
'xmap' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'ArcanistLinter',
|
||||
'BaseDirectoryScopedFormatLinter' => 'ArcanistLinter',
|
||||
'FacebookArcanistConfiguration' => 'ArcanistConfiguration',
|
||||
'FacebookFbcodeLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookFbcodeUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
|
||||
'FacebookHowtoevenLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookHowtoevenLinter' => 'ArcanistLinter',
|
||||
'FbcodeClangFormatLinter' => 'BaseDirectoryScopedFormatLinter',
|
||||
'FbcodeCppLinter' => 'ArcanistLinter',
|
||||
),
|
||||
));
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
require('RocksDBCommonHelper.php');
|
||||
|
||||
define("DIFF_COMMAND", "diff");
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
public function getCustomArgumentsForCommand($command) {
|
||||
if ($command == "land") {
|
||||
return array(
|
||||
'async' => array('help' => 'Just to make tools happy'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistWorkflow $workflow,
|
||||
$error_code) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
assert($workflow);
|
||||
assert(strlen($command) > 0);
|
||||
|
||||
if ($command == DIFF_COMMAND && !$workflow->isRawDiffSource()) {
|
||||
$diffID = $workflow->getDiffId();
|
||||
|
||||
// When submitting a diff this code path gets executed multiple times in
|
||||
// a row. We only care about the case when ID for the diff is provided
|
||||
// because that's what we need to apply the diff and trigger the tests.
|
||||
if (strlen($diffID) > 0) {
|
||||
assert(is_numeric($diffID));
|
||||
startTestsInSandcastle(true /* $applyDiff */, $workflow, $diffID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
require('RocksDBCommonHelper.php');
|
||||
|
||||
define("DIFF_COMMAND", "diff");
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
public function getCustomArgumentsForCommand($command) {
|
||||
if ($command == "land") {
|
||||
return array(
|
||||
'async' => array('help' => 'Just to make tools happy'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistBaseWorkflow $workflow,
|
||||
$error_code) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
assert($workflow);
|
||||
assert(strlen($command) > 0);
|
||||
|
||||
if ($command == DIFF_COMMAND && !$workflow->isRawDiffSource()) {
|
||||
$diffID = $workflow->getDiffId();
|
||||
|
||||
// When submitting a diff this code path gets executed multiple times in
|
||||
// a row. We only care about the case when ID for the diff is provided
|
||||
// because that's what we need to apply the diff and trigger the tests.
|
||||
if (strlen($diffID) > 0) {
|
||||
assert(is_numeric($diffID));
|
||||
startTestsInSandcastle(true /* $applyDiff */, $workflow, $diffID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
// Name of the environment variables which need to be set by the entity which
|
||||
// triggers continuous runs so that code at the end of the file gets executed
|
||||
// and Sandcastle run starts.
|
||||
define("ENV_POST_RECEIVE_HOOK", "POST_RECEIVE_HOOK");
|
||||
define("ENV_HTTPS_APP_VALUE", "HTTPS_APP_VALUE");
|
||||
define("ENV_HTTPS_TOKEN_VALUE", "HTTPS_TOKEN_VALUE");
|
||||
|
||||
define("PRIMARY_TOKEN_FILE", '/home/krad/.sandcastle');
|
||||
define("CONT_RUN_ALIAS", "leveldb");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Run tests in sandcastle */
|
||||
function postURL($diffID, $url) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($url) > 0);
|
||||
|
||||
$cmd = 'echo \'{"diff_id": ' . $diffID . ', '
|
||||
. '"name":"click here for sandcastle tests for D' . $diffID . '", '
|
||||
. '"link":"' . $url . '"}\' | '
|
||||
. 'arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
shell_exec($cmd);
|
||||
}
|
||||
|
||||
function buildUpdateTestStatusCmd($diffID, $test, $status) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
assert(strlen($status) > 0);
|
||||
|
||||
$cmd = 'echo \'{"diff_id": ' . $diffID . ', '
|
||||
. '"name":"' . $test . '", '
|
||||
. '"result":"' . $status . '"}\' | '
|
||||
. 'arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
function updateTestStatus($diffID, $test) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
shell_exec(buildUpdateTestStatusCmd($diffID, $test, "waiting"));
|
||||
}
|
||||
|
||||
function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
assert(strlen($username) > 0);
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
|
||||
$arcrc_content = (PHP_OS == "Darwin" ?
|
||||
exec("cat ~/.arcrc | gzip -f | base64") :
|
||||
exec("cat ~/.arcrc | gzip -f | base64 -w0"));
|
||||
assert(strlen($arcrc_content) > 0);
|
||||
|
||||
// Sandcastle machines don't have arc setup. We copy the user certificate
|
||||
// and authenticate using that in Sandcastle.
|
||||
$setup = array(
|
||||
"name" => "Setup arcrc",
|
||||
"shell" => "echo " . $arcrc_content . " | base64 --decode"
|
||||
. " | gzip -d > ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config.
|
||||
// also fix the sticky bit issue in sandcastle
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc && chmod +t /dev/shm",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Construct the steps in the order of execution.
|
||||
$steps[] = $setup;
|
||||
$steps[] = $fix_permission;
|
||||
}
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// Git thinks it is an uncommited change.
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $fix_git_ignore;
|
||||
|
||||
// This will be the command used to execute particular type of tests.
|
||||
$cmd = "";
|
||||
|
||||
if ($applyDiff) {
|
||||
// Patch the code (keep your fingures crossed).
|
||||
$patch = array(
|
||||
"name" => "Patch " . $diffID,
|
||||
"shell" => "arc --arcrc-file ~/.arcrc "
|
||||
. "patch --nocommit --diff " . $diffID,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $patch;
|
||||
|
||||
updateTestStatus($diffID, $test);
|
||||
$cmd = buildUpdateTestStatusCmd($diffID, $test, "running") . "; ";
|
||||
}
|
||||
|
||||
// Run the actual command.
|
||||
$cmd = $cmd . "J=$(nproc) ./build_tools/precommit_checker.py " . $test
|
||||
. "; exit_code=$?; ";
|
||||
|
||||
if ($applyDiff) {
|
||||
$cmd = $cmd . "([[ \$exit_code -eq 0 ]] &&"
|
||||
. buildUpdateTestStatusCmd($diffID, $test, "pass") . ")"
|
||||
. "||" . buildUpdateTestStatusCmd($diffID, $test, "fail")
|
||||
. "; ";
|
||||
}
|
||||
|
||||
// shell command to sort the tests based on exit code and print
|
||||
// the output of the log files.
|
||||
$cat_sorted_logs = "
|
||||
while read code log_file;
|
||||
do echo \"################ cat \$log_file [exit_code : \$code] ################\";
|
||||
cat \$log_file;
|
||||
done < <(tail -n +2 LOG | sort -k7,7n -k4,4gr | awk '{print \$7,\$NF}')";
|
||||
|
||||
// Shell command to cat all log files
|
||||
$cat_all_logs = "for f in `ls t/!(run-*)`; do echo \$f;cat \$f; done";
|
||||
|
||||
// If LOG file exist use it to cat log files sorted by exit code, otherwise
|
||||
// cat everything
|
||||
$logs_cmd = "if [ -f LOG ]; then {$cat_sorted_logs}; else {$cat_all_logs}; fi";
|
||||
|
||||
$cmd = $cmd . " cat /tmp/precommit-check.log"
|
||||
. "; shopt -s extglob; {$logs_cmd}"
|
||||
. "; shopt -u extglob; [[ \$exit_code -eq 0 ]]";
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$run_test = array(
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
"parser" => "python build_tools/error_filter.py " . $test,
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
|
||||
if ($applyDiff) {
|
||||
// Clean up the user arc config we are using.
|
||||
$cleanup = array(
|
||||
"name" => "Arc cleanup",
|
||||
"shell" => "rm -f ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $cleanup;
|
||||
}
|
||||
|
||||
assert(count($steps) > 0);
|
||||
return $steps;
|
||||
}
|
||||
|
||||
function getSandcastleConfig() {
|
||||
$sandcastle_config = array();
|
||||
|
||||
$cwd = getcwd();
|
||||
$cwd_token_file = "{$cwd}/.sandcastle";
|
||||
// This is a case when we're executed from a continuous run. Fetch the values
|
||||
// from the environment.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
$sandcastle_config[0] = getenv(ENV_HTTPS_APP_VALUE);
|
||||
$sandcastle_config[1] = getenv(ENV_HTTPS_TOKEN_VALUE);
|
||||
} else {
|
||||
// This is a typical `[p]arc diff` case. Fetch the values from the specific
|
||||
// configuration files.
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
if (file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file)) {
|
||||
break;
|
||||
}
|
||||
// If we failed to fetch the tokens, sleep for 0.2 second and try again
|
||||
usleep(200000);
|
||||
}
|
||||
assert(file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file));
|
||||
|
||||
// Try the primary location first, followed by a secondary.
|
||||
if (file_exists(PRIMARY_TOKEN_FILE)) {
|
||||
$cmd = 'cat ' . PRIMARY_TOKEN_FILE;
|
||||
} else {
|
||||
$cmd = 'cat ' . $cwd_token_file;
|
||||
}
|
||||
|
||||
assert(strlen($cmd) > 0);
|
||||
$sandcastle_config = explode(':', rtrim(shell_exec($cmd)));
|
||||
}
|
||||
|
||||
// In this case be very explicit about the implications.
|
||||
if (count($sandcastle_config) != 2) {
|
||||
echo "Sandcastle configuration files don't contain valid information " .
|
||||
"or the necessary environment variables aren't defined. Unable " .
|
||||
"to validate the code changes.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert(strlen($sandcastle_config[0]) > 0);
|
||||
assert(strlen($sandcastle_config[1]) > 0);
|
||||
assert(count($sandcastle_config) > 0);
|
||||
|
||||
return $sandcastle_config;
|
||||
}
|
||||
|
||||
// This function can be called either from `[p]arc diff` command or during
|
||||
// the Git post-receive hook.
|
||||
function startTestsInSandcastle($applyDiff, $workflow, $diffID) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
// In case of a diff we'll send notificatios to the author. Else it'll go to
|
||||
// the entire team because failures indicate that build quality has regressed.
|
||||
$username = $applyDiff ? exec("whoami") : CONT_RUN_ALIAS;
|
||||
assert(strlen($username) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert($workflow);
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
}
|
||||
|
||||
// List of tests we want to run in Sandcastle.
|
||||
$tests = array("unit", "unit_non_shm", "unit_481", "clang_unit", "tsan",
|
||||
"asan", "lite_test", "valgrind", "release", "release_481",
|
||||
"clang_release", "punit", "clang_analyze", "code_cov",
|
||||
"java_build", "no_compression", "unity", "ubsan");
|
||||
|
||||
$send_email_template = array(
|
||||
'type' => 'email',
|
||||
'triggers' => array('fail'),
|
||||
'emails' => array($username . '@fb.com'),
|
||||
);
|
||||
|
||||
// Construct a job definition for each test and add it to the master plan.
|
||||
foreach ($tests as $test) {
|
||||
$stepName = "RocksDB diff " . $diffID . " test " . $test;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$stepName = "RocksDB continuous integration test " . $test;
|
||||
}
|
||||
|
||||
$arg[] = array(
|
||||
"name" => $stepName,
|
||||
"report" => array($send_email_template),
|
||||
"steps" => getSteps($applyDiff, $diffID, $username, $test)
|
||||
);
|
||||
}
|
||||
|
||||
// We cannot submit the parallel execution master plan to Sandcastle and
|
||||
// need supply the job plan as a determinator. So we construct a small job
|
||||
// that will spit out the master job plan which Sandcastle will parse and
|
||||
// execute. Why compress the job definitions? Otherwise we run over the max
|
||||
// string size.
|
||||
$cmd = "echo " . base64_encode(json_encode($arg))
|
||||
. (PHP_OS == "Darwin" ?
|
||||
" | gzip -f | base64" :
|
||||
" | gzip -f | base64 -w0");
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$arg_encoded = shell_exec($cmd);
|
||||
assert(strlen($arg_encoded) > 0);
|
||||
|
||||
$runName = "Run diff " . $diffID . "for user " . $username;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$runName = "RocksDB continuous integration build and test run";
|
||||
}
|
||||
|
||||
$command = array(
|
||||
"name" => $runName,
|
||||
"steps" => array()
|
||||
);
|
||||
|
||||
$command["steps"][] = array(
|
||||
"name" => "Generate determinator",
|
||||
"shell" => "echo " . $arg_encoded . " | base64 --decode | gzip -d"
|
||||
. " | base64 --decode",
|
||||
"determinator" => true,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Submit to Sandcastle.
|
||||
$url = 'https://interngraph.intern.facebook.com/sandcastle/create';
|
||||
|
||||
$job = array(
|
||||
'command' => 'SandcastleUniversalCommand',
|
||||
'args' => $command,
|
||||
'capabilities' => array(
|
||||
'vcs' => 'rocksdb-int-git',
|
||||
'type' => 'lego',
|
||||
),
|
||||
'hash' => 'origin/master',
|
||||
'user' => $username,
|
||||
'alias' => 'rocksdb-precommit',
|
||||
'tags' => array('rocksdb'),
|
||||
'description' => 'Rocksdb precommit job',
|
||||
);
|
||||
|
||||
// Fetch the configuration necessary to submit a successful HTTPS request.
|
||||
$sandcastle_config = getSandcastleConfig();
|
||||
|
||||
$app = $sandcastle_config[0];
|
||||
$token = $sandcastle_config[1];
|
||||
|
||||
$cmd = 'curl -s -k -F app=' . $app . ' '
|
||||
. '-F token=' . $token . ' -F job=\'' . json_encode($job)
|
||||
.'\' "' . $url . '"';
|
||||
|
||||
$output = shell_exec($cmd);
|
||||
assert(strlen($output) > 0);
|
||||
|
||||
// Extract Sandcastle URL from the response.
|
||||
preg_match('/url": "(.+)"/', $output, $sandcastle_url);
|
||||
|
||||
assert(count($sandcastle_url) > 0, "Unable to submit Sandcastle request.");
|
||||
assert(strlen($sandcastle_url[1]) > 0, "Unable to extract Sandcastle URL.");
|
||||
|
||||
if ($applyDiff) {
|
||||
echo "\nSandcastle URL: " . $sandcastle_url[1] . "\n";
|
||||
// Ask Phabricator to display it on the diff UI.
|
||||
postURL($diffID, $sandcastle_url[1]);
|
||||
} else {
|
||||
echo "Continuous integration started Sandcastle tests. You can look at ";
|
||||
echo "the progress at:\n" . $sandcastle_url[1] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Continuous run cript will set the environment variable and based on that
|
||||
// we'll trigger the execution of tests in Sandcastle. In that case we don't
|
||||
// need to apply any diffs and there's no associated workflow either.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
startTestsInSandcastle(
|
||||
false /* $applyDiff */,
|
||||
NULL /* $workflow */,
|
||||
NULL /* $diffID */);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Uses google's cpplint.py to check code. RocksDB team forked this file from
|
||||
* phabricator's /src/lint/linter/ArcanistCpplintLinter.php, and customized it
|
||||
* for its own use.
|
||||
*
|
||||
* You can get it here:
|
||||
* http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py
|
||||
* @group linter
|
||||
*/
|
||||
final class ArcanistCpplintLinter extends ArcanistLinter {
|
||||
|
||||
public function willLintPaths(array $paths) {
|
||||
return;
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'cpplint.py';
|
||||
}
|
||||
|
||||
public function getLintPath() {
|
||||
$bin = 'cpplint.py';
|
||||
// Search under current dir
|
||||
list($err) = exec_manual('which %s/%s', $this->linterDir(), $bin);
|
||||
if (!$err) {
|
||||
return $this->linterDir().'/'.$bin;
|
||||
}
|
||||
|
||||
// Look for globally installed cpplint.py
|
||||
list($err) = exec_manual('which %s', $bin);
|
||||
if ($err) {
|
||||
throw new ArcanistUsageException(
|
||||
"cpplint.py does not appear to be installed on this system. Install ".
|
||||
"it (e.g., with 'wget \"http://google-styleguide.googlecode.com/".
|
||||
"svn/trunk/cpplint/cpplint.py\"') ".
|
||||
"in your .arcconfig to point to the directory where it resides. ".
|
||||
"Also don't forget to chmod a+x cpplint.py!");
|
||||
}
|
||||
|
||||
return $bin;
|
||||
}
|
||||
|
||||
public function lintPath($path) {
|
||||
$bin = $this->getLintPath();
|
||||
$path = $this->rocksdbDir().'/'.$path;
|
||||
|
||||
$f = new ExecFuture("%C $path", $bin);
|
||||
|
||||
list($err, $stdout, $stderr) = $f->resolve();
|
||||
|
||||
if ($err === 2) {
|
||||
throw new Exception("cpplint failed to run correctly:\n".$stderr);
|
||||
}
|
||||
|
||||
$lines = explode("\n", $stderr);
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
$matches = null;
|
||||
$regex = '/^[^:]+:(\d+):\s*(.*)\s*\[(.*)\] \[(\d+)\]$/';
|
||||
if (!preg_match($regex, $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($matches as $key => $match) {
|
||||
$matches[$key] = trim($match);
|
||||
}
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches[1]);
|
||||
$message->setCode($matches[3]);
|
||||
$message->setName($matches[3]);
|
||||
$message->setDescription($matches[2]);
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
$this->addLintMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
// The path of this linter
|
||||
private function linterDir() {
|
||||
return dirname(__FILE__);
|
||||
}
|
||||
|
||||
// TODO(kaili) a quick and dirty way to figure out rocksdb's root dir.
|
||||
private function rocksdbDir() {
|
||||
return $this->linterDir()."/../..";
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
abstract class BaseDirectoryScopedFormatLinter extends ArcanistLinter {
|
||||
|
||||
const LINT_FORMATTING = 1;
|
||||
|
||||
private $changedLines = array();
|
||||
private $rawLintOutput = array();
|
||||
|
||||
abstract protected function getPathsToLint();
|
||||
|
||||
protected function shouldLintPath($path) {
|
||||
foreach ($this->getPathsToLint() as $p) {
|
||||
// check if $path starts with $p
|
||||
if (strncmp($path, $p, strlen($p)) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// API to tell this linter which lines were changed
|
||||
final public function setPathChangedLines($path, $changed) {
|
||||
$this->changedLines[$path] = $changed;
|
||||
}
|
||||
|
||||
final public function willLintPaths(array $paths) {
|
||||
$futures = array();
|
||||
foreach ($paths as $path) {
|
||||
if (!$this->shouldLintPath($path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changed = $this->changedLines[$path];
|
||||
if (!isset($changed)) {
|
||||
// do not run linter if there are no changes
|
||||
continue;
|
||||
}
|
||||
|
||||
$futures[$path] = $this->getFormatFuture($path, $changed);
|
||||
}
|
||||
|
||||
foreach (id(new FutureIterator($futures))->limit(8) as $p => $f) {
|
||||
$this->rawLintOutput[$p] = $f->resolvex();
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function getFormatFuture($path, array $changed);
|
||||
abstract protected function getLintMessage($diff);
|
||||
|
||||
final public function lintPath($path) {
|
||||
if (!isset($this->rawLintOutput[$path])) {
|
||||
return;
|
||||
}
|
||||
|
||||
list($new_content) = $this->rawLintOutput[$path];
|
||||
$old_content = $this->getData($path);
|
||||
|
||||
if ($new_content != $old_content) {
|
||||
$diff = ArcanistDiffUtils::renderDifferences($old_content, $new_content);
|
||||
$this->raiseLintAtOffset(
|
||||
0,
|
||||
self::LINT_FORMATTING,
|
||||
$this->getLintMessage($diff),
|
||||
$old_content,
|
||||
$new_content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2015-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
final class FacebookHowtoevenLinter extends ArcanistLinter {
|
||||
|
||||
const VERSION = 'fd9192f324c36d28136d14380f0b552a1385b59b';
|
||||
|
||||
private $parsedTargets = array();
|
||||
|
||||
public function getLinterName() {
|
||||
return 'Howtoeven';
|
||||
}
|
||||
|
||||
protected function getSeverity($code) {
|
||||
$severities = array(
|
||||
ArcanistLintSeverity::SEVERITY_DISABLED,
|
||||
ArcanistLintSeverity::SEVERITY_ADVICE,
|
||||
ArcanistLintSeverity::SEVERITY_WARNING,
|
||||
ArcanistLintSeverity::SEVERITY_ERROR,
|
||||
);
|
||||
return idx($severities, $code, ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
}
|
||||
|
||||
public function willLintPaths(array $paths) {
|
||||
// Cleanup previous runs.
|
||||
$this->localExecx("rm -rf _build/_lint");
|
||||
|
||||
// Build compilation database.
|
||||
$lintable_paths = $this->getLintablePaths($paths);
|
||||
$interesting_paths = $this->getInterestingPaths($lintable_paths);
|
||||
|
||||
if (!$lintable_paths) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Run lint.
|
||||
try {
|
||||
$this->localExecx(
|
||||
"%C %C -p _build/dev/ %Ls",
|
||||
$this->getBinaryPath(),
|
||||
$this->getFilteredIssues(),
|
||||
$lintable_paths);
|
||||
} catch (CommandException $exception) {
|
||||
PhutilConsole::getConsole()->writeErr($exception->getMessage());
|
||||
}
|
||||
|
||||
// Load results.
|
||||
$result = id(
|
||||
new SQLite3(
|
||||
$this->getProjectRoot().'/_build/_lint/lint.db',
|
||||
SQLITE3_OPEN_READONLY))
|
||||
->query("SELECT * FROM raised_issues");
|
||||
|
||||
while ($issue = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
// Skip issues not part of the linted file.
|
||||
if (in_array($issue['file'], $interesting_paths)) {
|
||||
$this->addLintMessage(id(new ArcanistLintMessage())
|
||||
->setPath($issue['file'])
|
||||
->setLine($issue['line'])
|
||||
->setChar($issue['column'])
|
||||
->setCode('Howtoeven')
|
||||
->setSeverity($this->getSeverity($issue['severity']))
|
||||
->setName('Hte-'.$issue['name'])
|
||||
->setDescription(
|
||||
sprintf(
|
||||
"%s\n\n%s",
|
||||
($issue['message']) ? $issue['message'] : $issue['description'],
|
||||
$issue['explanation']))
|
||||
->setOriginalText(idx($issue, 'original', ''))
|
||||
->setReplacementText(idx($issue, 'replacement', '')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function lintPath($path) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the paths that we know how to lint.
|
||||
*
|
||||
* The strategy is to first look whether there's an existing compilation
|
||||
* database and use that if it's exhaustive. We generate our own only if
|
||||
* necessary.
|
||||
*/
|
||||
private function getLintablePaths($paths) {
|
||||
// Replace headers with existing sources.
|
||||
for ($i = 0; $i < count($paths); $i++) {
|
||||
if (preg_match("/\.h$/", $paths[$i])) {
|
||||
$header = preg_replace("/\.h$/", ".cpp", $paths[$i]);
|
||||
if (file_exists($header)) {
|
||||
$paths[$i] = $header;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if database exists and is exhaustive.
|
||||
$available_paths = $this->getAvailablePaths();
|
||||
$lintable_paths = array_intersect($paths, $available_paths);
|
||||
if ($paths === $lintable_paths) {
|
||||
return $lintable_paths;
|
||||
}
|
||||
|
||||
// Generate our own database.
|
||||
$targets = $this->getTargetsFor($paths);
|
||||
if (!$targets) {
|
||||
PhutilConsole::getConsole()->writeErr(
|
||||
"No build targets found for %s\n",
|
||||
implode(', ', $paths));
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->localExecx("./tools/build/bin/fbconfig.par -r %Ls", $targets);
|
||||
$this->localExecx("./tools/build/bin/fbmake.par gen_cdb");
|
||||
|
||||
$available_paths = $this->getAvailablePaths();
|
||||
$lintable_paths = array_intersect($paths, $available_paths);
|
||||
if ($paths != $lintable_paths) {
|
||||
PhutilConsole::getConsole()->writeErr(
|
||||
"Can't lint %s\n",
|
||||
implode(', ', array_diff($paths, $available_paths)));
|
||||
}
|
||||
|
||||
// Return what we know how to lint.
|
||||
return $lintable_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available paths in the current compilation database.
|
||||
*/
|
||||
private function getAvailablePaths() {
|
||||
$database_path = $this->getProjectRoot()
|
||||
.'/_build/dev/compile_commands.json';
|
||||
if (!file_exists($database_path)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$entries = json_decode(file_get_contents($database_path), true);
|
||||
$paths = array();
|
||||
foreach ($entries as $entry) {
|
||||
$paths[] = $entry['file'];
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the targets directories for the given files.
|
||||
*/
|
||||
private static function getTargetsFor($paths) {
|
||||
$targets = array();
|
||||
foreach ($paths as $path) {
|
||||
while (($path = dirname($path)) !== '.') {
|
||||
if (in_array('TARGETS', scandir($path))) {
|
||||
$contents = file_get_contents($path.'/TARGETS');
|
||||
if (strpos($contents, 'cpp_binary') !== false) {
|
||||
$targets[] = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_unique($targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* The paths that we actually want to report on.
|
||||
*/
|
||||
private function getInterestingPaths($paths) {
|
||||
$headers = array();
|
||||
foreach ($paths as $path) {
|
||||
$headers[] = preg_replace("/\.cpp$/", ".h", $path);
|
||||
}
|
||||
return array_merge($paths, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* The path where the binary is located. Will return the current dewey binary
|
||||
* unless the `HOWTOEVEN_BUILD` environment variable is set.
|
||||
*/
|
||||
private function getBinaryPath() {
|
||||
$path = sprintf(
|
||||
"/mnt/dewey/fbcode/.commits/%s/builds/howtoeven/client",
|
||||
self::VERSION);
|
||||
|
||||
$build = getenv('HOWTOEVEN_BUILD');
|
||||
if ($build) {
|
||||
$path = sprintf(
|
||||
"./_build/%s/tools/howtoeven/client",
|
||||
$build);
|
||||
if (!file_exists($path)) {
|
||||
PhutilConsole::getConsole()->writeErr(">> %s does not exist\n", $path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command in the root directory.
|
||||
*/
|
||||
private function localExecx($command /* , ... */) {
|
||||
$arguments = func_get_args();
|
||||
return newv('ExecFuture', $arguments)
|
||||
->setCWD($this->getProjectRoot())
|
||||
->resolvex();
|
||||
}
|
||||
|
||||
/**
|
||||
* The root of the project.
|
||||
*/
|
||||
private function getProjectRoot() {
|
||||
return $this->getEngine()->getWorkingCopy()->getProjectRoot();
|
||||
}
|
||||
|
||||
private function getFilteredIssues() {
|
||||
$issues = getenv('HOWTOEVEN_ISSUES');
|
||||
return ($issues) ? csprintf('-issues %s', $issues) : '';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
final class FbcodeClangFormatLinter extends BaseDirectoryScopedFormatLinter {
|
||||
|
||||
const LINT_FORMATTING = 1;
|
||||
const CLANG_FORMAT_BINARY = '/mnt/vol/engshare/admin/scripts/clang-format';
|
||||
|
||||
protected function getPathsToLint() {
|
||||
return array('');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'CLANG_FORMAT';
|
||||
}
|
||||
|
||||
public function getLintSeverityMap() {
|
||||
return array(
|
||||
self::LINT_FORMATTING => ArcanistLintSeverity::SEVERITY_ADVICE,
|
||||
);
|
||||
}
|
||||
|
||||
public function getLintNameMap() {
|
||||
return array(
|
||||
self::LINT_FORMATTING => pht('Changes are not clang-formatted'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getFormatFuture($path, array $changed) {
|
||||
$args = "";
|
||||
foreach ($changed as $key => $value) {
|
||||
$args .= " --lines=$key:$key";
|
||||
}
|
||||
|
||||
$binary = self::CLANG_FORMAT_BINARY;
|
||||
if (!file_exists($binary)) {
|
||||
// trust the $PATH
|
||||
$binary = "clang-format";
|
||||
}
|
||||
|
||||
return new ExecFuture(
|
||||
"%s %s $args",
|
||||
$binary,
|
||||
$this->getEngine()->getFilePathOnDisk($path));
|
||||
}
|
||||
|
||||
protected function getLintMessage($diff) {
|
||||
$link_to_clang_format =
|
||||
"[[ http://fburl.com/clang-format | clang-format ]]";
|
||||
return <<<LINT_MSG
|
||||
Changes in this file were not formatted using $link_to_clang_format.
|
||||
Please run build_tools/format-diff.sh or `make format`
|
||||
LINT_MSG;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All rights reserved.
|
||||
|
||||
class FbcodeCppLinter extends ArcanistLinter {
|
||||
const FLINT = "/home/engshare/tools/flint";
|
||||
const LINT_ERROR = 1;
|
||||
const LINT_WARNING = 2;
|
||||
const LINT_ADVICE = 3;
|
||||
const C_FLAG = "--c_mode=true";
|
||||
|
||||
private $rawLintOutput = array();
|
||||
|
||||
public function willLintPaths(array $paths) {
|
||||
if (!file_exists(self::FLINT)) {
|
||||
return;
|
||||
}
|
||||
$futures = array();
|
||||
foreach ($paths as $p) {
|
||||
$lpath = $this->getEngine()->getFilePathOnDisk($p);
|
||||
$lpath_file = file($lpath);
|
||||
if (preg_match('/\.(c)$/', $lpath) ||
|
||||
preg_match('/-\*-.*Mode: C[; ].*-\*-/', $lpath_file[0]) ||
|
||||
preg_match('/vim(:.*)*:\s*(set\s+)?filetype=c\s*:/', $lpath_file[0])
|
||||
) {
|
||||
$futures[$p] = new ExecFuture("%s %s %s 2>&1",
|
||||
self::FLINT, self::C_FLAG,
|
||||
$this->getEngine()->getFilePathOnDisk($p));
|
||||
} else {
|
||||
$futures[$p] = new ExecFuture("%s %s 2>&1",
|
||||
self::FLINT, $this->getEngine()->getFilePathOnDisk($p));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Futures($futures)->limit(8) as $p => $f) {
|
||||
$this->rawLintOutput[$p] = $f->resolvex();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return "FBCPP";
|
||||
}
|
||||
|
||||
public function lintPath($path) {
|
||||
$this->runCppLint($path);
|
||||
}
|
||||
|
||||
private function runCppLint($path) {
|
||||
$msgs = $this->getCppLintOutput($path);
|
||||
foreach ($msgs as $m) {
|
||||
$this->raiseLintAtLine($m['line'], 0, $m['severity'], $m['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
private function adviseOnEachPattern(
|
||||
$path,
|
||||
$regex,
|
||||
$message,
|
||||
$lint_type = self::LINT_ADVICE,
|
||||
$match_idx = 0) {
|
||||
$file_data = $this->getData($path);
|
||||
$matches = array();
|
||||
if (!preg_match_all($regex, $file_data, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($matches[$match_idx] as $match) {
|
||||
list($match_str, $offset) = $match;
|
||||
$this->raiseLintAtOffset($offset, $lint_type, $message, $match_str);
|
||||
}
|
||||
}
|
||||
|
||||
public function getLintSeverityMap() {
|
||||
return array(
|
||||
self::LINT_WARNING => ArcanistLintSeverity::SEVERITY_WARNING,
|
||||
self::LINT_ADVICE => ArcanistLintSeverity::SEVERITY_ADVICE,
|
||||
self::LINT_ERROR => ArcanistLintSeverity::SEVERITY_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
public function getLintNameMap() {
|
||||
return array(
|
||||
self::LINT_ADVICE => "CppLint Advice",
|
||||
self::LINT_WARNING => "CppLint Warning",
|
||||
self::LINT_ERROR => "CppLint Error"
|
||||
);
|
||||
}
|
||||
|
||||
private function getCppLintOutput($path) {
|
||||
if (!array_key_exists($path, $this->rawLintOutput)) {
|
||||
return array();
|
||||
}
|
||||
list($output) = $this->rawLintOutput[$path];
|
||||
|
||||
$msgs = array();
|
||||
$current = null;
|
||||
$matches = array();
|
||||
foreach (explode("\n", $output) as $line) {
|
||||
if (preg_match('/.*?:(\d+):(.*)/', $line, $matches)) {
|
||||
if ($current) {
|
||||
$msgs[] = $current;
|
||||
}
|
||||
$line = $matches[1];
|
||||
$text = $matches[2];
|
||||
if (preg_match('/.*Warning.*/', $text)) {
|
||||
$sev = self::LINT_WARNING;
|
||||
} else if (preg_match('/.*Advice.*/', $text)) {
|
||||
$sev = self::LINT_ADVICE;
|
||||
} else {
|
||||
$sev = self::LINT_ERROR;
|
||||
}
|
||||
$current = array('line' => $line,
|
||||
'msg' => $text,
|
||||
'severity' => $sev);
|
||||
} else if ($current) {
|
||||
$current['msg'] .= ' ' . $line;
|
||||
}
|
||||
}
|
||||
if ($current) {
|
||||
$msgs[] = $current;
|
||||
}
|
||||
|
||||
return $msgs;
|
||||
}
|
||||
}
|
||||
Vendored
-4767
File diff suppressed because it is too large.
Load diff
@@ -1,138 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
class FacebookFbcodeLintEngine extends ArcanistLintEngine {
|
||||
|
||||
public function buildLinters() {
|
||||
$linters = array();
|
||||
$paths = $this->getPaths();
|
||||
|
||||
// Remove all deleted files, which are not checked by the
|
||||
// following linters.
|
||||
foreach ($paths as $key => $path) {
|
||||
if (!Filesystem::pathExists($this->getFilePathOnDisk($path))) {
|
||||
unset($paths[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$generated_linter = new ArcanistGeneratedLinter();
|
||||
$linters[] = $generated_linter;
|
||||
|
||||
$nolint_linter = new ArcanistNoLintLinter();
|
||||
$linters[] = $nolint_linter;
|
||||
|
||||
$text_linter = new ArcanistTextLinter();
|
||||
$text_linter->setCustomSeverityMap(array(
|
||||
ArcanistTextLinter::LINT_LINE_WRAP
|
||||
=> ArcanistLintSeverity::SEVERITY_ADVICE,
|
||||
));
|
||||
$linters[] = $text_linter;
|
||||
|
||||
$java_text_linter = new ArcanistTextLinter();
|
||||
$java_text_linter->setMaxLineLength(100);
|
||||
$java_text_linter->setCustomSeverityMap(array(
|
||||
ArcanistTextLinter::LINT_LINE_WRAP
|
||||
=> ArcanistLintSeverity::SEVERITY_ADVICE,
|
||||
));
|
||||
$linters[] = $java_text_linter;
|
||||
|
||||
$python_linter = new ArcanistPEP8Linter();
|
||||
$linters[] = $python_linter;
|
||||
|
||||
$cpp_linters = array();
|
||||
$cpp_linters[] = $linters[] = new ArcanistCpplintLinter();
|
||||
$cpp_linters[] = $linters[] = new FbcodeCppLinter();
|
||||
|
||||
$clang_format_linter = new FbcodeClangFormatLinter();
|
||||
$linters[] = $clang_format_linter;
|
||||
|
||||
$spelling_linter = new ArcanistSpellingLinter();
|
||||
$linters[] = $spelling_linter;
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$is_text = false;
|
||||
|
||||
$text_extensions = (
|
||||
'/\.('.
|
||||
'cpp|cxx|c|cc|h|hpp|hxx|tcc|'.
|
||||
'py|rb|hs|pl|pm|tw|'.
|
||||
'php|phpt|css|js|'.
|
||||
'java|'.
|
||||
'thrift|'.
|
||||
'lua|'.
|
||||
'siv|'.
|
||||
'txt'.
|
||||
')$/'
|
||||
);
|
||||
if (preg_match($text_extensions, $path)) {
|
||||
$is_text = true;
|
||||
}
|
||||
if ($is_text) {
|
||||
$nolint_linter->addPath($path);
|
||||
|
||||
$generated_linter->addPath($path);
|
||||
$generated_linter->addData($path, $this->loadData($path));
|
||||
|
||||
if (preg_match('/\.java$/', $path)) {
|
||||
$java_text_linter->addPath($path);
|
||||
$java_text_linter->addData($path, $this->loadData($path));
|
||||
} else {
|
||||
$text_linter->addPath($path);
|
||||
$text_linter->addData($path, $this->loadData($path));
|
||||
}
|
||||
|
||||
$spelling_linter->addPath($path);
|
||||
$spelling_linter->addData($path, $this->loadData($path));
|
||||
}
|
||||
if (preg_match('/\.(cpp|c|cc|cxx|h|hh|hpp|hxx|tcc)$/', $path)
|
||||
&& !preg_match('/third-party/', $path)) {
|
||||
foreach ($cpp_linters as &$linter) {
|
||||
$linter->addPath($path);
|
||||
$linter->addData($path, $this->loadData($path));
|
||||
}
|
||||
|
||||
$clang_format_linter->addPath($path);
|
||||
$clang_format_linter->addData($path, $this->loadData($path));
|
||||
$clang_format_linter->setPathChangedLines(
|
||||
$path, $this->getPathChangedLines($path));
|
||||
}
|
||||
|
||||
// Match *.py and contbuild config files
|
||||
if (preg_match('/(\.(py|tw|smcprops)|^contbuild\/configs\/[^\/]*)$/',
|
||||
$path)) {
|
||||
$space_count = 4;
|
||||
$real_path = $this->getFilePathOnDisk($path);
|
||||
$dir = dirname($real_path);
|
||||
do {
|
||||
if (file_exists($dir.'/.python2space')) {
|
||||
$space_count = 2;
|
||||
break;
|
||||
}
|
||||
$dir = dirname($dir);
|
||||
} while ($dir != '/' && $dir != '.');
|
||||
|
||||
$cur_path_linter = $python_linter;
|
||||
$cur_path_linter->addPath($path);
|
||||
$cur_path_linter->addData($path, $this->loadData($path));
|
||||
|
||||
if (preg_match('/\.tw$/', $path)) {
|
||||
$cur_path_linter->setCustomSeverityMap(array(
|
||||
'E251' => ArcanistLintSeverity::SEVERITY_DISABLED,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$name_linter = new ArcanistFilenameLinter();
|
||||
$linters[] = $name_linter;
|
||||
foreach ($paths as $path) {
|
||||
$name_linter->addPath($path);
|
||||
}
|
||||
|
||||
return $linters;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2015-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
final class FacebookHowtoevenLintEngine extends ArcanistLintEngine {
|
||||
|
||||
public function buildLinters() {
|
||||
$paths = array();
|
||||
|
||||
foreach ($this->getPaths() as $path) {
|
||||
// Don't try to lint deleted files or changed directories.
|
||||
if (!Filesystem::pathExists($path) || is_dir($path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('/\.(cpp|c|cc|cxx|h|hh|hpp|hxx|tcc)$/', $path)) {
|
||||
$paths[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
$howtoeven = new FacebookHowtoevenLinter();
|
||||
$howtoeven->setPaths($paths);
|
||||
return array($howtoeven);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// For a call to `arc call-conduit differential.updateunitresults` to
|
||||
// succeed we need at least one entry here.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("dummy_placeholder_entry");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
|
||||
return array($result);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistBaseUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// For a call to `arc call-conduit differential.updateunitresults` to
|
||||
// succeed we need at least one entry here.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("dummy_placeholder_entry");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
|
||||
return array($result);
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,6 @@ rocksdb_compiler_flags = [
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
|
||||
@@ -51,12 +51,7 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -128,17 +123,10 @@ case "$TARGET_OS" in
|
||||
;;
|
||||
SunOS)
|
||||
PLATFORM=OS_SOLARIS
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_SOLARIS -m64"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -static-libstdc++ -static-libgcc -m64"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_SOLARIS"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/sunos/sunos_specific.cc
|
||||
;;
|
||||
AIX)
|
||||
PLATFORM=OS_AIX
|
||||
CC=gcc
|
||||
COMMON_FLAGS="$COMMON_FLAGS -maix64 -pthread -fno-builtin-memcmp -D_REENTRANT -DOS_AIX -D__STDC_FORMAT_MACROS"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -pthread -lpthread -lrt -maix64 -static-libstdc++ -static-libgcc"
|
||||
# PORT_FILES=port/aix/aix_specific.cc
|
||||
;;
|
||||
FreeBSD)
|
||||
PLATFORM=OS_FREEBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_FREEBSD"
|
||||
@@ -389,29 +377,6 @@ EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PROFILING_FLAGS=-pg
|
||||
fi
|
||||
|
||||
# Test whether sync_file_range is supported for compatibility with an old glibc
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
sync_file_range(fd, 0, 1024, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_RANGESYNC_PRESENT"
|
||||
fi
|
||||
|
||||
# Test whether sched_getcpu is supported
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
@@ -457,7 +422,7 @@ elif test -z "$PORTABLE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
|
||||
elif [ "$TARGET_OS" != AIX ] && [ "$TARGET_OS" != SunOS ]; then
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
fi
|
||||
fi
|
||||
|
||||
+19
-18
@@ -1,18 +1,19 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/2928bb3ed95bf64f5b388ee88c30dc74710c3b35/5.x/centos6-native/f4950a1
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/a5fea028cb7ba43498976e1f8054b0b2e790c295/stable/centos6-native/6aaf4de
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/7a9099f6587ee4378c0b1fa32bb8934019d30ca4/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/3b7c6469854dfc7832a1c3cc5b86919a84e5f865/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/d7861abe6f0e27ab98c9303b95a662f0e4cdedb5/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0815d59804160c96caac5f27ca004f51af893dc6/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/c15a4f5f619a2930478d01e2e34dc1e0652b0873/1.1.4/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f905a5e1032fb30c05db3d3752319857388c0c49/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/8d60633d822a2a55849c73db24e74a25e52b71db/master/gcc-5-glibc-2.23/1c32b4b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/8db74270cd6d0212ac92d69e7fc7beefe617d772/trunk/gcc-5-glibc-2.23/b1847cb
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/90c9734afc5579c9d1db529fa788d09f97763b85/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/9e829389ef61b92c62de8748c80169aaf25ce1f0/2.26.1/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.11.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -13,7 +13,7 @@ source "$BASEDIR/dependencies.sh"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
@@ -43,12 +43,16 @@ if test -z $PIC_BUILD; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
@@ -104,8 +108,8 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -116,8 +120,8 @@ else
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/5.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/5.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
@@ -128,14 +132,14 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-5-glibc-2.23/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-5-glibc-2.23/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
@@ -145,12 +149,4 @@ EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GF
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua.a"
|
||||
else
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua_pic.a"
|
||||
fi
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
@@ -93,7 +93,7 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA -DTBB"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
set -e
|
||||
if [ -z "$GIT" ]
|
||||
then
|
||||
GIT="git"
|
||||
fi
|
||||
|
||||
# Print out the colored progress info so that it can be brainlessly
|
||||
# distinguished by users.
|
||||
function title() {
|
||||
echo -e "\033[1;32m$*\033[0m"
|
||||
}
|
||||
|
||||
usage="Create new RocksDB version and prepare it for the release process\n"
|
||||
usage+="USAGE: ./make_new_version.sh <version> [<remote>]\n"
|
||||
usage+=" version: specify a version without '.fb' suffix (e.g. 5.4).\n"
|
||||
usage+=" remote: name of the remote to push the branch to (default: origin)."
|
||||
|
||||
# -- Pre-check
|
||||
if [[ $# < 1 ]]; then
|
||||
echo -e $usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROCKSDB_VERSION=$1
|
||||
|
||||
REMOTE="origin"
|
||||
if [[ $# > 1 ]]; then
|
||||
REMOTE=$2
|
||||
fi
|
||||
|
||||
GIT_BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
echo $GIT_BRANCH
|
||||
|
||||
if [ $GIT_BRANCH != "master" ]; then
|
||||
echo "Error: Current branch is '$GIT_BRANCH', Please switch to master branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
title "Adding new tag for this release ..."
|
||||
BRANCH="$ROCKSDB_VERSION.fb"
|
||||
$GIT checkout -b $BRANCH
|
||||
|
||||
# Setting up the proxy for remote repo access
|
||||
title "Pushing new branch to remote repo ..."
|
||||
git push $REMOTE --set-upstream $BRANCH
|
||||
|
||||
title "Branch $BRANCH is pushed to github;"
|
||||
@@ -99,18 +99,6 @@ function main() {
|
||||
|
||||
make static_lib
|
||||
make install INSTALL_PATH=package
|
||||
|
||||
cd package
|
||||
|
||||
LIB_DIR=lib
|
||||
if [[ -z "$ARCH" ]]; then
|
||||
ARCH=$(getconf LONG_BIT)
|
||||
fi
|
||||
if [[ ("$FPM_OUTPUT" = "rpm") && ($ARCH -eq 64) ]]; then
|
||||
mv lib lib64
|
||||
LIB_DIR=lib64
|
||||
fi
|
||||
|
||||
fpm \
|
||||
-s dir \
|
||||
-t $FPM_OUTPUT \
|
||||
@@ -122,7 +110,7 @@ function main() {
|
||||
--license BSD \
|
||||
--vendor Facebook \
|
||||
--description "RocksDB is an embeddable persistent key-value store for fast storage." \
|
||||
include $LIB_DIR
|
||||
package
|
||||
}
|
||||
|
||||
main $@
|
||||
@@ -276,7 +276,7 @@ if($RunAll) {
|
||||
continue
|
||||
}
|
||||
|
||||
if($RunOnly.Contains($bare_name)) {
|
||||
if($RunOnly.Contains($e)) {
|
||||
MakeAndAdd -token $e -HashTable $TestExes
|
||||
} else {
|
||||
$ListOfSuites += $bare_name
|
||||
|
||||
@@ -53,7 +53,7 @@ function get_lib_base()
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
# 4.9.x dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies.sh"
|
||||
@@ -64,16 +64,16 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/5.x/centos6-native/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/4.9.x/centos6-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos6-native/*/`
|
||||
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 5.x
|
||||
get_lib_base glibc 2.23
|
||||
get_lib_base snappy LATEST gcc-5-glibc-2.23
|
||||
get_lib_base libgcc 4.9.x
|
||||
get_lib_base glibc 2.20
|
||||
get_lib_base snappy LATEST gcc-4.9-glibc-2.20
|
||||
get_lib_base zlib LATEST
|
||||
get_lib_base bzip2 LATEST
|
||||
get_lib_base lz4 LATEST
|
||||
@@ -82,12 +82,12 @@ get_lib_base gflags LATEST
|
||||
get_lib_base jemalloc LATEST
|
||||
get_lib_base numa LATEST
|
||||
get_lib_base libunwind LATEST
|
||||
get_lib_base tbb 4.0_update2 gcc-5-glibc-2.23
|
||||
get_lib_base tbb 4.0_update2 gcc-4.9-glibc-2.20
|
||||
|
||||
get_lib_base kernel-headers LATEST
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base valgrind 3.10.0 gcc-5-glibc-2.23
|
||||
get_lib_base lua 5.2.3 gcc-5-glibc-2.23
|
||||
get_lib_base valgrind 3.10.0 gcc-4.9-glibc-2.20
|
||||
get_lib_base lua 5.2.3 gcc-4.9-glibc-2.20
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
|
||||
Vendored
-31
@@ -475,37 +475,6 @@ void deleter(const Slice& key, void* value) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_P(CacheTest, ReleaseAndErase) {
|
||||
std::shared_ptr<Cache> cache = NewCache(5, 0, false);
|
||||
Cache::Handle* handle;
|
||||
Status s = cache->Insert(EncodeKey(100), EncodeValue(100), 1,
|
||||
&CacheTest::Deleter, &handle);
|
||||
ASSERT_TRUE(s.ok());
|
||||
ASSERT_EQ(5U, cache->GetCapacity());
|
||||
ASSERT_EQ(1U, cache->GetUsage());
|
||||
ASSERT_EQ(0U, deleted_keys_.size());
|
||||
auto erased = cache->Release(handle, true);
|
||||
ASSERT_TRUE(erased);
|
||||
// This tests that deleter has been called
|
||||
ASSERT_EQ(1U, deleted_keys_.size());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, ReleaseWithoutErase) {
|
||||
std::shared_ptr<Cache> cache = NewCache(5, 0, false);
|
||||
Cache::Handle* handle;
|
||||
Status s = cache->Insert(EncodeKey(100), EncodeValue(100), 1,
|
||||
&CacheTest::Deleter, &handle);
|
||||
ASSERT_TRUE(s.ok());
|
||||
ASSERT_EQ(5U, cache->GetCapacity());
|
||||
ASSERT_EQ(1U, cache->GetUsage());
|
||||
ASSERT_EQ(0U, deleted_keys_.size());
|
||||
auto erased = cache->Release(handle);
|
||||
ASSERT_FALSE(erased);
|
||||
// This tests that deleter is not called. When cache has free capacity it is
|
||||
// not expected to immediately erase the released items.
|
||||
ASSERT_EQ(0U, deleted_keys_.size());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, SetCapacity) {
|
||||
// test1: increase capacity
|
||||
// lets create a cache with capacity 5,
|
||||
|
||||
Vendored
+17
-34
@@ -26,6 +26,7 @@ std::shared_ptr<Cache> NewClockCache(size_t capacity, int num_shard_bits,
|
||||
#include <assert.h>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
|
||||
#include "tbb/concurrent_hash_map.h"
|
||||
|
||||
@@ -251,11 +252,8 @@ class ClockCacheShard : public CacheShard {
|
||||
//
|
||||
// Not necessary to hold mutex_ before being called.
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
bool force_erase = false) override;
|
||||
virtual void Release(Cache::Handle* handle) override;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) override;
|
||||
bool EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
CleanupContext* context);
|
||||
virtual size_t GetUsage() const override;
|
||||
virtual size_t GetPinnedUsage() const override;
|
||||
virtual void EraseUnRefEntries() override;
|
||||
@@ -276,17 +274,13 @@ class ClockCacheShard : public CacheShard {
|
||||
// Decrease reference count of the entry. If this decreases the count to 0,
|
||||
// recycle the entry. If set_usage is true, also set the usage bit.
|
||||
//
|
||||
// returns true if a value is erased.
|
||||
//
|
||||
// Not necessary to hold mutex_ before being called.
|
||||
bool Unref(CacheHandle* handle, bool set_usage, CleanupContext* context);
|
||||
void Unref(CacheHandle* handle, bool set_usage, CleanupContext* context);
|
||||
|
||||
// Unset in-cache bit of the entry. Recycle the handle if necessary.
|
||||
//
|
||||
// returns true if a value is erased.
|
||||
//
|
||||
// Has to hold mutex_ before being called.
|
||||
bool UnsetInCache(CacheHandle* handle, CleanupContext* context);
|
||||
void UnsetInCache(CacheHandle* handle, CleanupContext* context);
|
||||
|
||||
// Put the handle back to recycle_ list, and put the value associated with
|
||||
// it into to-be-deleted list. It doesn't cleanup the key as it might be
|
||||
@@ -438,7 +432,7 @@ bool ClockCacheShard::Ref(Cache::Handle* h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClockCacheShard::Unref(CacheHandle* handle, bool set_usage,
|
||||
void ClockCacheShard::Unref(CacheHandle* handle, bool set_usage,
|
||||
CleanupContext* context) {
|
||||
if (set_usage) {
|
||||
handle->flags.fetch_or(kUsageBit, std::memory_order_relaxed);
|
||||
@@ -457,10 +451,9 @@ bool ClockCacheShard::Unref(CacheHandle* handle, bool set_usage,
|
||||
RecycleHandle(handle, context);
|
||||
}
|
||||
}
|
||||
return context->to_delete_value.size();
|
||||
}
|
||||
|
||||
bool ClockCacheShard::UnsetInCache(CacheHandle* handle,
|
||||
void ClockCacheShard::UnsetInCache(CacheHandle* handle,
|
||||
CleanupContext* context) {
|
||||
mutex_.AssertHeld();
|
||||
// Use acquire-release semantics as previous operations on the cache entry
|
||||
@@ -472,7 +465,6 @@ bool ClockCacheShard::UnsetInCache(CacheHandle* handle,
|
||||
if (InCache(flags) && CountRefs(flags) == 0) {
|
||||
RecycleHandle(handle, context);
|
||||
}
|
||||
return context->to_delete_value.size();
|
||||
}
|
||||
|
||||
bool ClockCacheShard::TryEvict(CacheHandle* handle, CleanupContext* context) {
|
||||
@@ -626,34 +618,25 @@ Cache::Handle* ClockCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
return reinterpret_cast<Cache::Handle*>(handle);
|
||||
}
|
||||
|
||||
bool ClockCacheShard::Release(Cache::Handle* h, bool force_erase) {
|
||||
void ClockCacheShard::Release(Cache::Handle* h) {
|
||||
CleanupContext context;
|
||||
CacheHandle* handle = reinterpret_cast<CacheHandle*>(h);
|
||||
bool erased = Unref(handle, true, &context);
|
||||
if (force_erase && !erased) {
|
||||
erased = EraseAndConfirm(handle->key, handle->hash, &context);
|
||||
}
|
||||
Unref(handle, true, &context);
|
||||
Cleanup(context);
|
||||
return erased;
|
||||
}
|
||||
|
||||
void ClockCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
CleanupContext context;
|
||||
EraseAndConfirm(key, hash, &context);
|
||||
Cleanup(context);
|
||||
}
|
||||
|
||||
bool ClockCacheShard::EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
CleanupContext* context) {
|
||||
MutexLock l(&mutex_);
|
||||
HashTable::accessor accessor;
|
||||
bool erased = false;
|
||||
if (table_.find(accessor, CacheKey(key, hash))) {
|
||||
CacheHandle* handle = accessor->second;
|
||||
table_.erase(accessor);
|
||||
erased = UnsetInCache(handle, context);
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
HashTable::accessor accessor;
|
||||
if (table_.find(accessor, CacheKey(key, hash))) {
|
||||
CacheHandle* handle = accessor->second;
|
||||
table_.erase(accessor);
|
||||
UnsetInCache(handle, &context);
|
||||
}
|
||||
}
|
||||
return erased;
|
||||
Cleanup(context);
|
||||
}
|
||||
|
||||
void ClockCacheShard::EraseUnRefEntries() {
|
||||
|
||||
Vendored
+4
-5
@@ -273,9 +273,9 @@ void LRUCacheShard::SetHighPriorityPoolRatio(double high_pri_pool_ratio) {
|
||||
MaintainPoolSize();
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
void LRUCacheShard::Release(Cache::Handle* handle) {
|
||||
if (handle == nullptr) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
|
||||
bool last_reference = false;
|
||||
@@ -287,10 +287,10 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
}
|
||||
if (e->refs == 1 && e->InCache()) {
|
||||
// The item is still in cache, and nobody else holds a reference to it
|
||||
if (usage_ > capacity_ || force_erase) {
|
||||
if (usage_ > capacity_) {
|
||||
// the cache is full
|
||||
// The LRU list must be empty since the cache is full
|
||||
assert(!(usage_ > capacity_) || lru_.next == &lru_);
|
||||
assert(lru_.next == &lru_);
|
||||
// take this opportunity and remove the item
|
||||
table_.Remove(e->key(), e->hash);
|
||||
e->SetInCache(false);
|
||||
@@ -308,7 +308,6 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
return last_reference;
|
||||
}
|
||||
|
||||
Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
|
||||
Vendored
+1
-2
@@ -178,8 +178,7 @@ class LRUCacheShard : public CacheShard {
|
||||
Cache::Priority priority) override;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
bool force_erase = false) override;
|
||||
virtual void Release(Cache::Handle* handle) override;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) override;
|
||||
|
||||
// Although in some platforms the update of size_t is atomic, to make sure
|
||||
|
||||
Vendored
+2
-2
@@ -63,9 +63,9 @@ bool ShardedCache::Ref(Handle* handle) {
|
||||
return GetShard(Shard(hash))->Ref(handle);
|
||||
}
|
||||
|
||||
bool ShardedCache::Release(Handle* handle, bool force_erase) {
|
||||
void ShardedCache::Release(Handle* handle) {
|
||||
uint32_t hash = GetHash(handle);
|
||||
return GetShard(Shard(hash))->Release(handle, force_erase);
|
||||
GetShard(Shard(hash))->Release(handle);
|
||||
}
|
||||
|
||||
void ShardedCache::Erase(const Slice& key) {
|
||||
|
||||
Vendored
+2
-2
@@ -30,7 +30,7 @@ class CacheShard {
|
||||
Cache::Handle** handle, Cache::Priority priority) = 0;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) = 0;
|
||||
virtual bool Ref(Cache::Handle* handle) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool force_erase = false) = 0;
|
||||
virtual void Release(Cache::Handle* handle) = 0;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) = 0;
|
||||
virtual void SetCapacity(size_t capacity) = 0;
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) = 0;
|
||||
@@ -65,7 +65,7 @@ class ShardedCache : public Cache {
|
||||
Handle** handle, Priority priority) override;
|
||||
virtual Handle* Lookup(const Slice& key, Statistics* stats) override;
|
||||
virtual bool Ref(Handle* handle) override;
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override;
|
||||
virtual void Release(Handle* handle) override;
|
||||
virtual void Erase(const Slice& key) override;
|
||||
virtual uint64_t NewId() override;
|
||||
virtual size_t GetCapacity() const override;
|
||||
|
||||
@@ -34,9 +34,6 @@
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "rocksdb/utilities/transaction.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
|
||||
using rocksdb::BytewiseComparator;
|
||||
using rocksdb::Cache;
|
||||
@@ -90,12 +87,6 @@ using rocksdb::RestoreOptions;
|
||||
using rocksdb::CompactRangeOptions;
|
||||
using rocksdb::RateLimiter;
|
||||
using rocksdb::NewGenericRateLimiter;
|
||||
using rocksdb::PinnableSlice;
|
||||
using rocksdb::TransactionDBOptions;
|
||||
using rocksdb::TransactionDB;
|
||||
using rocksdb::TransactionOptions;
|
||||
using rocksdb::Transaction;
|
||||
using rocksdb::Checkpoint;
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
@@ -134,24 +125,6 @@ struct rocksdb_envoptions_t { EnvOptions rep; };
|
||||
struct rocksdb_ingestexternalfileoptions_t { IngestExternalFileOptions rep; };
|
||||
struct rocksdb_sstfilewriter_t { SstFileWriter* rep; };
|
||||
struct rocksdb_ratelimiter_t { RateLimiter* rep; };
|
||||
struct rocksdb_pinnableslice_t {
|
||||
PinnableSlice rep;
|
||||
};
|
||||
struct rocksdb_transactiondb_options_t {
|
||||
TransactionDBOptions rep;
|
||||
};
|
||||
struct rocksdb_transactiondb_t {
|
||||
TransactionDB* rep;
|
||||
};
|
||||
struct rocksdb_transaction_options_t {
|
||||
TransactionOptions rep;
|
||||
};
|
||||
struct rocksdb_transaction_t {
|
||||
Transaction* rep;
|
||||
};
|
||||
struct rocksdb_checkpoint_t {
|
||||
Checkpoint* rep;
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfiltercontext_t {
|
||||
CompactionFilter::Context rep;
|
||||
@@ -564,29 +537,6 @@ void rocksdb_backup_engine_close(rocksdb_backup_engine_t* be) {
|
||||
delete be;
|
||||
}
|
||||
|
||||
rocksdb_checkpoint_t* rocksdb_checkpoint_object_create(rocksdb_t* db,
|
||||
char** errptr) {
|
||||
Checkpoint* checkpoint;
|
||||
if (SaveError(errptr, Checkpoint::Create(db->rep, &checkpoint))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_checkpoint_t* result = new rocksdb_checkpoint_t;
|
||||
result->rep = checkpoint;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_checkpoint_create(rocksdb_checkpoint_t* checkpoint,
|
||||
const char* checkpoint_dir,
|
||||
uint64_t log_size_for_flush, char** errptr) {
|
||||
SaveError(errptr, checkpoint->rep->CreateCheckpoint(
|
||||
std::string(checkpoint_dir), log_size_for_flush));
|
||||
}
|
||||
|
||||
void rocksdb_checkpoint_object_destroy(rocksdb_checkpoint_t* checkpoint) {
|
||||
delete checkpoint->rep;
|
||||
delete checkpoint;
|
||||
}
|
||||
|
||||
void rocksdb_close(rocksdb_t* db) {
|
||||
delete db->rep;
|
||||
delete db;
|
||||
@@ -1410,10 +1360,6 @@ void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
|
||||
SaveError(errptr, b->rep.RollbackToSavePoint());
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_pop_save_point(rocksdb_writebatch_t* b, char** errptr) {
|
||||
SaveError(errptr, b->rep.PopSavePoint());
|
||||
}
|
||||
|
||||
rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create(size_t reserved_bytes, unsigned char overwrite_key) {
|
||||
rocksdb_writebatch_wi_t* b = new rocksdb_writebatch_wi_t;
|
||||
b->rep = new WriteBatchWithIndex(BytewiseComparator(), reserved_bytes, overwrite_key);
|
||||
@@ -2037,10 +1983,6 @@ void rocksdb_options_set_max_open_files(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_open_files = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_file_opening_threads(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_file_opening_threads = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_total_wal_size(rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_total_wal_size = n;
|
||||
}
|
||||
@@ -3138,270 +3080,8 @@ void rocksdb_delete_file_in_range_cf(
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr)));
|
||||
}
|
||||
|
||||
rocksdb_transactiondb_options_t* rocksdb_transactiondb_options_create() {
|
||||
return new rocksdb_transactiondb_options_t;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_options_destroy(rocksdb_transactiondb_options_t* opt){
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_options_set_max_num_locks(
|
||||
rocksdb_transactiondb_options_t* opt, int64_t max_num_locks) {
|
||||
opt->rep.max_num_locks = max_num_locks;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_options_set_num_stripes(
|
||||
rocksdb_transactiondb_options_t* opt, size_t num_stripes) {
|
||||
opt->rep.num_stripes = num_stripes;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_options_set_transaction_lock_timeout(
|
||||
rocksdb_transactiondb_options_t* opt, int64_t txn_lock_timeout) {
|
||||
opt->rep.transaction_lock_timeout = txn_lock_timeout;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_options_set_default_lock_timeout(
|
||||
rocksdb_transactiondb_options_t* opt, int64_t default_lock_timeout) {
|
||||
opt->rep.default_lock_timeout = default_lock_timeout;
|
||||
}
|
||||
|
||||
rocksdb_transaction_options_t* rocksdb_transaction_options_create() {
|
||||
return new rocksdb_transaction_options_t;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_destroy(rocksdb_transaction_options_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_set_set_snapshot(
|
||||
rocksdb_transaction_options_t* opt, unsigned char v) {
|
||||
opt->rep.set_snapshot = v;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_set_deadlock_detect(
|
||||
rocksdb_transaction_options_t* opt, unsigned char v) {
|
||||
opt->rep.deadlock_detect = v;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_set_lock_timeout(
|
||||
rocksdb_transaction_options_t* opt, int64_t lock_timeout) {
|
||||
opt->rep.lock_timeout = lock_timeout;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_set_expiration(
|
||||
rocksdb_transaction_options_t* opt, int64_t expiration) {
|
||||
opt->rep.expiration = expiration;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_set_deadlock_detect_depth(
|
||||
rocksdb_transaction_options_t* opt, int64_t depth) {
|
||||
opt->rep.deadlock_detect_depth = depth;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_options_set_max_write_batch_size(
|
||||
rocksdb_transaction_options_t* opt, size_t size) {
|
||||
opt->rep.max_write_batch_size = size;
|
||||
}
|
||||
|
||||
rocksdb_transactiondb_t* rocksdb_transactiondb_open(
|
||||
const rocksdb_options_t* options,
|
||||
const rocksdb_transactiondb_options_t* txn_db_options, const char* name,
|
||||
char** errptr) {
|
||||
TransactionDB* txn_db;
|
||||
if (SaveError(errptr, TransactionDB::Open(options->rep, txn_db_options->rep,
|
||||
std::string(name), &txn_db))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_transactiondb_t* result = new rocksdb_transactiondb_t;
|
||||
result->rep = txn_db;
|
||||
return result;
|
||||
}
|
||||
|
||||
const rocksdb_snapshot_t* rocksdb_transactiondb_create_snapshot(
|
||||
rocksdb_transactiondb_t* txn_db) {
|
||||
rocksdb_snapshot_t* result = new rocksdb_snapshot_t;
|
||||
result->rep = txn_db->rep->GetSnapshot();
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_release_snapshot(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_snapshot_t* snapshot) {
|
||||
txn_db->rep->ReleaseSnapshot(snapshot->rep);
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
rocksdb_transaction_t* rocksdb_transaction_begin(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* write_options,
|
||||
const rocksdb_transaction_options_t* txn_options,
|
||||
rocksdb_transaction_t* old_txn) {
|
||||
if (old_txn == nullptr) {
|
||||
rocksdb_transaction_t* result = new rocksdb_transaction_t;
|
||||
result->rep = txn_db->rep->BeginTransaction(write_options->rep,
|
||||
txn_options->rep, nullptr);
|
||||
return result;
|
||||
}
|
||||
old_txn->rep = txn_db->rep->BeginTransaction(write_options->rep,
|
||||
txn_options->rep, old_txn->rep);
|
||||
return old_txn;
|
||||
}
|
||||
|
||||
void rocksdb_transaction_commit(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Commit());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_rollback(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Rollback());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_destroy(rocksdb_transaction_t* txn) {
|
||||
delete txn->rep;
|
||||
delete txn;
|
||||
}
|
||||
|
||||
//Read a key inside a transaction
|
||||
char* rocksdb_transaction_get(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, size_t klen, size_t* vlen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = txn->rep->Get(options->rep, Slice(key, klen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vlen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vlen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read a key outside a transaction
|
||||
char* rocksdb_transactiondb_get(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, size_t klen,
|
||||
size_t* vlen,
|
||||
char** errptr){
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = txn_db->rep->Get(options->rep, Slice(key, klen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vlen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vlen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Put a key inside a transaction
|
||||
void rocksdb_transaction_put(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, const char* val, size_t vlen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn->rep->Put(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
//Put a key outside a transaction
|
||||
void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr,
|
||||
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
// Delete a key inside a transaction
|
||||
void rocksdb_transaction_delete(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Delete(Slice(key, klen)));
|
||||
}
|
||||
|
||||
// Delete a key outside a transaction
|
||||
void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
// Create an iterator inside a transaction
|
||||
rocksdb_iterator_t* rocksdb_transaction_create_iterator(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = txn->rep->GetIterator(options->rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_close(rocksdb_transactiondb_t* txn_db) {
|
||||
delete txn_db->rep;
|
||||
delete txn_db;
|
||||
}
|
||||
|
||||
rocksdb_checkpoint_t* rocksdb_transactiondb_checkpoint_object_create(
|
||||
rocksdb_transactiondb_t* txn_db, char** errptr) {
|
||||
Checkpoint* checkpoint;
|
||||
if (SaveError(errptr, Checkpoint::Create(txn_db->rep, &checkpoint))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_checkpoint_t* result = new rocksdb_checkpoint_t;
|
||||
result->rep = checkpoint;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_free(void* ptr) { free(ptr); }
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_get_pinned(
|
||||
rocksdb_t* db, const rocksdb_readoptions_t* options, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
rocksdb_pinnableslice_t* v = new (rocksdb_pinnableslice_t);
|
||||
Status s = db->rep->Get(options->rep, db->rep->DefaultColumnFamily(),
|
||||
Slice(key, keylen), &v->rep);
|
||||
if (!s.ok()) {
|
||||
delete (v);
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_get_pinned_cf(
|
||||
rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
rocksdb_pinnableslice_t* v = new (rocksdb_pinnableslice_t);
|
||||
Status s = db->rep->Get(options->rep, column_family->rep, Slice(key, keylen),
|
||||
&v->rep);
|
||||
if (!s.ok()) {
|
||||
delete v;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void rocksdb_pinnableslice_destroy(rocksdb_pinnableslice_t* v) { delete v; }
|
||||
|
||||
const char* rocksdb_pinnableslice_value(const rocksdb_pinnableslice_t* v,
|
||||
size_t* vlen) {
|
||||
if (!v) {
|
||||
*vlen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*vlen = v->rep.size();
|
||||
return v->rep.data();
|
||||
}
|
||||
} // end extern "C"
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
+5
-198
@@ -20,7 +20,7 @@
|
||||
// Can not use port/port.h macros as this is a c file
|
||||
#ifdef OS_WIN
|
||||
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#define snprintf _snprintf
|
||||
|
||||
@@ -40,7 +40,6 @@ const char* phase = "";
|
||||
static char dbname[200];
|
||||
static char sstfilename[200];
|
||||
static char dbbackupname[200];
|
||||
static char dbcheckpointname[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
@@ -127,32 +126,6 @@ static void CheckGetCF(
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckPinGet(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
const char* val;
|
||||
rocksdb_pinnableslice_t* p;
|
||||
p = rocksdb_get_pinned(db, options, key, strlen(key), &err);
|
||||
CheckNoError(err);
|
||||
val = rocksdb_pinnableslice_value(p, &val_len);
|
||||
CheckEqual(expected, val, val_len);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckPinGetCF(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* handle,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
const char* val;
|
||||
rocksdb_pinnableslice_t* p;
|
||||
p = rocksdb_get_pinned_cf(db, options, handle, key, strlen(key), &err);
|
||||
CheckNoError(err);
|
||||
val = rocksdb_pinnableslice_value(p, &val_len);
|
||||
CheckEqual(expected, val, val_len);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckIter(rocksdb_iterator_t* iter,
|
||||
const char* key, const char* val) {
|
||||
@@ -319,34 +292,6 @@ static char* MergeOperatorPartialMerge(
|
||||
return result;
|
||||
}
|
||||
|
||||
static void CheckTxnGet(
|
||||
rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transaction_get(txn, options, key, strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGet(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transactiondb_get(txn_db, options, key, strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb_t* db;
|
||||
rocksdb_comparator_t* cmp;
|
||||
@@ -358,10 +303,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_readoptions_t* roptions;
|
||||
rocksdb_writeoptions_t* woptions;
|
||||
rocksdb_ratelimiter_t* rate_limiter;
|
||||
rocksdb_transactiondb_t* txn_db;
|
||||
rocksdb_transactiondb_options_t* txn_db_options;
|
||||
rocksdb_transaction_t* txn;
|
||||
rocksdb_transaction_options_t* txn_options;
|
||||
char* err = NULL;
|
||||
int run = -1;
|
||||
|
||||
@@ -375,11 +316,6 @@ int main(int argc, char** argv) {
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(dbcheckpointname, sizeof(dbcheckpointname),
|
||||
"%s/rocksdb_c_test-%d-checkpoint",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(sstfilename, sizeof(sstfilename),
|
||||
"%s/rocksdb_c_test-%d-sst",
|
||||
GetTempDir(),
|
||||
@@ -495,36 +431,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_backup_engine_close(be);
|
||||
}
|
||||
|
||||
StartPhase("checkpoint");
|
||||
{
|
||||
rocksdb_destroy_db(options, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_t* checkpoint = rocksdb_checkpoint_object_create(db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// start a new database from the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("compactall");
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
@@ -681,13 +587,10 @@ int main(int argc, char** argv) {
|
||||
{
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_set_save_point(wb);
|
||||
rocksdb_writebatch_set_save_point(wb);
|
||||
const char* k_list[2] = {"z", "ap"};
|
||||
const size_t k_sizes[2] = {1, 2};
|
||||
const char* v_list[3] = {"x", "y", "z"};
|
||||
const size_t v_sizes[3] = {1, 1, 1};
|
||||
rocksdb_writebatch_pop_save_point(wb, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_writebatch_rollback_to_save_point(wb, &err);
|
||||
CheckNoError(err);
|
||||
@@ -883,13 +786,6 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
StartPhase("pin_get");
|
||||
{
|
||||
CheckPinGet(db, roptions, "box", "c");
|
||||
CheckPinGet(db, roptions, "foo", "hello");
|
||||
CheckPinGet(db, roptions, "notfound", NULL);
|
||||
}
|
||||
|
||||
StartPhase("approximate_sizes");
|
||||
{
|
||||
int i;
|
||||
@@ -1101,13 +997,11 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
CheckPinGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
|
||||
rocksdb_delete_cf(db, woptions, handles[1], "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "foo", NULL);
|
||||
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "baz", 3, "a", 1);
|
||||
@@ -1120,9 +1014,6 @@ int main(int argc, char** argv) {
|
||||
CheckGetCF(db, roptions, handles[1], "baz", NULL);
|
||||
CheckGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckGetCF(db, roptions, handles[1], "box", "c");
|
||||
CheckPinGetCF(db, roptions, handles[1], "baz", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "box", "c");
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
|
||||
const char* keys[3] = { "box", "box", "barfooxx" };
|
||||
@@ -1334,98 +1225,14 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
StartPhase("transactions");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// open a TransactionDB
|
||||
txn_db_options = rocksdb_transactiondb_options_create();
|
||||
txn_options = rocksdb_transaction_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
txn_db = rocksdb_transactiondb_open(options, txn_db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// put outside a transaction
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
|
||||
// delete from outside transaction
|
||||
rocksdb_transactiondb_delete(txn_db, woptions, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", NULL);
|
||||
|
||||
// begin a transaction
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
// put
|
||||
rocksdb_transaction_put(txn, "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn, roptions, "foo", "hello");
|
||||
// delete
|
||||
rocksdb_transaction_delete(txn, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn, roptions, "foo", NULL);
|
||||
|
||||
rocksdb_transaction_put(txn, "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// read from outside transaction, before commit
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", NULL);
|
||||
|
||||
// commit
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// read from outside transaction, after commit
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
|
||||
// reuse old transaction
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, txn);
|
||||
|
||||
// snapshot
|
||||
const rocksdb_snapshot_t* snapshot;
|
||||
snapshot = rocksdb_transactiondb_create_snapshot(txn_db);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snapshot);
|
||||
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
rocksdb_readoptions_set_snapshot(roptions, NULL);
|
||||
rocksdb_transactiondb_release_snapshot(txn_db, snapshot);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hey");
|
||||
|
||||
// iterate
|
||||
rocksdb_transaction_put(txn, "bar", 3, "hi", 2, &err);
|
||||
rocksdb_iterator_t* iter = rocksdb_transaction_create_iterator(txn, roptions);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "bar", "hi");
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
|
||||
// rollback
|
||||
rocksdb_transaction_rollback(txn, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "bar", NULL);
|
||||
|
||||
// close and destroy
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_transactiondb_close(txn_db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_options_destroy(txn_options);
|
||||
rocksdb_transactiondb_options_destroy(txn_db_options);
|
||||
}
|
||||
|
||||
// Simple sanity check that setting memtable rep works.
|
||||
StartPhase("memtable_reps");
|
||||
{
|
||||
// Create database with vector memtable.
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_memtable_vector_rep(options);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
+2
-6
@@ -170,12 +170,6 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.num_levels < 2) {
|
||||
result.num_levels = 2;
|
||||
}
|
||||
|
||||
if (result.compaction_style == kCompactionStyleUniversal &&
|
||||
db_options.allow_ingest_behind && result.num_levels < 3) {
|
||||
result.num_levels = 3;
|
||||
}
|
||||
|
||||
if (result.max_write_buffer_number < 2) {
|
||||
result.max_write_buffer_number = 2;
|
||||
}
|
||||
@@ -202,6 +196,7 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.num_levels = 1;
|
||||
// since we delete level0 files in FIFO compaction when there are too many
|
||||
// of them, these options don't really mean anything
|
||||
result.level0_file_num_compaction_trigger = std::numeric_limits<int>::max();
|
||||
result.level0_slowdown_writes_trigger = std::numeric_limits<int>::max();
|
||||
result.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
}
|
||||
@@ -782,6 +777,7 @@ uint64_t ColumnFamilyData::GetTotalSstFilesSize() const {
|
||||
|
||||
MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
assert(current_ != nullptr);
|
||||
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
|
||||
write_buffer_manager_, earliest_seq);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class EnvCounter : public EnvWrapper {
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<int> num_new_writable_file_;
|
||||
int num_new_writable_file_;
|
||||
};
|
||||
|
||||
class ColumnFamilyTest : public testing::Test {
|
||||
|
||||
@@ -287,6 +287,10 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
return bottommost_level_;
|
||||
}
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel &&
|
||||
output_level_ == 0) {
|
||||
return false;
|
||||
}
|
||||
// Maybe use binary search to find right entry instead of linear search?
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "table/internal_iterator.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -67,9 +66,7 @@ CompactionIterator::CompactionIterator(
|
||||
range_del_agg_(range_del_agg),
|
||||
compaction_(std::move(compaction)),
|
||||
compaction_filter_(compaction_filter),
|
||||
#ifndef ROCKSDB_LITE
|
||||
compaction_listener_(compaction_listener),
|
||||
#endif // ROCKSDB_LITE
|
||||
shutting_down_(shutting_down),
|
||||
ignore_snapshots_(false),
|
||||
merge_out_iter_(merge_helper_) {
|
||||
@@ -83,7 +80,7 @@ CompactionIterator::CompactionIterator(
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
earliest_snapshot_ = last_sequence;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = false;
|
||||
@@ -546,8 +543,7 @@ void CompactionIterator::PrepareOutput() {
|
||||
|
||||
// This is safe for TransactionDB write-conflict checking since transactions
|
||||
// only care about sequence number larger than any active snapshots.
|
||||
if ((compaction_ != nullptr && !compaction_->allow_ingest_behind()) &&
|
||||
bottommost_level_ && valid_ && ikey_.sequence <= earliest_snapshot_ &&
|
||||
if (bottommost_level_ && valid_ && ikey_.sequence < earliest_snapshot_ &&
|
||||
ikey_.type != kTypeMerge &&
|
||||
!cmp_->Equal(compaction_->GetLargestUserKey(), ikey_.user_key)) {
|
||||
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -45,9 +44,6 @@ class CompactionIterator {
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
return compaction_->GetLargestUserKey();
|
||||
}
|
||||
virtual bool allow_ingest_behind() const {
|
||||
return compaction_->immutable_cf_options()->allow_ingest_behind;
|
||||
}
|
||||
|
||||
protected:
|
||||
CompactionProxy() = default;
|
||||
@@ -130,9 +126,7 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
#ifndef ROCKSDB_LITE
|
||||
CompactionEventListener* compaction_listener_;
|
||||
#endif // ROCKSDB_LITE
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
|
||||
@@ -154,7 +154,6 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
virtual bool allow_ingest_behind() const { return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
};
|
||||
|
||||
+26
-61
@@ -36,39 +36,6 @@ uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
uint64_t max_compact_bytes_per_del_file,
|
||||
CompactionInputFiles* comp_inputs) {
|
||||
size_t compact_bytes = level_files[0]->fd.file_size;
|
||||
size_t compact_bytes_per_del_file = port::kMaxSizet;
|
||||
// compaction range will be [0, span_len).
|
||||
size_t span_len;
|
||||
// pull in files until the amount of compaction work per deleted file begins
|
||||
// increasing.
|
||||
size_t new_compact_bytes_per_del_file = 0;
|
||||
for (span_len = 1; span_len < level_files.size(); ++span_len) {
|
||||
compact_bytes += level_files[span_len]->fd.file_size;
|
||||
new_compact_bytes_per_del_file = compact_bytes / span_len;
|
||||
if (level_files[span_len]->being_compacted ||
|
||||
new_compact_bytes_per_del_file > compact_bytes_per_del_file) {
|
||||
break;
|
||||
}
|
||||
compact_bytes_per_del_file = new_compact_bytes_per_del_file;
|
||||
}
|
||||
|
||||
if (span_len >= min_files_to_compact &&
|
||||
new_compact_bytes_per_del_file < max_compact_bytes_per_del_file) {
|
||||
assert(comp_inputs != nullptr);
|
||||
comp_inputs->level = 0;
|
||||
for (size_t i = 0; i < span_len; ++i) {
|
||||
comp_inputs->files.push_back(level_files[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
// Determine compression type, based on user options, level of the output
|
||||
@@ -527,11 +494,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// files together to the last level.
|
||||
assert(vstorage->num_levels() > 1);
|
||||
// DBImpl::CompactRange() set output level to be the last level
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level == vstorage->num_levels() - 2);
|
||||
} else {
|
||||
assert(output_level == vstorage->num_levels() - 1);
|
||||
}
|
||||
assert(output_level == vstorage->num_levels() - 1);
|
||||
// DBImpl::RunManualCompaction will make full range for universal compaction
|
||||
assert(begin == nullptr);
|
||||
assert(end == nullptr);
|
||||
@@ -1383,8 +1346,31 @@ bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
// resort to L0->L0 compaction yet.
|
||||
return false;
|
||||
}
|
||||
return FindIntraL0Compaction(level_files, kMinFilesForIntraL0Compaction,
|
||||
port::kMaxUint64, &start_level_inputs_);
|
||||
|
||||
size_t compact_bytes = level_files[0]->fd.file_size;
|
||||
size_t compact_bytes_per_del_file = port::kMaxSizet;
|
||||
// compaction range will be [0, span_len).
|
||||
size_t span_len;
|
||||
// pull in files until the amount of compaction work per deleted file begins
|
||||
// increasing.
|
||||
for (span_len = 1; span_len < level_files.size(); ++span_len) {
|
||||
compact_bytes += level_files[span_len]->fd.file_size;
|
||||
size_t new_compact_bytes_per_del_file = compact_bytes / span_len;
|
||||
if (level_files[span_len]->being_compacted ||
|
||||
new_compact_bytes_per_del_file > compact_bytes_per_del_file) {
|
||||
break;
|
||||
}
|
||||
compact_bytes_per_del_file = new_compact_bytes_per_del_file;
|
||||
}
|
||||
|
||||
if (span_len >= kMinFilesForIntraL0Compaction) {
|
||||
start_level_inputs_.level = 0;
|
||||
for (size_t i = 0; i < span_len; ++i) {
|
||||
start_level_inputs_.files.push_back(level_files[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -1417,27 +1403,6 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size ||
|
||||
level_files.size() == 0) {
|
||||
// total size not exceeded
|
||||
if (ioptions_.compaction_options_fifo.allow_compaction &&
|
||||
level_files.size() > 0) {
|
||||
CompactionInputFiles comp_inputs;
|
||||
if (FindIntraL0Compaction(
|
||||
level_files,
|
||||
mutable_cf_options
|
||||
.level0_file_num_compaction_trigger /* min_files_to_compact */,
|
||||
mutable_cf_options.write_buffer_size, &comp_inputs)) {
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, {comp_inputs}, 0,
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: nothing to do. Total size %" PRIu64
|
||||
", max size %" PRIu64 "\n",
|
||||
|
||||
@@ -400,35 +400,6 @@ TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
vstorage_->CompactionScore(0) >= 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
ioptions_.allow_ingest_behind = true;
|
||||
ioptions_.num_levels = 3;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
// must return false when there's no files.
|
||||
ASSERT_EQ(universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
false);
|
||||
|
||||
NewVersionStorage(3, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", kFileSize, 0, 401, 450);
|
||||
Add(0, 4U, "260", "300", kFileSize, 0, 260, 300);
|
||||
Add(1, 5U, "100", "151", kFileSize, 0, 200, 251);
|
||||
Add(1, 3U, "301", "350", kFileSize, 0, 101, 150);
|
||||
Add(2, 6U, "120", "200", kFileSize, 0, 20, 100);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
// output level should be the one above the bottom-most
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
}
|
||||
// Tests if the files can be trivially moved in multi level
|
||||
// universal compaction when allow_trivial_move option is set
|
||||
// In this test as the input files overlaps, they cannot
|
||||
@@ -1040,8 +1011,7 @@ TEST_F(CompactionPickerTest, EstimateCompactionBytesNeededDynamicLevel) {
|
||||
|
||||
// Set Last level size 50000
|
||||
// num_levels - 1 target 5000
|
||||
// num_levels - 2 is base level with target 1000 (rounded up to
|
||||
// max_bytes_for_level_base).
|
||||
// num_levels - 2 is base level with taret 500
|
||||
Add(num_levels - 1, 10U, "400", "500", 50000);
|
||||
|
||||
Add(0, 1U, "150", "200", 200);
|
||||
@@ -1050,16 +1020,16 @@ TEST_F(CompactionPickerTest, EstimateCompactionBytesNeededDynamicLevel) {
|
||||
Add(0, 5U, "150", "200", 200);
|
||||
Add(0, 6U, "150", "200", 200);
|
||||
// num_levels - 3 is over target by 100 + 1000
|
||||
Add(num_levels - 3, 7U, "400", "500", 550);
|
||||
Add(num_levels - 3, 8U, "600", "700", 550);
|
||||
Add(num_levels - 3, 7U, "400", "500", 300);
|
||||
Add(num_levels - 3, 8U, "600", "700", 300);
|
||||
// num_levels - 2 is over target by 1100 + 200
|
||||
Add(num_levels - 2, 9U, "150", "200", 5200);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// Merging to the second last level: (5200 / 2100 + 1) * 1100
|
||||
// Merging to the second last level: (5200 / 1600 + 1) * 1100
|
||||
// Merging to the last level: (50000 / 6300 + 1) * 1300
|
||||
ASSERT_EQ(2100u + 3823u + 11617u,
|
||||
ASSERT_EQ(1600u + 4675u + 11617u,
|
||||
vstorage_->estimated_compaction_needed_bytes());
|
||||
}
|
||||
|
||||
|
||||
@@ -342,6 +342,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
assert(f->smallest_seqno <= f->largest_seqno);
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
} else {
|
||||
assert(prev_smallest_seqno > f->largest_seqno);
|
||||
}
|
||||
prev_smallest_seqno = f->smallest_seqno;
|
||||
}
|
||||
@@ -566,13 +568,6 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
output_level = sorted_runs[first_index_after].level - 1;
|
||||
}
|
||||
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
(output_level == vstorage->num_levels() - 1)) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage->num_levels());
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
@@ -724,20 +719,13 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
cf_name.c_str(), file_num_buf);
|
||||
}
|
||||
|
||||
// output files at the bottom most level, unless it's reserved
|
||||
int output_level = vstorage->num_levels() - 1;
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
vstorage->num_levels() - 1,
|
||||
mutable_cf_options.MaxFileSizeForLevel(vstorage->num_levels() - 1),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
vstorage->num_levels() - 1, 1),
|
||||
/* grandparents */ {}, /* is manual */ false, score,
|
||||
false /* deletion_compaction */,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
|
||||
+2
-2
@@ -13,12 +13,12 @@
|
||||
namespace rocksdb {
|
||||
|
||||
void CancelAllBackgroundWork(DB* db, bool wait) {
|
||||
(dynamic_cast<DBImpl*>(db->GetRootDB()))->CancelAllBackgroundWork(wait);
|
||||
(dynamic_cast<DBImpl*>(db))->CancelAllBackgroundWork(wait);
|
||||
}
|
||||
|
||||
Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
return (dynamic_cast<DBImpl*>(db->GetRootDB()))
|
||||
return (dynamic_cast<DBImpl*>(db))
|
||||
->DeleteFilesInRange(column_family, begin, end);
|
||||
}
|
||||
|
||||
|
||||
@@ -320,14 +320,8 @@ TEST_F(CuckooTableDBTest, AdaptiveTable) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (rocksdb::port::kLittleEndian) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "SKIPPED as Cuckoo table doesn't support Big Endian\n");
|
||||
return 0;
|
||||
}
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -796,19 +796,36 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTestWithIter) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value1, iter->value().ToString());
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_hit_count);
|
||||
if (partition_filters_) {
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_hit_count); // no_io
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count); // no_io
|
||||
} else {
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_hit_count);
|
||||
}
|
||||
|
||||
iter->Seek(key3);
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value3, iter->value().ToString());
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
if (partition_filters_) {
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_hit_count); // no_io
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count); // no_io
|
||||
} else {
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
}
|
||||
|
||||
iter->Seek(key2);
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_miss_count);
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
if (partition_filters_) {
|
||||
// iter is still valid since filter did not reject the key2
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_hit_count); // no_io
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count); // no_io
|
||||
} else {
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_miss_count);
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(BloomStatsTestWithParam, BloomStatsTestWithParam,
|
||||
|
||||
@@ -626,7 +626,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(total, 700);
|
||||
ASSERT_EQ(count, 1);
|
||||
ASSERT_EQ(count, 2);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+66
-10
@@ -1193,8 +1193,7 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
}
|
||||
}
|
||||
|
||||
// Disable as the test is flaky.
|
||||
TEST_F(DBCompactionTest, DISABLED_ManualPartialFill) {
|
||||
TEST_F(DBCompactionTest, ManualPartialFill) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -2604,13 +2603,11 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
// Files 6-9 are the longest span of available files for which
|
||||
// work-per-deleted-file decreases (see "score" row above).
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(0), "")); // prevents trivial move
|
||||
if (i == 5) {
|
||||
ASSERT_OK(Put(Key(i + 1), value + value));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
}
|
||||
ASSERT_OK(Put(Key(0), "")); // prevents trivial move
|
||||
if (i == 5) {
|
||||
ASSERT_OK(Put(Key(i + 1), value + value));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2625,10 +2622,69 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
ASSERT_EQ(2, level_to_files[0].size());
|
||||
ASSERT_GT(level_to_files[1].size(), 0);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_GE(level_to_files[0][0].fd.file_size, 1 << 21);
|
||||
ASSERT_GE(level_to_files[0][i].fd.file_size, 1 << 21);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
|
||||
// regression test for issue #2722: L0->L0 compaction can resurrect deleted
|
||||
// keys from older L0 files if L1+ files' key-ranges do not include the key.
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = 5;
|
||||
options.max_background_compactions = 2;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kValueSize = 1 << 20;
|
||||
Random rnd(301);
|
||||
std::string value(RandomString(&rnd, kValueSize));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"LevelCompactionPicker::PickCompactionBySize:0",
|
||||
"CompactionJob::Run():Start"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// index: 0 1 2 3 4 5 6 7 8 9
|
||||
// size: 1MB 1MB 1MB 1MB 1MB 1MB 1MB 1MB 1MB 1MB
|
||||
// score: 1.25 1.33 1.5 2.0 inf
|
||||
//
|
||||
// Files 0-4 will be included in an L0->L1 compaction.
|
||||
//
|
||||
// L0->L0 will be triggered since the sync points guarantee compaction to base
|
||||
// level is still blocked when files 5-9 trigger another compaction. All files
|
||||
// 5-9 are included in the L0->L0 due to work-per-deleted file decreasing.
|
||||
//
|
||||
// Put a key-value in files 0-4. Delete that key in files 5-9. Verify the
|
||||
// L0->L0 preserves the deletion such that the key remains deleted.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// key 0 serves both to prevent trivial move and as the key we want to
|
||||
// verify is not resurrected by L0->L0 compaction.
|
||||
if (i < 5) {
|
||||
ASSERT_OK(Put(Key(0), ""));
|
||||
} else {
|
||||
ASSERT_OK(Delete(Key(0)));
|
||||
}
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
ASSERT_GE(level_to_files.size(), 2); // at least L0 and L1
|
||||
// L0 has a single output file from L0->L0
|
||||
ASSERT_EQ(1, level_to_files[0].size());
|
||||
ASSERT_GT(level_to_files[1].size(), 0);
|
||||
ASSERT_GE(level_to_files[0][0].fd.file_size, 1 << 22);
|
||||
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
ASSERT_TRUE(db_->Get(roptions, Key(0), &result).IsNotFound());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
|
||||
+8
-18
@@ -157,7 +157,10 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
max_total_in_memory_state_(0),
|
||||
is_snapshot_supported_(true),
|
||||
write_buffer_manager_(immutable_db_options_.write_buffer_manager.get()),
|
||||
write_thread_(immutable_db_options_),
|
||||
write_thread_(immutable_db_options_.enable_write_thread_adaptive_yield
|
||||
? immutable_db_options_.write_thread_max_yield_usec
|
||||
: 0,
|
||||
immutable_db_options_.write_thread_slow_yield_usec),
|
||||
write_controller_(mutable_db_options_.delayed_write_rate),
|
||||
last_batch_group_size_(0),
|
||||
unscheduled_flushes_(0),
|
||||
@@ -187,9 +190,9 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
|
||||
// Reserve ten files or so for other uses and give the rest to TableCache.
|
||||
// Give a large number for setting of "infinite" open files.
|
||||
const int table_cache_size = (mutable_db_options_.max_open_files == -1)
|
||||
? TableCache::kInfiniteCapacity
|
||||
: mutable_db_options_.max_open_files - 10;
|
||||
const int table_cache_size = (immutable_db_options_.max_open_files == -1)
|
||||
? 4194304
|
||||
: immutable_db_options_.max_open_files - 10;
|
||||
table_cache_ = NewLRUCache(table_cache_size,
|
||||
immutable_db_options_.table_cache_numshardbits);
|
||||
|
||||
@@ -568,9 +571,6 @@ Status DBImpl::SetDBOptions(
|
||||
}
|
||||
|
||||
write_controller_.set_max_delayed_write_rate(new_options.delayed_write_rate);
|
||||
table_cache_.get()->SetCapacity(new_options.max_open_files == -1
|
||||
? TableCache::kInfiniteCapacity
|
||||
: new_options.max_open_files - 10);
|
||||
|
||||
mutable_db_options_ = new_options;
|
||||
|
||||
@@ -2609,15 +2609,6 @@ Status DBImpl::IngestExternalFile(
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
// Ingest should immediately fail if ingest_behind is requested,
|
||||
// but the DB doesn't support it.
|
||||
if (ingestion_options.ingest_behind) {
|
||||
if (!immutable_db_options_.allow_ingest_behind) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
}
|
||||
}
|
||||
|
||||
ExternalSstFileIngestionJob ingestion_job(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
&snapshots_, ingestion_options);
|
||||
@@ -2661,8 +2652,7 @@ Status DBImpl::IngestExternalFile(
|
||||
if (status.ok()) {
|
||||
bool need_flush = false;
|
||||
status = ingestion_job.NeedsFlush(&need_flush);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IngestExternalFile:NeedFlush",
|
||||
&need_flush);
|
||||
|
||||
if (status.ok() && need_flush) {
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), true /* writes_stopped */);
|
||||
|
||||
+7
-18
@@ -387,9 +387,9 @@ class DBImpl : public DB {
|
||||
Status TEST_GetAllImmutableCFOptions(
|
||||
std::unordered_map<std::string, const ImmutableCFOptions*>* iopts_map);
|
||||
|
||||
// Return the lastest MutableCFOptions of a column family
|
||||
// Return the lastest MutableCFOptions of of a column family
|
||||
Status TEST_GetLatestMutableCFOptions(ColumnFamilyHandle* column_family,
|
||||
MutableCFOptions* mutable_cf_options);
|
||||
MutableCFOptions* mutable_cf_opitons);
|
||||
|
||||
Cache* TEST_table_cache() { return table_cache_.get(); }
|
||||
|
||||
@@ -605,11 +605,6 @@ class DBImpl : public DB {
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false);
|
||||
|
||||
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false);
|
||||
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
uint64_t FindMinPrepLogReferencedByMemTable();
|
||||
|
||||
@@ -729,19 +724,13 @@ class DBImpl : public DB {
|
||||
Status HandleWriteBufferFull(WriteContext* write_context);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status PreprocessWrite(const WriteOptions& write_options, bool* need_log_sync,
|
||||
WriteContext* write_context);
|
||||
Status PreprocessWrite(const WriteOptions& write_options, bool need_log_sync,
|
||||
bool* logs_getting_syned, WriteContext* write_context);
|
||||
|
||||
Status WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
Status WriteToWAL(const autovector<WriteThread::Writer*>& write_group,
|
||||
log::Writer* log_writer, bool need_log_sync,
|
||||
bool need_log_dir_sync, SequenceNumber sequence);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void ParanoidCheck(const Status& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status CompactFilesImpl(const CompactionOptions& compact_options,
|
||||
@@ -1103,7 +1092,7 @@ class DBImpl : public DB {
|
||||
// Indicate DB was opened successfully
|
||||
bool opened_successfully_;
|
||||
|
||||
// minimum log number still containing prepared data.
|
||||
// minmum log number still containing prepared data.
|
||||
// this is used by FindObsoleteFiles to determine which
|
||||
// flushed logs we must keep around because they still
|
||||
// contain prepared data which has not been flushed or rolled back
|
||||
@@ -1116,7 +1105,7 @@ class DBImpl : public DB {
|
||||
// to prepared_section_completed_ which maps LOG -> instance_count
|
||||
// since a log could contain multiple prepared sections
|
||||
//
|
||||
// when trying to determine the minimum log still active we first
|
||||
// when trying to determine the minmum log still active we first
|
||||
// consult min_log_with_prep_. while that root value maps to
|
||||
// a value > 0 in prepared_section_completed_ we decrement the
|
||||
// instance_count for that log and pop the root value in
|
||||
|
||||
@@ -282,14 +282,10 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->NumberLevels() > 1) {
|
||||
// Always compact all files together.
|
||||
final_output_level = cfd->NumberLevels() - 1;
|
||||
// if bottom most level is reserved
|
||||
if (immutable_db_options_.allow_ingest_behind) {
|
||||
final_output_level--;
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options.target_path_id,
|
||||
cfd->NumberLevels() - 1, options.target_path_id,
|
||||
begin, end, exclusive);
|
||||
final_output_level = cfd->NumberLevels() - 1;
|
||||
} else {
|
||||
for (int level = 0; level <= max_level_with_files; level++) {
|
||||
int output_level;
|
||||
@@ -1159,8 +1155,8 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
"Calling FlushMemTableToOutputFile with column "
|
||||
"family [%s], flush slots available %d, compaction slots allowed %d, "
|
||||
"compaction slots scheduled %d",
|
||||
cfd->GetName().c_str(), immutable_db_options_.max_background_flushes -
|
||||
bg_flush_scheduled_, BGCompactionsAllowed(), bg_compaction_scheduled_);
|
||||
cfd->GetName().c_str(), immutable_db_options_.max_background_flushes,
|
||||
bg_flush_scheduled_, BGCompactionsAllowed() - bg_compaction_scheduled_);
|
||||
status = FlushMemTableToOutputFile(cfd, mutable_cf_options, made_progress,
|
||||
job_context, log_buffer);
|
||||
if (cfd->Unref()) {
|
||||
@@ -1401,7 +1397,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// Can't compact right now, but try again later
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction()::Conflict");
|
||||
|
||||
// Stay in the compaction queue.
|
||||
// Stay in the compaciton queue.
|
||||
unscheduled_compactions_++;
|
||||
|
||||
return Status::OK();
|
||||
|
||||
+2
-3
@@ -36,7 +36,7 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
if (result.max_open_files != -1) {
|
||||
int max_max_open_files = port::GetMaxOpenFiles();
|
||||
if (max_max_open_files == -1) {
|
||||
max_max_open_files = 0x400000;
|
||||
max_max_open_files = 1000000;
|
||||
}
|
||||
ClipToRange(&result.max_open_files, 20, max_max_open_files);
|
||||
}
|
||||
@@ -498,8 +498,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
unique_ptr<SequentialFileReader> file_reader;
|
||||
{
|
||||
unique_ptr<SequentialFile> file;
|
||||
status = env_->NewSequentialFile(fname, &file,
|
||||
env_->OptimizeForLogRead(env_options_));
|
||||
status = env_->NewSequentialFile(fname, &file, env_options_);
|
||||
if (!status.ok()) {
|
||||
MaybeIgnoreError(&status);
|
||||
if (!status.ok()) {
|
||||
|
||||
+86
-223
@@ -64,11 +64,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
return PipelinedWriteImpl(write_options, my_batch, callback, log_used,
|
||||
log_ref, disable_memtable);
|
||||
}
|
||||
|
||||
Status status;
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
@@ -82,7 +77,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
if (w.state == WriteThread::STATE_PARALLEL_FOLLOWER) {
|
||||
// we are a non-leader in a parallel group
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
@@ -96,12 +91,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
true /*concurrent_memtable_writes*/);
|
||||
}
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
// we're responsible for exit batch group
|
||||
auto last_sequence = w.write_group->last_sequence;
|
||||
if (write_thread_.CompleteParallelWorker(&w)) {
|
||||
// we're responsible for early exit
|
||||
auto last_sequence = w.parallel_group->last_sequence;
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
write_thread_.ExitAsBatchGroupFollower(&w);
|
||||
write_thread_.EarlyExitParallelGroup(&w);
|
||||
}
|
||||
assert(w.state == WriteThread::STATE_COMPLETED);
|
||||
// STATE_COMPLETED conditional below handles exit
|
||||
@@ -123,15 +117,16 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// when it finds suitable, and finish them in the same write batch.
|
||||
// This is how a write job could be done by the other writer.
|
||||
WriteContext write_context;
|
||||
WriteThread::WriteGroup write_group;
|
||||
bool in_parallel_group = false;
|
||||
uint64_t last_sequence = versions_->LastSequence();
|
||||
WriteThread::Writer* last_writer = &w; // Dummy intial value
|
||||
autovector<WriteThread::Writer*> write_group;
|
||||
bool logs_getting_synced = false;
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
bool need_log_sync = !write_options.disableWAL && write_options.sync;
|
||||
bool need_log_dir_sync = need_log_sync && !log_dir_synced_;
|
||||
status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
status = PreprocessWrite(write_options, need_log_sync, &logs_getting_synced,
|
||||
&write_context);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
|
||||
mutex_.Unlock();
|
||||
@@ -141,8 +136,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// and protects against concurrent loggers and concurrent writes
|
||||
// into memtables
|
||||
|
||||
bool exit_completed_early = false;
|
||||
last_batch_group_size_ =
|
||||
write_thread_.EnterAsBatchGroupLeader(&w, &write_group);
|
||||
write_thread_.EnterAsBatchGroupLeader(&w, &last_writer, &write_group);
|
||||
|
||||
if (status.ok()) {
|
||||
// Rules for when we can update the memtable concurrently
|
||||
@@ -157,28 +153,32 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// relax rules 2 if we could prevent write batches from referring
|
||||
// more than once to a particular key.
|
||||
bool parallel = immutable_db_options_.allow_concurrent_memtable_write &&
|
||||
write_group.size > 1;
|
||||
write_group.size() > 1;
|
||||
int total_count = 0;
|
||||
uint64_t total_byte_size = 0;
|
||||
for (auto* writer : write_group) {
|
||||
for (auto writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
total_count += WriteBatchInternal::Count(writer->batch);
|
||||
parallel = parallel && !writer->batch->HasMerge();
|
||||
}
|
||||
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
if (writer->ShouldWriteToWAL()) {
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t last_sequence = versions_->LastSequence();
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += total_count;
|
||||
|
||||
// Update stats while we are an exclusive group leader, so we know
|
||||
// that nobody else can be writing to these particular stats.
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
// commit. That lets us release our leader status early in
|
||||
// some cases.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
@@ -186,7 +186,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
auto write_done_by_other = write_group.size() - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER,
|
||||
write_done_by_other);
|
||||
@@ -213,23 +213,26 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
if (!parallel) {
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
write_group, current_sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*recovery_log_number*/, this);
|
||||
} else {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
writer->sequence = next_sequence;
|
||||
next_sequence += WriteBatchInternal::Count(writer->batch);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
// There were no write failures. Set leader's status
|
||||
// in case the write callback returned a non-ok status.
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
write_group.last_sequence = last_sequence;
|
||||
write_group.running.store(static_cast<uint32_t>(write_group.size),
|
||||
std::memory_order_relaxed);
|
||||
write_thread_.LaunchParallelMemTableWriters(&write_group);
|
||||
in_parallel_group = true;
|
||||
|
||||
} else {
|
||||
WriteThread::ParallelGroup pg;
|
||||
pg.leader = &w;
|
||||
pg.last_writer = last_writer;
|
||||
pg.last_sequence = last_sequence;
|
||||
pg.early_exit_allowed = !need_log_sync;
|
||||
pg.running.store(static_cast<uint32_t>(write_group.size()),
|
||||
std::memory_order_relaxed);
|
||||
write_thread_.LaunchParallelFollowers(&pg, current_sequence);
|
||||
|
||||
// Each parallel follower is doing each own writes. The leader should
|
||||
// also do its own.
|
||||
@@ -243,199 +246,67 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/,
|
||||
this, true /*concurrent_memtable_writes*/);
|
||||
}
|
||||
|
||||
// CompleteParallelWorker returns true if this thread should
|
||||
// handle exit, false means somebody else did
|
||||
exit_completed_early = !write_thread_.CompleteParallelWorker(&w);
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
|
||||
if (!exit_completed_early && w.status.ok()) {
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
if (!need_log_sync) {
|
||||
write_thread_.ExitAsBatchGroupLeader(&w, last_writer, w.status);
|
||||
exit_completed_early = true;
|
||||
}
|
||||
}
|
||||
|
||||
// A non-OK status here indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be
|
||||
// because of a corrupt write_batch (very bad), or because the
|
||||
// client specified an invalid column family and didn't specify
|
||||
// ignore_missing_column_families.
|
||||
//
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if (!status.ok() && !w.CallbackFailed()) {
|
||||
mutex_.Lock();
|
||||
if (bg_error_.ok()) {
|
||||
bg_error_ = status; // stop compaction & fail any further writes
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
ParanoidCheck(status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
bool should_exit_batch_group = true;
|
||||
if (in_parallel_group) {
|
||||
// CompleteParallelWorker returns true if this thread should
|
||||
// handle exit, false means somebody else did
|
||||
should_exit_batch_group = write_thread_.CompleteParallelMemTableWriter(&w);
|
||||
}
|
||||
if (should_exit_batch_group) {
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
write_thread_.ExitAsBatchGroupLeader(write_group, w.status);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
WriteContext write_context;
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable);
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_GROUP_LEADER) {
|
||||
WriteThread::WriteGroup wal_write_group;
|
||||
if (w.callback && !w.callback->AllowWriteBatching()) {
|
||||
write_thread_.WaitForMemTableWriters();
|
||||
}
|
||||
mutex_.Lock();
|
||||
bool need_log_sync = !write_options.disableWAL && write_options.sync;
|
||||
bool need_log_dir_sync = need_log_sync && !log_dir_synced_;
|
||||
w.status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
mutex_.Unlock();
|
||||
|
||||
// This can set non-OK status if callback fail.
|
||||
last_batch_group_size_ =
|
||||
write_thread_.EnterAsBatchGroupLeader(&w, &wal_write_group);
|
||||
const SequenceNumber current_sequence =
|
||||
write_thread_.UpdateLastSequence(versions_->LastSequence()) + 1;
|
||||
size_t total_count = 0;
|
||||
size_t total_byte_size = 0;
|
||||
|
||||
if (w.status.ok()) {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
for (auto writer : wal_write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
writer->sequence = next_sequence;
|
||||
size_t count = WriteBatchInternal::Count(writer->batch);
|
||||
next_sequence += count;
|
||||
total_count += count;
|
||||
}
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
}
|
||||
}
|
||||
if (w.disable_wal) {
|
||||
has_unpersisted_data_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
write_thread_.UpdateLastSequence(current_sequence + total_count - 1);
|
||||
}
|
||||
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
if (w.ShouldWriteToWAL()) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF, 1);
|
||||
if (wal_write_group.size > 1) {
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
w.status = WriteToWAL(wal_write_group, cur_log_writer, need_log_sync,
|
||||
need_log_dir_sync, current_sequence);
|
||||
}
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
ParanoidCheck(w.status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, w.status);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
write_thread_.ExitAsBatchGroupLeader(wal_write_group, w.status);
|
||||
}
|
||||
|
||||
WriteThread::WriteGroup memtable_write_group;
|
||||
if (w.state == WriteThread::STATE_MEMTABLE_WRITER_LEADER) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
assert(w.status.ok());
|
||||
write_thread_.EnterAsMemTableWriter(&w, &memtable_write_group);
|
||||
if (memtable_write_group.size > 1 &&
|
||||
immutable_db_options_.allow_concurrent_memtable_write) {
|
||||
write_thread_.LaunchParallelMemTableWriters(&memtable_write_group);
|
||||
} else {
|
||||
memtable_write_group.status = WriteBatchInternal::InsertInto(
|
||||
memtable_write_group, w.sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*log_number*/, this);
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
write_thread_.ExitAsMemTableWriter(&w, memtable_write_group);
|
||||
}
|
||||
}
|
||||
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
assert(w.ShouldWriteToMemtable());
|
||||
WriteBatchInternal::SetSequence(w.batch, w.sequence);
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/);
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
versions_->SetLastSequence(w.write_group->last_sequence);
|
||||
write_thread_.ExitAsMemTableWriter(&w, *w.write_group);
|
||||
}
|
||||
}
|
||||
|
||||
assert(w.state == WriteThread::STATE_COMPLETED);
|
||||
if (log_used != nullptr) {
|
||||
*log_used = w.log_used;
|
||||
}
|
||||
|
||||
return w.FinalStatus();
|
||||
}
|
||||
|
||||
void DBImpl::ParanoidCheck(const Status& status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if (immutable_db_options_.paranoid_checks && !status.ok() &&
|
||||
!status.IsBusy() && !status.IsIncomplete()) {
|
||||
!w.CallbackFailed() && !status.IsBusy() && !status.IsIncomplete()) {
|
||||
mutex_.Lock();
|
||||
if (bg_error_.ok()) {
|
||||
bg_error_ = status; // stop compaction & fail any further writes
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
// A non-OK status here indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be
|
||||
// because of a corrupt write_batch (very bad), or because the
|
||||
// client specified an invalid column family and didn't specify
|
||||
// ignore_missing_column_families.
|
||||
if (!status.ok()) {
|
||||
if (logs_getting_synced) {
|
||||
mutex_.Lock();
|
||||
assert(bg_error_.ok());
|
||||
bg_error_ = status;
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
if (!exit_completed_early) {
|
||||
write_thread_.ExitAsBatchGroupLeader(&w, last_writer, w.status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
bool* need_log_sync,
|
||||
bool need_log_sync, bool* logs_getting_synced,
|
||||
WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr && need_log_sync != nullptr);
|
||||
assert(write_context != nullptr && logs_getting_synced != nullptr);
|
||||
Status status;
|
||||
|
||||
assert(!single_column_family_mode_ ||
|
||||
@@ -455,7 +326,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !bg_error_.ok())) {
|
||||
return bg_error_;
|
||||
status = bg_error_;
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !flush_scheduler_.Empty())) {
|
||||
@@ -472,7 +343,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
status = DelayWrite(last_batch_group_size_, write_options);
|
||||
}
|
||||
|
||||
if (status.ok() && *need_log_sync) {
|
||||
if (status.ok() && need_log_sync) {
|
||||
// Wait until the parallel syncs are finished. Any sync process has to sync
|
||||
// the front log too so it is enough to check the status of front()
|
||||
// We do a while loop since log_sync_cv_ is signalled when any sync is
|
||||
@@ -492,28 +363,26 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
// actually write to the WAL
|
||||
log.getting_synced = true;
|
||||
}
|
||||
} else {
|
||||
*need_log_sync = false;
|
||||
*logs_getting_synced = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
Status DBImpl::WriteToWAL(const autovector<WriteThread::Writer*>& write_group,
|
||||
log::Writer* log_writer, bool need_log_sync,
|
||||
bool need_log_dir_sync, SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
WriteBatch* merged_batch = nullptr;
|
||||
size_t write_with_wal = 0;
|
||||
auto* leader = write_group.leader;
|
||||
if (write_group.size == 1 && leader->ShouldWriteToWAL() &&
|
||||
leader->batch->GetWalTerminationPoint().is_cleared()) {
|
||||
if (write_group.size() == 1 && write_group[0]->ShouldWriteToWAL() &&
|
||||
write_group[0]->batch->GetWalTerminationPoint().is_cleared()) {
|
||||
// we simply write the first WriteBatch to WAL if the group only
|
||||
// contains one batch, that batch should be written to the WAL,
|
||||
// and the batch is not wanting to be truncated
|
||||
merged_batch = leader->batch;
|
||||
leader->log_used = logfile_number_;
|
||||
merged_batch = write_group[0]->batch;
|
||||
write_group[0]->log_used = logfile_number_;
|
||||
write_with_wal = 1;
|
||||
} else {
|
||||
// WAL needs all of the batches flattened into a single batch.
|
||||
@@ -781,12 +650,6 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log::Writer* new_log = nullptr;
|
||||
MemTable* new_mem = nullptr;
|
||||
|
||||
// In case of pipelined write is enabled, wait for all pending memtable
|
||||
// writers.
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
write_thread_.WaitForMemTableWriters();
|
||||
}
|
||||
|
||||
// Attempt to switch to a new memtable and trigger flush of old.
|
||||
// Do this without holding the dbmutex lock.
|
||||
assert(versions_->prev_log_number() == 0);
|
||||
|
||||
@@ -1005,7 +1005,6 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
int upper_bound_hits = 0;
|
||||
Options options = CurrentOptions();
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::BlockEntryIteratorState::KeyReachedUpperBound",
|
||||
[&upper_bound_hits](void* arg) {
|
||||
@@ -1013,6 +1012,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
upper_bound_hits += (*static_cast<bool*>(arg) ? 1 : 0);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor = nullptr;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -384,24 +383,6 @@ TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, MaxOpenFilesChange) {
|
||||
SpecialEnv env(env_);
|
||||
Options options;
|
||||
options.max_open_files = -1;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
Cache* tc = dbfull()->TEST_table_cache();
|
||||
|
||||
ASSERT_EQ(-1, dbfull()->GetDBOptions().max_open_files);
|
||||
ASSERT_LT(2000, tc->GetCapacity());
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"max_open_files", "1024"}}));
|
||||
ASSERT_EQ(1024, dbfull()->GetDBOptions().max_open_files);
|
||||
// examine the table cache (actual size should be 1014)
|
||||
ASSERT_GT(1500, tc->GetCapacity());
|
||||
Close();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+1
-1
@@ -297,7 +297,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
std::vector<uint64_t> penalties;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::BackgroundEmptyTrash:Wait",
|
||||
[&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
|
||||
[&](void* arg) { penalties.push_back(*(static_cast<int*>(arg))); });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
// Turn timed wait into a simulated sleep
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
+1
-44
@@ -2765,56 +2765,13 @@ TEST_P(DBTestWithParam, FIFOCompactionTest) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, FIFOCompactionTestWithCompaction) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.write_buffer_size = 20 << 10; // 20K
|
||||
options.arena_block_size = 4096;
|
||||
options.compaction_options_fifo.max_table_files_size = 1500 << 10; // 1MB
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 20KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
// It should be compacted to 10 files.
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j + 2000), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
// It should be compacted to no more than 20 files.
|
||||
ASSERT_GT(NumTableFilesAtLevel(0), 10);
|
||||
ASSERT_LT(NumTableFilesAtLevel(0), 18);
|
||||
// Size limit is still guaranteed.
|
||||
ASSERT_LE(SizeAtLevel(0),
|
||||
options.compaction_options_fifo.max_table_files_size);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
/*
|
||||
* This test is not reliable enough as it heavily depends on disk behavior.
|
||||
* Disable as it is flaky.
|
||||
*/
|
||||
TEST_F(DBTest, DISABLED_RateLimitingTest) {
|
||||
TEST_F(DBTest, RateLimitingTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 1 << 20; // 1MB
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
|
||||
@@ -1516,7 +1516,6 @@ class MockPersistentCache : public PersistentCache {
|
||||
const size_t max_size_ = 10 * 1024; // 10KiB
|
||||
};
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
TEST_F(DBTest2, PersistentCache) {
|
||||
int num_iter = 80;
|
||||
|
||||
@@ -1580,7 +1579,6 @@ TEST_F(DBTest2, PersistentCache) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !OS_SOLARIS
|
||||
|
||||
namespace {
|
||||
void CountSyncPoint() {
|
||||
@@ -1718,7 +1716,6 @@ TEST_F(DBTest2, ReadAmpBitmap) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
if (dbname_.find("dev/shm") != std::string::npos) {
|
||||
// /dev/shm dont support getting a unique file id, this mean that
|
||||
@@ -1814,7 +1811,6 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !OS_SOLARIS
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, AutomaticCompactionOverlapManualCompaction) {
|
||||
|
||||
@@ -265,14 +265,6 @@ Options DBTestBase::CurrentOptions(
|
||||
Options options = defaultOptions;
|
||||
BlockBasedTableOptions table_options;
|
||||
bool set_block_based_table_factory = true;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && \
|
||||
!defined(OS_AIX)
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"NewRandomAccessFile:O_DIRECT");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"NewWritableFile:O_DIRECT");
|
||||
#endif
|
||||
|
||||
switch (option_config_) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
case kHashSkipList:
|
||||
@@ -437,26 +429,6 @@ Options DBTestBase::CurrentOptions(
|
||||
options.enable_write_thread_adaptive_yield = true;
|
||||
break;
|
||||
}
|
||||
case kDirectIO: {
|
||||
options.use_direct_reads = true;
|
||||
options.use_direct_io_for_flush_and_compaction = true;
|
||||
options.compaction_readahead_size = 2 * 1024 * 1024;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && \
|
||||
!defined(OS_AIX)
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile:O_DIRECT", [&](void* arg) {
|
||||
int* val = static_cast<int*>(arg);
|
||||
*val &= ~O_DIRECT;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewRandomAccessFile:O_DIRECT", [&](void* arg) {
|
||||
int* val = static_cast<int*>(arg);
|
||||
*val &= ~O_DIRECT;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
+6
-7
@@ -620,13 +620,12 @@ class DBTestBase : public testing::Test {
|
||||
kRowCache = 27,
|
||||
kRecycleLogFiles = 28,
|
||||
kConcurrentSkipList = 29,
|
||||
kDirectIO = 30,
|
||||
kEnd = 31,
|
||||
kLevelSubcompactions = 32,
|
||||
kUniversalSubcompactions = 33,
|
||||
kBlockBasedTableWithIndexRestartInterval = 34,
|
||||
kBlockBasedTableWithPartitionedIndex = 35,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions = 36,
|
||||
kEnd = 30,
|
||||
kLevelSubcompactions = 31,
|
||||
kUniversalSubcompactions = 32,
|
||||
kBlockBasedTableWithIndexRestartInterval = 33,
|
||||
kBlockBasedTableWithPartitionedIndex = 34,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions = 35,
|
||||
};
|
||||
int option_config_;
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ class DelayFilterFactory : public CompactionFilterFactory {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Make sure we don't trigger a problem if the trigger condtion is given
|
||||
// Make sure we don't trigger a problem if the trigger conditon is given
|
||||
// to be 0, which is invalid.
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionSingleSortedRun) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -182,94 +182,92 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
std::map<std::string, std::string> true_data;
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
int file_id = 1;
|
||||
int file_id = 1;
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2, 3, 4, 5, 6}, file_id++,
|
||||
&true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2, 3, 4, 5, 6}, file_id++,
|
||||
&true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {10, 11, 12, 13}, file_id++,
|
||||
&true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {10, 11, 12, 13}, file_id++,
|
||||
&true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {1, 4, 6}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {1, 4, 6}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {11, 15, 19}, file_id++,
|
||||
&true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {11, 15, 19}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {120, 130}, file_id++, &true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {120, 130}, file_id++, &true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {1, 130}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {1, 130}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
// Write some keys through normal write path
|
||||
for (int i = 0; i < 50; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
true_data[Key(i)] = "memtable";
|
||||
}
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
// Write some keys through normal write path
|
||||
for (int i = 0; i < 50; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
true_data[Key(i)] = "memtable";
|
||||
}
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {60, 61, 62}, file_id++,
|
||||
&true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {60, 61, 62}, file_id++, &true_data));
|
||||
// File dont overwrite any keys, No seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {40, 41, 42}, file_id++,
|
||||
&true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {40, 41, 42}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {20, 30, 40}, file_id++,
|
||||
&true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {20, 30, 40}, file_id++, &true_data));
|
||||
// File overwrite some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// We will need a seqno for the file regardless if the file overwrite
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1002}, file_id++,
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
// We will need a seqno for the file regardless if the file overwrite
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {1000, 1002}, file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2000, 3002}, file_id++,
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {2000, 3002}, file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 20, 40, 100, 150},
|
||||
file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 20, 40, 100, 150},
|
||||
file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {5000, 5001}, file_id++,
|
||||
&true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
ASSERT_OK(
|
||||
GenerateAndAddExternalFile(options, {5000, 5001}, file_id++, &true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
} while (ChangeCompactOptions());
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
@@ -156,38 +154,34 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
|
||||
bool consumed_seqno = false;
|
||||
bool force_global_seqno = false;
|
||||
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
if (ingestion_options_.snapshot_consistency && !db_snapshots_->empty()) {
|
||||
// We need to assign a global sequence number to all the files even
|
||||
// if the dont overlap with any ranges since we have snapshots
|
||||
force_global_seqno = true;
|
||||
}
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
edit_.SetColumnFamily(cfd_->GetID());
|
||||
// The levels that the files will be ingested into
|
||||
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
SequenceNumber assigned_seqno = 0;
|
||||
if (ingestion_options_.ingest_behind) {
|
||||
status = CheckLevelForIngestedBehindFile(&f);
|
||||
} else {
|
||||
status = AssignLevelAndSeqnoForIngestedFile(
|
||||
super_version, force_global_seqno, cfd_->ioptions()->compaction_style,
|
||||
&f, &assigned_seqno);
|
||||
}
|
||||
bool overlap_with_db = false;
|
||||
status = AssignLevelForIngestedFile(super_version, &f, &overlap_with_db);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, assigned_seqno);
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
|
||||
&assigned_seqno);
|
||||
if (assigned_seqno == last_seqno + 1) {
|
||||
|
||||
if (overlap_with_db || force_global_seqno) {
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, last_seqno + 1);
|
||||
consumed_seqno = true;
|
||||
} else {
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, 0);
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
edit_.AddFile(f.picked_level, f.fd.GetNumber(), f.fd.GetPathId(),
|
||||
f.fd.GetFileSize(), f.smallest_internal_key(),
|
||||
f.largest_internal_key(), f.assigned_seqno, f.assigned_seqno,
|
||||
@@ -392,27 +386,17 @@ Status ExternalSstFileIngestionJob::IngestedFilesOverlapWithMemtables(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
SuperVersion* sv, bool force_global_seqno, CompactionStyle compaction_style,
|
||||
IngestedFileInfo* file_to_ingest, SequenceNumber* assigned_seqno) {
|
||||
Status status;
|
||||
*assigned_seqno = 0;
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
if (force_global_seqno) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
if (compaction_style == kCompactionStyleUniversal) {
|
||||
file_to_ingest->picked_level = 0;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
Status ExternalSstFileIngestionJob::AssignLevelForIngestedFile(
|
||||
SuperVersion* sv, IngestedFileInfo* file_to_ingest, bool* overlap_with_db) {
|
||||
*overlap_with_db = false;
|
||||
|
||||
bool overlap_with_db = false;
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
|
||||
Status status;
|
||||
int target_level = 0;
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
|
||||
for (int lvl = 0; lvl < cfd_->NumberLevels(); lvl++) {
|
||||
if (lvl > 0 && lvl < vstorage->base_level()) {
|
||||
continue;
|
||||
@@ -420,81 +404,44 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
|
||||
if (vstorage->NumLevelFiles(lvl) > 0) {
|
||||
bool overlap_with_level = false;
|
||||
status = IngestedFileOverlapWithLevel(sv, file_to_ingest, lvl,
|
||||
&overlap_with_level);
|
||||
MergeIteratorBuilder merge_iter_builder(&cfd_->internal_comparator(),
|
||||
&arena);
|
||||
RangeDelAggregator range_del_agg(cfd_->internal_comparator(),
|
||||
{} /* snapshots */);
|
||||
sv->current->AddIteratorsForLevel(ro, env_options_, &merge_iter_builder,
|
||||
lvl, &range_del_agg);
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
return Status::NotSupported(
|
||||
"file ingestion with range tombstones is currently unsupported");
|
||||
}
|
||||
ScopedArenaIterator level_iter(merge_iter_builder.Finish());
|
||||
|
||||
status = IngestedFileOverlapWithIteratorRange(
|
||||
file_to_ingest, level_iter.get(), &overlap_with_level);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (overlap_with_level) {
|
||||
// We must use L0 or any level higher than `lvl` to be able to overwrite
|
||||
// the keys that we overlap with in this level, We also need to assign
|
||||
// this file a seqno to overwrite the existing keys in level `lvl`
|
||||
overlap_with_db = true;
|
||||
*overlap_with_db = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (compaction_style == kCompactionStyleUniversal && lvl != 0) {
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage->LevelFiles(lvl);
|
||||
const SequenceNumber level_largest_seqno =
|
||||
(*max_element(level_files.begin(), level_files.end(),
|
||||
[](FileMetaData* f1, FileMetaData* f2) {
|
||||
return f1->largest_seqno < f2->largest_seqno;
|
||||
}))
|
||||
->largest_seqno;
|
||||
if (level_largest_seqno != 0) {
|
||||
*assigned_seqno = level_largest_seqno;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (compaction_style == kCompactionStyleUniversal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We dont overlap with any keys in this level, but we still need to check
|
||||
// if our file can fit in it
|
||||
|
||||
if (IngestedFileFitInLevel(file_to_ingest, lvl)) {
|
||||
target_level = lvl;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile",
|
||||
&overlap_with_db);
|
||||
file_to_ingest->picked_level = target_level;
|
||||
if (overlap_with_db && *assigned_seqno == 0) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::CheckLevelForIngestedBehindFile(
|
||||
IngestedFileInfo* file_to_ingest) {
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
// first check if new files fit in the bottommost level
|
||||
int bottom_lvl = cfd_->NumberLevels() - 1;
|
||||
if(!IngestedFileFitInLevel(file_to_ingest, bottom_lvl)) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file as it doesn't fit "
|
||||
"at the bottommost level!");
|
||||
}
|
||||
|
||||
// second check if despite allow_ingest_behind=true we still have 0 seqnums
|
||||
// at some upper level
|
||||
for (int lvl = 0; lvl < cfd_->NumberLevels() - 1; lvl++) {
|
||||
for (auto file : vstorage->LevelFiles(lvl)) {
|
||||
if (file->smallest_seqno == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file as despite allow_ingest_behind=true "
|
||||
"there are files with 0 seqno in database at upper levels!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_to_ingest->picked_level = bottom_lvl;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
IngestedFileInfo* file_to_ingest, SequenceNumber seqno) {
|
||||
if (file_to_ingest->original_seqno == seqno) {
|
||||
@@ -581,27 +528,6 @@ bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::IngestedFileOverlapWithLevel(
|
||||
SuperVersion* sv, IngestedFileInfo* file_to_ingest, int lvl,
|
||||
bool* overlap_with_level) {
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&cfd_->internal_comparator(),
|
||||
&arena);
|
||||
RangeDelAggregator range_del_agg(cfd_->internal_comparator(),
|
||||
{} /* snapshots */);
|
||||
sv->current->AddIteratorsForLevel(ro, env_options_, &merge_iter_builder,
|
||||
lvl, &range_del_agg);
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
return Status::NotSupported(
|
||||
"file ingestion with range tombstones is currently unsupported");
|
||||
}
|
||||
ScopedArenaIterator level_iter(merge_iter_builder.Finish());
|
||||
return IngestedFileOverlapWithIteratorRange(
|
||||
file_to_ingest, level_iter.get(), overlap_with_level);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -114,20 +114,12 @@ class ExternalSstFileIngestionJob {
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFilesOverlapWithMemtables(SuperVersion* sv, bool* overlap);
|
||||
|
||||
// Assign `file_to_ingest` the appropriate sequence number and the lowest
|
||||
// possible level that it can be ingested to according to compaction_style.
|
||||
// Assign `file_to_ingest` the lowest possible level that it can
|
||||
// be ingested to.
|
||||
// REQUIRES: Mutex held
|
||||
Status AssignLevelAndSeqnoForIngestedFile(SuperVersion* sv,
|
||||
bool force_global_seqno,
|
||||
CompactionStyle compaction_style,
|
||||
IngestedFileInfo* file_to_ingest,
|
||||
SequenceNumber* assigned_seqno);
|
||||
|
||||
// File that we want to ingest behind always goes to the lowest level;
|
||||
// we just check that it fits in the level, that DB allows ingest_behind,
|
||||
// and that we don't have 0 seqnums at the upper levels.
|
||||
// REQUIRES: Mutex held
|
||||
Status CheckLevelForIngestedBehindFile(IngestedFileInfo* file_to_ingest);
|
||||
Status AssignLevelForIngestedFile(SuperVersion* sv,
|
||||
IngestedFileInfo* file_to_ingest,
|
||||
bool* overlap_with_db);
|
||||
|
||||
// Set the file global sequence number to `seqno`
|
||||
Status AssignGlobalSeqnoForIngestedFile(IngestedFileInfo* file_to_ingest,
|
||||
@@ -139,11 +131,6 @@ class ExternalSstFileIngestionJob {
|
||||
const IngestedFileInfo* file_to_ingest, InternalIterator* iter,
|
||||
bool* overlap);
|
||||
|
||||
// Check if `file_to_ingest` key range overlap with level
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFileOverlapWithLevel(SuperVersion* sv,
|
||||
IngestedFileInfo* file_to_ingest, int lvl, bool* overlap_with_level);
|
||||
|
||||
// Check if `file_to_ingest` can fit in level `level`
|
||||
// REQUIRES: Mutex held
|
||||
bool IngestedFileFitInLevel(const IngestedFileInfo* file_to_ingest,
|
||||
|
||||
+17
-169
@@ -88,68 +88,6 @@ class ExternalSSTFileTest : public DBTestBase {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFileIngestBehind(
|
||||
const Options options, const IngestExternalFileOptions ifo,
|
||||
std::vector<std::pair<std::string, std::string>> data, int file_id = -1,
|
||||
bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
// Generate a file id if not provided
|
||||
if (file_id == -1) {
|
||||
file_id = last_file_id_ + 1;
|
||||
last_file_id_++;
|
||||
}
|
||||
|
||||
// Sort data if asked to do so
|
||||
if (sort_data) {
|
||||
std::sort(data.begin(), data.end(),
|
||||
[&](const std::pair<std::string, std::string>& e1,
|
||||
const std::pair<std::string, std::string>& e2) {
|
||||
return options.comparator->Compare(e1.first, e2.first) < 0;
|
||||
});
|
||||
auto uniq_iter = std::unique(
|
||||
data.begin(), data.end(),
|
||||
[&](const std::pair<std::string, std::string>& e1,
|
||||
const std::pair<std::string, std::string>& e2) {
|
||||
return options.comparator->Compare(e1.first, e2.first) == 0;
|
||||
});
|
||||
data.resize(uniq_iter - data.begin());
|
||||
}
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, cfh);
|
||||
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
for (auto& entry : data) {
|
||||
s = sst_file_writer.Add(entry.first, entry.second);
|
||||
if (!s.ok()) {
|
||||
sst_file_writer.Finish();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s = sst_file_writer.Finish();
|
||||
|
||||
if (s.ok()) {
|
||||
if (cfh) {
|
||||
s = db_->IngestExternalFile(cfh, {file_path}, ifo);
|
||||
} else {
|
||||
s = db_->IngestExternalFile({file_path}, ifo);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && true_data) {
|
||||
for (auto& entry : data) {
|
||||
(*true_data)[entry.first] = entry.second;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<std::pair<int, std::string>> data,
|
||||
int file_id = -1, bool allow_global_seqno = false, bool sort_data = false,
|
||||
@@ -377,7 +315,8 @@ TEST_F(ExternalSSTFileTest, Basic) {
|
||||
ASSERT_EQ(Get(Key(k)), value);
|
||||
}
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
class SstFileWriterCollector : public TablePropertiesCollector {
|
||||
public:
|
||||
@@ -617,7 +556,8 @@ TEST_F(ExternalSSTFileTest, AddList) {
|
||||
ASSERT_EQ(Get(Key(k)), value);
|
||||
}
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, AddListAtomicity) {
|
||||
@@ -659,7 +599,8 @@ TEST_F(ExternalSSTFileTest, AddListAtomicity) {
|
||||
ASSERT_EQ(Get(Key(k)), value);
|
||||
}
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
// This test reporduce a bug that can happen in some cases if the DB started
|
||||
// purging obsolete files when we are adding an external sst file.
|
||||
@@ -890,31 +831,12 @@ TEST_F(ExternalSSTFileTest, MultiThreaded) {
|
||||
|
||||
fprintf(stderr, "Verified %d values\n", num_files * keys_per_file);
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, OverlappingRanges) {
|
||||
Random rnd(301);
|
||||
int picked_level = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ExternalSstFileIngestionJob::Run", [&picked_level](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
picked_level = *(static_cast<int*>(arg));
|
||||
});
|
||||
bool need_flush = false;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::IngestExternalFile:NeedFlush", [&need_flush](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
need_flush = *(static_cast<bool*>(arg));
|
||||
});
|
||||
bool overlap_with_db = false;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile",
|
||||
[&overlap_with_db](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
overlap_with_db = *(static_cast<bool*>(arg));
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -967,27 +889,15 @@ TEST_F(ExternalSSTFileTest, OverlappingRanges) {
|
||||
|
||||
// Insert the generated file
|
||||
s = DeprecatedAddFile({file_name});
|
||||
|
||||
auto it = true_data.lower_bound(Key(range_start));
|
||||
if (option_config_ != kUniversalCompaction &&
|
||||
option_config_ != kUniversalCompactionMultiLevel) {
|
||||
if (it != true_data.end() && it->first <= Key(range_end)) {
|
||||
// This range overlap with data already exist in DB
|
||||
ASSERT_NOK(s);
|
||||
failed_add_file++;
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
success_add_file++;
|
||||
}
|
||||
if (it != true_data.end() && it->first <= Key(range_end)) {
|
||||
// This range overlap with data already exist in DB
|
||||
ASSERT_NOK(s);
|
||||
failed_add_file++;
|
||||
} else {
|
||||
if ((it != true_data.end() && it->first <= Key(range_end)) ||
|
||||
need_flush || picked_level > 0 || overlap_with_db) {
|
||||
// This range overlap with data already exist in DB
|
||||
ASSERT_NOK(s);
|
||||
failed_add_file++;
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
success_add_file++;
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
success_add_file++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,7 +930,8 @@ TEST_F(ExternalSSTFileTest, OverlappingRanges) {
|
||||
}
|
||||
printf("keys/values verified\n");
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, PickedLevel) {
|
||||
@@ -1877,69 +1788,6 @@ TEST_F(ExternalSSTFileTest, SnapshotInconsistencyBug) {
|
||||
|
||||
db_->ReleaseSnapshot(snap);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, IngestBehind) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 3;
|
||||
options.disable_auto_compactions = false;
|
||||
DestroyAndReopen(options);
|
||||
std::vector<std::pair<std::string, std::string>> file_data;
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
// Insert 100 -> 200 into the memtable
|
||||
for (int i = 100; i <= 200; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
true_data[Key(i)] = "memtable";
|
||||
}
|
||||
|
||||
// Insert 100 -> 200 using IngestExternalFile
|
||||
file_data.clear();
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
file_data.emplace_back(Key(i), "ingest_behind");
|
||||
}
|
||||
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = true;
|
||||
ifo.ingest_behind = true;
|
||||
|
||||
// Can't ingest behind since allow_ingest_behind isn't set to true
|
||||
ASSERT_NOK(GenerateAndAddExternalFileIngestBehind(options, ifo,
|
||||
file_data, -1, false,
|
||||
&true_data));
|
||||
|
||||
options.allow_ingest_behind = true;
|
||||
// check that we still can open the DB, as num_levels should be
|
||||
// sanitized to 3
|
||||
options.num_levels = 2;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
options.num_levels = 3;
|
||||
DestroyAndReopen(options);
|
||||
// Insert 100 -> 200 into the memtable
|
||||
for (int i = 100; i <= 200; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
true_data[Key(i)] = "memtable";
|
||||
}
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
// Universal picker should go at second from the bottom level
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
ASSERT_OK(GenerateAndAddExternalFileIngestBehind(options, ifo,
|
||||
file_data, -1, false,
|
||||
&true_data));
|
||||
ASSERT_EQ("0,1,1", FilesPerLevel());
|
||||
// this time ingest should fail as the file doesn't fit to the bottom level
|
||||
ASSERT_NOK(GenerateAndAddExternalFileIngestBehind(options, ifo,
|
||||
file_data, -1, false,
|
||||
&true_data));
|
||||
ASSERT_EQ("0,1,1", FilesPerLevel());
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
// bottom level should be empty
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -256,15 +256,13 @@ __attribute__((__no_sanitize_undefined__))
|
||||
|
||||
void CloseDB() {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
db_ = NULL;
|
||||
}
|
||||
|
||||
Status OpenDB() {
|
||||
CloseDB();
|
||||
env_->ResetState();
|
||||
Status s = DB::Open(options_, dbname_, &db_);
|
||||
assert(db_ != nullptr);
|
||||
return s;
|
||||
return DB::Open(options_, dbname_, &db_);
|
||||
}
|
||||
|
||||
void DeleteAllData() {
|
||||
|
||||
+12
-16
@@ -13,9 +13,11 @@ namespace rocksdb {
|
||||
|
||||
void FlushScheduler::ScheduleFlush(ColumnFamilyData* cfd) {
|
||||
#ifndef NDEBUG
|
||||
std::lock_guard<std::mutex> lock(checking_mutex_);
|
||||
assert(checking_set_.count(cfd) == 0);
|
||||
checking_set_.insert(cfd);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(checking_mutex_);
|
||||
assert(checking_set_.count(cfd) == 0);
|
||||
checking_set_.insert(cfd);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
cfd->Ref();
|
||||
// Suppress false positive clang analyzer warnings.
|
||||
@@ -32,11 +34,8 @@ void FlushScheduler::ScheduleFlush(ColumnFamilyData* cfd) {
|
||||
}
|
||||
|
||||
ColumnFamilyData* FlushScheduler::TakeNextColumnFamily() {
|
||||
#ifndef NDEBUG
|
||||
std::lock_guard<std::mutex> lock(checking_mutex_);
|
||||
#endif // NDEBUG
|
||||
while (true) {
|
||||
if (head_.load(std::memory_order_relaxed) == nullptr) {
|
||||
if (Empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -47,9 +46,11 @@ ColumnFamilyData* FlushScheduler::TakeNextColumnFamily() {
|
||||
delete node;
|
||||
|
||||
#ifndef NDEBUG
|
||||
auto iter = checking_set_.find(cfd);
|
||||
assert(iter != checking_set_.end());
|
||||
checking_set_.erase(iter);
|
||||
{
|
||||
auto iter = checking_set_.find(cfd);
|
||||
assert(iter != checking_set_.end());
|
||||
checking_set_.erase(iter);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
if (!cfd->IsDropped()) {
|
||||
@@ -65,13 +66,8 @@ ColumnFamilyData* FlushScheduler::TakeNextColumnFamily() {
|
||||
}
|
||||
|
||||
bool FlushScheduler::Empty() {
|
||||
#ifndef NDEBUG
|
||||
std::lock_guard<std::mutex> lock(checking_mutex_);
|
||||
#endif // NDEBUG
|
||||
auto rv = head_.load(std::memory_order_relaxed) == nullptr;
|
||||
#ifndef NDEBUG
|
||||
assert(rv == checking_set_.empty());
|
||||
#endif // NDEBUG
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -82,7 +78,7 @@ void FlushScheduler::Clear() {
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
assert(head_.load(std::memory_order_relaxed) == nullptr);
|
||||
assert(Empty());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -49,7 +49,6 @@ struct DBPropertyInfo {
|
||||
extern const DBPropertyInfo* GetPropertyInfo(const Slice& property);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#undef SCORE
|
||||
enum class LevelStatType {
|
||||
INVALID = 0,
|
||||
NUM_FILES,
|
||||
|
||||
+2
-6
@@ -141,9 +141,7 @@ class Counters {
|
||||
// mapped to a levedb Put
|
||||
bool set(const std::string& key, uint64_t value) {
|
||||
// just treat the internal rep of int64 as the string
|
||||
char buf[sizeof(value)];
|
||||
EncodeFixed64(buf, value);
|
||||
Slice slice(buf, sizeof(value));
|
||||
Slice slice((char *)&value, sizeof(value));
|
||||
auto s = db_->Put(put_option_, key, slice);
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -377,9 +375,7 @@ void testSingleBatchSuccessiveMerge(DB* db, size_t max_num_merges,
|
||||
|
||||
Slice key("BatchSuccessiveMerge");
|
||||
uint64_t merge_value = 1;
|
||||
char buf[sizeof(merge_value)];
|
||||
EncodeFixed64(buf, merge_value);
|
||||
Slice merge_value_slice(buf, sizeof(merge_value));
|
||||
Slice merge_value_slice((char *)&merge_value, sizeof(merge_value));
|
||||
|
||||
// Create the batch
|
||||
WriteBatch batch;
|
||||
|
||||
@@ -633,33 +633,18 @@ TEST_F(PerfContextTest, MergeOperatorTime) {
|
||||
SetPerfLevel(kEnableTime);
|
||||
perf_context.Reset();
|
||||
ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
|
||||
#ifdef OS_SOLARIS
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
|
||||
}
|
||||
#endif
|
||||
EXPECT_GT(perf_context.merge_operator_time_nanos, 0);
|
||||
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
|
||||
perf_context.Reset();
|
||||
ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
|
||||
#ifdef OS_SOLARIS
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
|
||||
}
|
||||
#endif
|
||||
EXPECT_GT(perf_context.merge_operator_time_nanos, 0);
|
||||
|
||||
ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
perf_context.Reset();
|
||||
ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
|
||||
#ifdef OS_SOLARIS
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ASSERT_OK(db->Get(ReadOptions(), "k1", &val));
|
||||
}
|
||||
#endif
|
||||
EXPECT_GT(perf_context.merge_operator_time_nanos, 0);
|
||||
|
||||
delete db;
|
||||
|
||||
+24
-44
@@ -32,7 +32,6 @@ int main() {
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
|
||||
@@ -66,16 +65,12 @@ struct TestKey {
|
||||
};
|
||||
|
||||
// return a slice backed by test_key
|
||||
inline Slice TestKeyToSlice(std::string &s, const TestKey& test_key) {
|
||||
s.clear();
|
||||
PutFixed64(&s, test_key.prefix);
|
||||
PutFixed64(&s, test_key.sorted);
|
||||
return Slice(s.c_str(), s.size());
|
||||
inline Slice TestKeyToSlice(const TestKey& test_key) {
|
||||
return Slice((const char*)&test_key, sizeof(test_key));
|
||||
}
|
||||
|
||||
inline const TestKey SliceToTestKey(const Slice& slice) {
|
||||
return TestKey(DecodeFixed64(slice.data()),
|
||||
DecodeFixed64(slice.data() + 8));
|
||||
inline const TestKey* SliceToTestKey(const Slice& slice) {
|
||||
return (const TestKey*)slice.data();
|
||||
}
|
||||
|
||||
class TestKeyComparator : public Comparator {
|
||||
@@ -84,10 +79,8 @@ class TestKeyComparator : public Comparator {
|
||||
// Compare needs to be aware of the possibility of a and/or b is
|
||||
// prefix only
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
const TestKey kkey_a = SliceToTestKey(a);
|
||||
const TestKey kkey_b = SliceToTestKey(b);
|
||||
const TestKey *key_a = &kkey_a;
|
||||
const TestKey *key_b = &kkey_b;
|
||||
const TestKey* key_a = SliceToTestKey(a);
|
||||
const TestKey* key_b = SliceToTestKey(b);
|
||||
if (key_a->prefix != key_b->prefix) {
|
||||
if (key_a->prefix < key_b->prefix) return -1;
|
||||
if (key_a->prefix > key_b->prefix) return 1;
|
||||
@@ -118,8 +111,7 @@ class TestKeyComparator : public Comparator {
|
||||
}
|
||||
|
||||
bool operator()(const TestKey& a, const TestKey& b) const {
|
||||
std::string sa, sb;
|
||||
return Compare(TestKeyToSlice(sa, a), TestKeyToSlice(sb, b)) < 0;
|
||||
return Compare(TestKeyToSlice(a), TestKeyToSlice(b)) < 0;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
@@ -136,35 +128,30 @@ namespace {
|
||||
void PutKey(DB* db, WriteOptions write_options, uint64_t prefix,
|
||||
uint64_t suffix, const Slice& value) {
|
||||
TestKey test_key(prefix, suffix);
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
ASSERT_OK(db->Put(write_options, key, value));
|
||||
}
|
||||
|
||||
void PutKey(DB* db, WriteOptions write_options, const TestKey& test_key,
|
||||
const Slice& value) {
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
ASSERT_OK(db->Put(write_options, key, value));
|
||||
}
|
||||
|
||||
void MergeKey(DB* db, WriteOptions write_options, const TestKey& test_key,
|
||||
const Slice& value) {
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
ASSERT_OK(db->Merge(write_options, key, value));
|
||||
}
|
||||
|
||||
void DeleteKey(DB* db, WriteOptions write_options, const TestKey& test_key) {
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
ASSERT_OK(db->Delete(write_options, key));
|
||||
}
|
||||
|
||||
void SeekIterator(Iterator* iter, uint64_t prefix, uint64_t suffix) {
|
||||
TestKey test_key(prefix, suffix);
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
iter->Seek(key);
|
||||
}
|
||||
|
||||
@@ -173,8 +160,7 @@ const std::string kNotFoundResult = "NOT_FOUND";
|
||||
std::string Get(DB* db, const ReadOptions& read_options, uint64_t prefix,
|
||||
uint64_t suffix) {
|
||||
TestKey test_key(prefix, suffix);
|
||||
std::string s2;
|
||||
Slice key = TestKeyToSlice(s2, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
|
||||
std::string result;
|
||||
Status s = db->Get(read_options, key, &result);
|
||||
@@ -541,9 +527,7 @@ TEST_F(PrefixTest, PrefixValid) {
|
||||
PutKey(db.get(), write_options, 12345, 9, v19);
|
||||
PutKey(db.get(), write_options, 12346, 8, v16);
|
||||
db->Flush(FlushOptions());
|
||||
TestKey test_key(12346, 8);
|
||||
std::string s;
|
||||
db->Delete(write_options, TestKeyToSlice(s, test_key));
|
||||
db->Delete(write_options, TestKeyToSlice(TestKey(12346, 8)));
|
||||
db->Flush(FlushOptions());
|
||||
read_options.prefix_same_as_start = true;
|
||||
std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
|
||||
@@ -599,8 +583,7 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
for (uint64_t sorted = 0; sorted < FLAGS_items_per_prefix; sorted++) {
|
||||
TestKey test_key(prefix, sorted);
|
||||
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
std::string value(FLAGS_value_size, 0);
|
||||
|
||||
perf_context.Reset();
|
||||
@@ -622,8 +605,7 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
|
||||
for (auto prefix : prefixes) {
|
||||
TestKey test_key(prefix, FLAGS_items_per_prefix / 2);
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
std::string value = "v" + ToString(0);
|
||||
|
||||
perf_context.Reset();
|
||||
@@ -657,8 +639,7 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
prefix < FLAGS_total_prefixes + 10000;
|
||||
prefix++) {
|
||||
TestKey test_key(prefix, 0);
|
||||
std::string s;
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
Slice key = TestKeyToSlice(test_key);
|
||||
|
||||
perf_context.Reset();
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
@@ -744,8 +725,8 @@ TEST_F(PrefixTest, PrefixSeekModePrev) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
if (FLAGS_enable_print) {
|
||||
std::cout << "round " << prefix
|
||||
<< " iter: " << SliceToTestKey(iter->key()).prefix
|
||||
<< SliceToTestKey(iter->key()).sorted
|
||||
<< " iter: " << SliceToTestKey(iter->key())->prefix
|
||||
<< SliceToTestKey(iter->key())->sorted
|
||||
<< " | map: " << it->first.prefix << it->first.sorted << " | "
|
||||
<< iter->value().ToString() << " " << it->second << std::endl;
|
||||
}
|
||||
@@ -766,16 +747,16 @@ TEST_F(PrefixTest, PrefixSeekModePrev) {
|
||||
}
|
||||
}
|
||||
if (!iter->Valid() ||
|
||||
SliceToTestKey(iter->key()).prefix != stored_prefix) {
|
||||
SliceToTestKey(iter->key())->prefix != stored_prefix) {
|
||||
break;
|
||||
}
|
||||
stored_prefix = SliceToTestKey(iter->key()).prefix;
|
||||
stored_prefix = SliceToTestKey(iter->key())->prefix;
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_NE(it, whole_map.end());
|
||||
ASSERT_EQ(iter->value(), it->second);
|
||||
if (FLAGS_enable_print) {
|
||||
std::cout << "iter: " << SliceToTestKey(iter->key()).prefix
|
||||
<< SliceToTestKey(iter->key()).sorted
|
||||
std::cout << "iter: " << SliceToTestKey(iter->key())->prefix
|
||||
<< SliceToTestKey(iter->key())->sorted
|
||||
<< " | map: " << it->first.prefix << it->first.sorted
|
||||
<< " | " << iter->value().ToString() << " " << it->second
|
||||
<< std::endl;
|
||||
@@ -829,8 +810,7 @@ TEST_F(PrefixTest, PrefixSeekModePrev3) {
|
||||
options.write_buffer_size = 1024 * 1024;
|
||||
std::string v14("v14");
|
||||
TestKey upper_bound_key = TestKey(1, 5);
|
||||
std::string s;
|
||||
Slice upper_bound = TestKeyToSlice(s, upper_bound_key);
|
||||
Slice upper_bound = TestKeyToSlice(upper_bound_key);
|
||||
|
||||
{
|
||||
DestroyDB(kDbName, Options());
|
||||
|
||||
+1
-2
@@ -314,8 +314,7 @@ class Repairer {
|
||||
// Open the log file
|
||||
std::string logname = LogFileName(dbname_, log);
|
||||
unique_ptr<SequentialFile> lfile;
|
||||
Status status = env_->NewSequentialFile(
|
||||
logname, &lfile, env_->OptimizeForLogRead(env_options_));
|
||||
Status status = env_->NewSequentialFile(logname, &lfile, env_options_);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -115,10 +115,6 @@ class TableCache {
|
||||
// Release the handle from a cache
|
||||
void ReleaseHandle(Cache::Handle* handle);
|
||||
|
||||
// Capacity of the backing Cache that indicates inifinite TableCache capacity.
|
||||
// For example when max_open_files is -1 we set the backing Cache to this.
|
||||
static const int kInfiniteCapacity = 0x400000;
|
||||
|
||||
private:
|
||||
// Build a table reader
|
||||
Status GetTableReader(const EnvOptions& env_options,
|
||||
|
||||
@@ -45,18 +45,17 @@ Status TransactionLogIteratorImpl::OpenLogFile(
|
||||
Env* env = options_->env;
|
||||
unique_ptr<SequentialFile> file;
|
||||
Status s;
|
||||
EnvOptions optimized_env_options = env->OptimizeForLogRead(soptions_);
|
||||
if (logFile->Type() == kArchivedLogFile) {
|
||||
std::string fname = ArchivedLogFileName(dir_, logFile->LogNumber());
|
||||
s = env->NewSequentialFile(fname, &file, optimized_env_options);
|
||||
s = env->NewSequentialFile(fname, &file, soptions_);
|
||||
} else {
|
||||
std::string fname = LogFileName(dir_, logFile->LogNumber());
|
||||
s = env->NewSequentialFile(fname, &file, optimized_env_options);
|
||||
s = env->NewSequentialFile(fname, &file, soptions_);
|
||||
if (!s.ok()) {
|
||||
// If cannot open file in DB directory.
|
||||
// Try the archive dir, as it could have moved in the meanwhile.
|
||||
fname = ArchivedLogFileName(dir_, logFile->LogNumber());
|
||||
s = env->NewSequentialFile(fname, &file, optimized_env_options);
|
||||
s = env->NewSequentialFile(fname, &file, soptions_);
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
|
||||
+7
-24
@@ -1126,11 +1126,8 @@ void Version::UpdateAccumulatedStats(bool update_stats) {
|
||||
storage_info_.UpdateAccumulatedStats(file_meta);
|
||||
// when option "max_open_files" is -1, all the file metadata has
|
||||
// already been read, so MaybeInitializeFileMetaData() won't incur
|
||||
// any I/O cost. "max_open_files=-1" means that the table cache passed
|
||||
// to the VersionSet and then to the ColumnFamilySet has a size of
|
||||
// TableCache::kInfiniteCapacity
|
||||
if (vset_->GetColumnFamilySet()->get_table_cache()->GetCapacity() ==
|
||||
TableCache::kInfiniteCapacity) {
|
||||
// any I/O cost.
|
||||
if (vset_->db_options_->max_open_files == -1) {
|
||||
continue;
|
||||
}
|
||||
if (++init_count >= kMaxInitCount) {
|
||||
@@ -1317,13 +1314,6 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
score =
|
||||
static_cast<double>(total_size) /
|
||||
immutable_cf_options.compaction_options_fifo.max_table_files_size;
|
||||
if (immutable_cf_options.compaction_options_fifo.allow_compaction) {
|
||||
score = std::max(
|
||||
static_cast<double>(num_sorted_runs) /
|
||||
mutable_cf_options.level0_file_num_compaction_trigger,
|
||||
score);
|
||||
}
|
||||
|
||||
} else {
|
||||
score = static_cast<double>(num_sorted_runs) /
|
||||
mutable_cf_options.level0_file_num_compaction_trigger;
|
||||
@@ -2125,11 +2115,7 @@ void VersionStorageInfo::CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
level_size = MultiplyCheckOverflow(
|
||||
level_size, options.max_bytes_for_level_multiplier);
|
||||
}
|
||||
// Don't set any level below base_bytes_max. Otherwise, the LSM can
|
||||
// assume an hourglass shape where L1+ sizes are smaller than L0. This
|
||||
// causes compaction scoring, which depends on level sizes, to favor L1+
|
||||
// at the expense of L0, which may fill up and stall.
|
||||
level_max_bytes_[i] = std::max(level_size, base_bytes_max);
|
||||
level_max_bytes_[i] = level_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2399,8 +2385,7 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
|
||||
|
||||
TEST_SYNC_POINT("VersionSet::LogAndApply:WriteManifest");
|
||||
if (!w.edit_list.front()->IsColumnFamilyManipulation() &&
|
||||
this->GetColumnFamilySet()->get_table_cache()->GetCapacity() ==
|
||||
TableCache::kInfiniteCapacity) {
|
||||
db_options_->max_open_files == -1) {
|
||||
// unlimited table cache. Pre-load table handle now.
|
||||
// Need to do it out of the mutex.
|
||||
builder_guard->version_builder()->LoadTableHandlers(
|
||||
@@ -2635,7 +2620,7 @@ Status VersionSet::Recover(
|
||||
{
|
||||
unique_ptr<SequentialFile> manifest_file;
|
||||
s = env_->NewSequentialFile(manifest_filename, &manifest_file,
|
||||
env_->OptimizeForManifestRead(env_options_));
|
||||
env_options_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -2846,8 +2831,7 @@ Status VersionSet::Recover(
|
||||
assert(builders_iter != builders.end());
|
||||
auto* builder = builders_iter->second->version_builder();
|
||||
|
||||
if (GetColumnFamilySet()->get_table_cache()->GetCapacity() ==
|
||||
TableCache::kInfiniteCapacity) {
|
||||
if (db_options_->max_open_files == -1) {
|
||||
// unlimited table cache. Pre-load table handle now.
|
||||
// Need to do it out of the mutex.
|
||||
builder->LoadTableHandlers(
|
||||
@@ -3062,8 +3046,7 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
|
||||
Status s;
|
||||
{
|
||||
unique_ptr<SequentialFile> file;
|
||||
s = options.env->NewSequentialFile(
|
||||
dscname, &file, env_->OptimizeForManifestRead(env_options_));
|
||||
s = options.env->NewSequentialFile(dscname, &file, env_options_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -166,13 +166,13 @@ TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamic) {
|
||||
Add(5, 2U, "3", "4", 550U);
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1000U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 210U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 4);
|
||||
|
||||
Add(4, 3U, "3", "4", 550U);
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1000U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 210U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 4);
|
||||
|
||||
Add(3, 4U, "3", "4", 250U);
|
||||
@@ -180,7 +180,7 @@ TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamic) {
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(1, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1005U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 1000U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 201U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 3);
|
||||
|
||||
Add(1, 6U, "3", "4", 5U);
|
||||
@@ -191,7 +191,7 @@ TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamic) {
|
||||
ASSERT_GT(vstorage_.MaxBytesForLevel(4), 1005U);
|
||||
ASSERT_GT(vstorage_.MaxBytesForLevel(3), 1005U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 1005U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 1000U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 201U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 1);
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLargeLevel) {
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(5), 3000U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 300U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 30U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 10U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 3U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.base_level(), 2);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
}
|
||||
|
||||
+1
-2
@@ -431,8 +431,7 @@ Status WalManager::ReadFirstLine(const std::string& fname,
|
||||
};
|
||||
|
||||
std::unique_ptr<SequentialFile> file;
|
||||
Status status = env_->NewSequentialFile(
|
||||
fname, &file, env_->OptimizeForLogRead(env_options_));
|
||||
Status status = env_->NewSequentialFile(fname, &file, env_options_);
|
||||
unique_ptr<SequentialFileReader> file_reader(
|
||||
new SequentialFileReader(std::move(file)));
|
||||
|
||||
|
||||
+7
-19
@@ -801,17 +801,6 @@ Status WriteBatch::RollbackToSavePoint() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status WriteBatch::PopSavePoint() {
|
||||
if (save_points_ == nullptr || save_points_->stack.size() == 0) {
|
||||
return Status::NotFound();
|
||||
}
|
||||
|
||||
// Pop the most recent savepoint off the stack
|
||||
save_points_->stack.pop();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
SequenceNumber sequence_;
|
||||
@@ -1288,17 +1277,16 @@ public:
|
||||
// 2) During Write(), in a single-threaded write thread
|
||||
// 3) During Write(), in a concurrent context where memtables has been cloned
|
||||
// The reason is that it calls memtables->Seek(), which has a stateful cache
|
||||
Status WriteBatchInternal::InsertInto(WriteThread::WriteGroup& write_group,
|
||||
SequenceNumber sequence,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
FlushScheduler* flush_scheduler,
|
||||
bool ignore_missing_column_families,
|
||||
uint64_t recovery_log_number, DB* db,
|
||||
bool concurrent_memtable_writes) {
|
||||
Status WriteBatchInternal::InsertInto(
|
||||
const autovector<WriteThread::Writer*>& writers, SequenceNumber sequence,
|
||||
ColumnFamilyMemTables* memtables, FlushScheduler* flush_scheduler,
|
||||
bool ignore_missing_column_families, uint64_t recovery_log_number, DB* db,
|
||||
bool concurrent_memtable_writes) {
|
||||
MemTableInserter inserter(sequence, memtables, flush_scheduler,
|
||||
ignore_missing_column_families, recovery_log_number,
|
||||
db, concurrent_memtable_writes);
|
||||
for (auto w : write_group) {
|
||||
for (size_t i = 0; i < writers.size(); i++) {
|
||||
auto w = writers[i];
|
||||
if (!w->ShouldWriteToMemtable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ class WriteBatchInternal {
|
||||
//
|
||||
// Under concurrent use, the caller is responsible for making sure that
|
||||
// the memtables object itself is thread-local.
|
||||
static Status InsertInto(WriteThread::WriteGroup& write_group,
|
||||
static Status InsertInto(const autovector<WriteThread::Writer*>& batches,
|
||||
SequenceNumber sequence,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
FlushScheduler* flush_scheduler,
|
||||
|
||||
@@ -859,19 +859,6 @@ TEST_F(WriteBatchTest, SavePointTest) {
|
||||
s = batch2.RollbackToSavePoint();
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
ASSERT_EQ("", PrintContents(&batch2));
|
||||
|
||||
WriteBatch batch3;
|
||||
|
||||
s = batch3.PopSavePoint();
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
ASSERT_EQ("", PrintContents(&batch3));
|
||||
|
||||
batch3.SetSavePoint();
|
||||
batch3.Delete("A");
|
||||
|
||||
s = batch3.PopSavePoint();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("Delete(A)@0", PrintContents(&batch3));
|
||||
}
|
||||
|
||||
TEST_F(WriteBatchTest, MemoryLimitTest) {
|
||||
|
||||
+142
-159
@@ -5,7 +5,6 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -64,19 +63,11 @@ class WriteCallbackTestWriteCallback2 : public WriteCallback {
|
||||
class MockWriteCallback : public WriteCallback {
|
||||
public:
|
||||
bool should_fail_ = false;
|
||||
bool was_called_ = false;
|
||||
bool allow_batching_ = false;
|
||||
std::atomic<bool> was_called_{false};
|
||||
|
||||
MockWriteCallback() {}
|
||||
|
||||
MockWriteCallback(const MockWriteCallback& other) {
|
||||
should_fail_ = other.should_fail_;
|
||||
allow_batching_ = other.allow_batching_;
|
||||
was_called_.store(other.was_called_.load());
|
||||
}
|
||||
|
||||
Status Callback(DB* db) override {
|
||||
was_called_.store(true);
|
||||
was_called_ = true;
|
||||
if (should_fail_) {
|
||||
return Status::Busy();
|
||||
} else {
|
||||
@@ -99,7 +90,7 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
void Clear() {
|
||||
kvs_.clear();
|
||||
write_batch_.Clear();
|
||||
callback_.was_called_.store(false);
|
||||
callback_.was_called_ = false;
|
||||
}
|
||||
|
||||
MockWriteCallback callback_;
|
||||
@@ -126,169 +117,161 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
for (auto& allow_parallel : {true, false}) {
|
||||
for (auto& allow_batching : {true, false}) {
|
||||
for (auto& enable_WAL : {true, false}) {
|
||||
for (auto& enable_pipelined_write : {true, false}) {
|
||||
for (auto& write_group : write_scenarios) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.allow_concurrent_memtable_write = allow_parallel;
|
||||
options.enable_pipelined_write = enable_pipelined_write;
|
||||
for (auto& write_group : write_scenarios) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.allow_concurrent_memtable_write = allow_parallel;
|
||||
|
||||
ReadOptions read_options;
|
||||
DB* db;
|
||||
DBImpl* db_impl;
|
||||
ReadOptions read_options;
|
||||
DB* db;
|
||||
DBImpl* db_impl;
|
||||
|
||||
DestroyDB(dbname, options);
|
||||
ASSERT_OK(DB::Open(options, dbname, &db));
|
||||
DestroyDB(dbname, options);
|
||||
ASSERT_OK(DB::Open(options, dbname, &db));
|
||||
|
||||
db_impl = dynamic_cast<DBImpl*>(db);
|
||||
ASSERT_TRUE(db_impl);
|
||||
db_impl = dynamic_cast<DBImpl*>(db);
|
||||
ASSERT_TRUE(db_impl);
|
||||
|
||||
std::atomic<uint64_t> threads_waiting(0);
|
||||
std::atomic<uint64_t> seq(db_impl->GetLatestSequenceNumber());
|
||||
ASSERT_EQ(db_impl->GetLatestSequenceNumber(), 0);
|
||||
std::atomic<uint64_t> threads_waiting(0);
|
||||
std::atomic<uint64_t> seq(db_impl->GetLatestSequenceNumber());
|
||||
ASSERT_EQ(db_impl->GetLatestSequenceNumber(), 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg) {
|
||||
uint64_t cur_threads_waiting = 0;
|
||||
bool is_leader = false;
|
||||
bool is_last = false;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg) {
|
||||
uint64_t cur_threads_waiting = 0;
|
||||
bool is_leader = false;
|
||||
bool is_last = false;
|
||||
|
||||
// who am i
|
||||
do {
|
||||
cur_threads_waiting = threads_waiting.load();
|
||||
is_leader = (cur_threads_waiting == 0);
|
||||
is_last = (cur_threads_waiting == write_group.size() - 1);
|
||||
} while (!threads_waiting.compare_exchange_strong(
|
||||
cur_threads_waiting, cur_threads_waiting + 1));
|
||||
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
|
||||
if (is_leader) {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_GROUP_LEADER);
|
||||
} else {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_INIT);
|
||||
}
|
||||
|
||||
// (meta test) the first WriteOP should indeed be the first
|
||||
// and the last should be the last (all others can be out of
|
||||
// order)
|
||||
if (is_leader) {
|
||||
ASSERT_TRUE(writer->callback->Callback(nullptr).ok() ==
|
||||
!write_group.front().callback_.should_fail_);
|
||||
} else if (is_last) {
|
||||
ASSERT_TRUE(writer->callback->Callback(nullptr).ok() ==
|
||||
!write_group.back().callback_.should_fail_);
|
||||
}
|
||||
|
||||
// wait for friends
|
||||
while (threads_waiting.load() < write_group.size()) {
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:DoneWaiting", [&](void* arg) {
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
|
||||
if (!allow_batching) {
|
||||
// no batching so everyone should be a leader
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_GROUP_LEADER);
|
||||
} else if (!allow_parallel) {
|
||||
ASSERT_TRUE(
|
||||
writer->state == WriteThread::State::STATE_COMPLETED ||
|
||||
(enable_pipelined_write &&
|
||||
writer->state ==
|
||||
WriteThread::State::STATE_MEMTABLE_WRITER_LEADER));
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic<uint32_t> thread_num(0);
|
||||
std::atomic<char> dummy_key(0);
|
||||
std::function<void()> write_with_callback_func = [&]() {
|
||||
uint32_t i = thread_num.fetch_add(1);
|
||||
Random rnd(i);
|
||||
|
||||
// leaders gotta lead
|
||||
while (i > 0 && threads_waiting.load() < 1) {
|
||||
}
|
||||
|
||||
// loser has to lose
|
||||
while (i == write_group.size() - 1 &&
|
||||
threads_waiting.load() < write_group.size() - 1) {
|
||||
}
|
||||
|
||||
auto& write_op = write_group.at(i);
|
||||
write_op.Clear();
|
||||
write_op.callback_.allow_batching_ = allow_batching;
|
||||
|
||||
// insert some keys
|
||||
for (uint32_t j = 0; j < rnd.Next() % 50; j++) {
|
||||
// grab unique key
|
||||
char my_key = 0;
|
||||
// who am i
|
||||
do {
|
||||
my_key = dummy_key.load();
|
||||
} while (
|
||||
!dummy_key.compare_exchange_strong(my_key, my_key + 1));
|
||||
cur_threads_waiting = threads_waiting.load();
|
||||
is_leader = (cur_threads_waiting == 0);
|
||||
is_last = (cur_threads_waiting == write_group.size() - 1);
|
||||
} while (!threads_waiting.compare_exchange_strong(
|
||||
cur_threads_waiting, cur_threads_waiting + 1));
|
||||
|
||||
string skey(5, my_key);
|
||||
string sval(10, my_key);
|
||||
write_op.Put(skey, sval);
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
|
||||
if (!write_op.callback_.should_fail_) {
|
||||
seq.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
WriteOptions woptions;
|
||||
woptions.disableWAL = !enable_WAL;
|
||||
woptions.sync = enable_WAL;
|
||||
Status s = db_impl->WriteWithCallback(
|
||||
woptions, &write_op.write_batch_, &write_op.callback_);
|
||||
|
||||
if (write_op.callback_.should_fail_) {
|
||||
ASSERT_TRUE(s.IsBusy());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
};
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// do all the writes
|
||||
std::vector<port::Thread> threads;
|
||||
for (uint32_t i = 0; i < write_group.size(); i++) {
|
||||
threads.emplace_back(write_with_callback_func);
|
||||
}
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// check for keys
|
||||
string value;
|
||||
for (auto& w : write_group) {
|
||||
ASSERT_TRUE(w.callback_.was_called_.load());
|
||||
for (auto& kvp : w.kvs_) {
|
||||
if (w.callback_.should_fail_) {
|
||||
ASSERT_TRUE(
|
||||
db->Get(read_options, kvp.first, &value).IsNotFound());
|
||||
if (is_leader) {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_GROUP_LEADER);
|
||||
} else {
|
||||
ASSERT_OK(db->Get(read_options, kvp.first, &value));
|
||||
ASSERT_EQ(value, kvp.second);
|
||||
ASSERT_TRUE(writer->state == WriteThread::State::STATE_INIT);
|
||||
}
|
||||
|
||||
// (meta test) the first WriteOP should indeed be the first
|
||||
// and the last should be the last (all others can be out of
|
||||
// order)
|
||||
if (is_leader) {
|
||||
ASSERT_TRUE(writer->callback->Callback(nullptr).ok() ==
|
||||
!write_group.front().callback_.should_fail_);
|
||||
} else if (is_last) {
|
||||
ASSERT_TRUE(writer->callback->Callback(nullptr).ok() ==
|
||||
!write_group.back().callback_.should_fail_);
|
||||
}
|
||||
|
||||
// wait for friends
|
||||
while (threads_waiting.load() < write_group.size()) {
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:DoneWaiting", [&](void* arg) {
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
|
||||
if (!allow_batching) {
|
||||
// no batching so everyone should be a leader
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_GROUP_LEADER);
|
||||
} else if (!allow_parallel) {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_COMPLETED);
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic<uint32_t> thread_num(0);
|
||||
std::atomic<char> dummy_key(0);
|
||||
std::function<void()> write_with_callback_func = [&]() {
|
||||
uint32_t i = thread_num.fetch_add(1);
|
||||
Random rnd(i);
|
||||
|
||||
// leaders gotta lead
|
||||
while (i > 0 && threads_waiting.load() < 1) {
|
||||
}
|
||||
|
||||
// loser has to lose
|
||||
while (i == write_group.size() - 1 &&
|
||||
threads_waiting.load() < write_group.size() - 1) {
|
||||
}
|
||||
|
||||
auto& write_op = write_group.at(i);
|
||||
write_op.Clear();
|
||||
write_op.callback_.allow_batching_ = allow_batching;
|
||||
|
||||
// insert some keys
|
||||
for (uint32_t j = 0; j < rnd.Next() % 50; j++) {
|
||||
// grab unique key
|
||||
char my_key = 0;
|
||||
do {
|
||||
my_key = dummy_key.load();
|
||||
} while (!dummy_key.compare_exchange_strong(my_key, my_key + 1));
|
||||
|
||||
string skey(5, my_key);
|
||||
string sval(10, my_key);
|
||||
write_op.Put(skey, sval);
|
||||
|
||||
if (!write_op.callback_.should_fail_) {
|
||||
seq.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(seq.load(), db_impl->GetLatestSequenceNumber());
|
||||
WriteOptions woptions;
|
||||
woptions.disableWAL = !enable_WAL;
|
||||
woptions.sync = enable_WAL;
|
||||
Status s = db_impl->WriteWithCallback(
|
||||
woptions, &write_op.write_batch_, &write_op.callback_);
|
||||
|
||||
delete db;
|
||||
DestroyDB(dbname, options);
|
||||
if (write_op.callback_.should_fail_) {
|
||||
ASSERT_TRUE(s.IsBusy());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
};
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// do all the writes
|
||||
std::vector<port::Thread> threads;
|
||||
for (uint32_t i = 0; i < write_group.size(); i++) {
|
||||
threads.emplace_back(write_with_callback_func);
|
||||
}
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// check for keys
|
||||
string value;
|
||||
for (auto& w : write_group) {
|
||||
ASSERT_TRUE(w.callback_.was_called_);
|
||||
for (auto& kvp : w.kvs_) {
|
||||
if (w.callback_.should_fail_) {
|
||||
ASSERT_TRUE(
|
||||
db->Get(read_options, kvp.first, &value).IsNotFound());
|
||||
} else {
|
||||
ASSERT_OK(db->Get(read_options, kvp.first, &value));
|
||||
ASSERT_EQ(value, kvp.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(seq.load(), db_impl->GetLatestSequenceNumber());
|
||||
|
||||
delete db;
|
||||
DestroyDB(dbname, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+129
-304
@@ -13,17 +13,10 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
WriteThread::WriteThread(const ImmutableDBOptions& db_options)
|
||||
: max_yield_usec_(db_options.enable_write_thread_adaptive_yield
|
||||
? db_options.write_thread_max_yield_usec
|
||||
: 0),
|
||||
slow_yield_usec_(db_options.write_thread_slow_yield_usec),
|
||||
allow_concurrent_memtable_write_(
|
||||
db_options.allow_concurrent_memtable_write),
|
||||
enable_pipelined_write_(db_options.enable_pipelined_write),
|
||||
newest_writer_(nullptr),
|
||||
newest_memtable_writer_(nullptr),
|
||||
last_sequence_(0) {}
|
||||
WriteThread::WriteThread(uint64_t max_yield_usec, uint64_t slow_yield_usec)
|
||||
: max_yield_usec_(max_yield_usec),
|
||||
slow_yield_usec_(slow_yield_usec),
|
||||
newest_writer_(nullptr) {}
|
||||
|
||||
uint8_t WriteThread::BlockingAwaitState(Writer* w, uint8_t goal_mask) {
|
||||
// We're going to block. Lazily create the mutex. We guarantee
|
||||
@@ -189,39 +182,22 @@ void WriteThread::SetState(Writer* w, uint8_t new_state) {
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteThread::LinkOne(Writer* w, std::atomic<Writer*>* newest_writer) {
|
||||
assert(newest_writer != nullptr);
|
||||
void WriteThread::LinkOne(Writer* w, bool* linked_as_leader) {
|
||||
assert(w->state == STATE_INIT);
|
||||
Writer* writers = newest_writer->load(std::memory_order_relaxed);
|
||||
while (true) {
|
||||
w->link_older = writers;
|
||||
if (newest_writer->compare_exchange_weak(writers, w)) {
|
||||
return (writers == nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteThread::LinkGroup(WriteGroup& write_group,
|
||||
std::atomic<Writer*>* newest_writer) {
|
||||
assert(newest_writer != nullptr);
|
||||
Writer* leader = write_group.leader;
|
||||
Writer* last_writer = write_group.last_writer;
|
||||
Writer* w = last_writer;
|
||||
while (true) {
|
||||
// Unset link_newer pointers to make sure when we call
|
||||
// CreateMissingNewerLinks later it create all missing links.
|
||||
w->link_newer = nullptr;
|
||||
w->write_group = nullptr;
|
||||
if (w == leader) {
|
||||
break;
|
||||
}
|
||||
w = w->link_older;
|
||||
}
|
||||
Writer* newest = newest_writer->load(std::memory_order_relaxed);
|
||||
while (true) {
|
||||
leader->link_older = newest;
|
||||
if (newest_writer->compare_exchange_weak(newest, last_writer)) {
|
||||
return (newest == nullptr);
|
||||
Writer* writers = newest_writer_.load(std::memory_order_relaxed);
|
||||
w->link_older = writers;
|
||||
if (newest_writer_.compare_exchange_strong(writers, w)) {
|
||||
if (writers == nullptr) {
|
||||
// this isn't part of the WriteThread machinery, but helps with
|
||||
// debugging and is checked by an assert in WriteImpl
|
||||
w->state.store(STATE_GROUP_LEADER, std::memory_order_relaxed);
|
||||
}
|
||||
// Then we are the head of the queue and hence definiltly the leader
|
||||
*linked_as_leader = (writers == nullptr);
|
||||
// Otherwise we will wait for previous leader to define our status
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,43 +214,12 @@ void WriteThread::CreateMissingNewerLinks(Writer* head) {
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::CompleteLeader(WriteGroup& write_group) {
|
||||
assert(write_group.size > 0);
|
||||
Writer* leader = write_group.leader;
|
||||
if (write_group.size == 1) {
|
||||
write_group.leader = nullptr;
|
||||
write_group.last_writer = nullptr;
|
||||
} else {
|
||||
assert(leader->link_newer != nullptr);
|
||||
leader->link_newer->link_older = nullptr;
|
||||
write_group.leader = leader->link_newer;
|
||||
}
|
||||
write_group.size -= 1;
|
||||
SetState(leader, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
void WriteThread::CompleteFollower(Writer* w, WriteGroup& write_group) {
|
||||
assert(write_group.size > 1);
|
||||
assert(w != write_group.leader);
|
||||
if (w == write_group.last_writer) {
|
||||
w->link_older->link_newer = nullptr;
|
||||
write_group.last_writer = w->link_older;
|
||||
} else {
|
||||
w->link_older->link_newer = w->link_newer;
|
||||
w->link_newer->link_older = w->link_older;
|
||||
}
|
||||
write_group.size -= 1;
|
||||
SetState(w, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
static AdaptationContext ctx("JoinBatchGroup");
|
||||
|
||||
assert(w->batch != nullptr);
|
||||
bool linked_as_leader = LinkOne(w, &newest_writer_);
|
||||
if (linked_as_leader) {
|
||||
SetState(w, STATE_GROUP_LEADER);
|
||||
}
|
||||
bool linked_as_leader;
|
||||
LinkOne(w, &linked_as_leader);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:Wait", w);
|
||||
|
||||
@@ -282,30 +227,25 @@ void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
/**
|
||||
* Wait util:
|
||||
* 1) An existing leader pick us as the new leader when it finishes
|
||||
* 2) An existing leader pick us as its follewer and
|
||||
* 2) An exisitng leader pick us as its follewer and
|
||||
* 2.1) finishes the memtable writes on our behalf
|
||||
* 2.2) Or tell us to finish the memtable writes in pralallel
|
||||
* 3) (pipelined write) An existing leader pick us as its follower and
|
||||
* finish book-keeping and WAL write for us, enqueue us as pending
|
||||
* memtable writer, and
|
||||
* 3.1) we become memtable writer group leader, or
|
||||
* 3.2) an existing memtable writer group leader tell us to finish memtable
|
||||
* writes in parallel.
|
||||
* 2.2) Or tell us to finish the memtable writes it in pralallel
|
||||
*/
|
||||
AwaitState(w, STATE_GROUP_LEADER | STATE_MEMTABLE_WRITER_LEADER |
|
||||
STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED,
|
||||
AwaitState(w,
|
||||
STATE_GROUP_LEADER | STATE_PARALLEL_FOLLOWER | STATE_COMPLETED,
|
||||
&ctx);
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:DoneWaiting", w);
|
||||
}
|
||||
}
|
||||
|
||||
size_t WriteThread::EnterAsBatchGroupLeader(Writer* leader,
|
||||
WriteGroup* write_group) {
|
||||
size_t WriteThread::EnterAsBatchGroupLeader(
|
||||
Writer* leader, WriteThread::Writer** last_writer,
|
||||
autovector<WriteThread::Writer*>* write_batch_group) {
|
||||
assert(leader->link_older == nullptr);
|
||||
assert(leader->batch != nullptr);
|
||||
assert(write_group != nullptr);
|
||||
|
||||
size_t size = WriteBatchInternal::ByteSize(leader->batch);
|
||||
write_batch_group->push_back(leader);
|
||||
|
||||
// Allow the group to grow up to a maximum size, but if the
|
||||
// original write is small, limit the growth so we do not slow
|
||||
@@ -315,10 +255,8 @@ size_t WriteThread::EnterAsBatchGroupLeader(Writer* leader,
|
||||
max_size = size + (128 << 10);
|
||||
}
|
||||
|
||||
leader->write_group = write_group;
|
||||
write_group->leader = leader;
|
||||
write_group->last_writer = leader;
|
||||
write_group->size = 1;
|
||||
*last_writer = leader;
|
||||
|
||||
Writer* newest_writer = newest_writer_.load(std::memory_order_acquire);
|
||||
|
||||
// This is safe regardless of any db mutex status of the caller. Previous
|
||||
@@ -368,268 +306,155 @@ size_t WriteThread::EnterAsBatchGroupLeader(Writer* leader,
|
||||
break;
|
||||
}
|
||||
|
||||
w->write_group = write_group;
|
||||
size += batch_size;
|
||||
write_group->last_writer = w;
|
||||
write_group->size++;
|
||||
write_batch_group->push_back(w);
|
||||
w->in_batch_group = true;
|
||||
*last_writer = w;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void WriteThread::EnterAsMemTableWriter(Writer* leader,
|
||||
WriteGroup* write_group) {
|
||||
assert(leader != nullptr);
|
||||
assert(leader->link_older == nullptr);
|
||||
assert(leader->batch != nullptr);
|
||||
assert(write_group != nullptr);
|
||||
void WriteThread::LaunchParallelFollowers(ParallelGroup* pg,
|
||||
SequenceNumber sequence) {
|
||||
// EnterAsBatchGroupLeader already created the links from leader to
|
||||
// newer writers in the group
|
||||
|
||||
size_t size = WriteBatchInternal::ByteSize(leader->batch);
|
||||
pg->leader->parallel_group = pg;
|
||||
|
||||
// Allow the group to grow up to a maximum size, but if the
|
||||
// original write is small, limit the growth so we do not slow
|
||||
// down the small write too much.
|
||||
size_t max_size = 1 << 20;
|
||||
if (size <= (128 << 10)) {
|
||||
max_size = size + (128 << 10);
|
||||
}
|
||||
Writer* w = pg->leader;
|
||||
w->sequence = sequence;
|
||||
|
||||
leader->write_group = write_group;
|
||||
write_group->leader = leader;
|
||||
write_group->size = 1;
|
||||
Writer* last_writer = leader;
|
||||
|
||||
if (!allow_concurrent_memtable_write_ || !leader->batch->HasMerge()) {
|
||||
Writer* newest_writer = newest_memtable_writer_.load();
|
||||
CreateMissingNewerLinks(newest_writer);
|
||||
|
||||
Writer* w = leader;
|
||||
while (w != newest_writer) {
|
||||
w = w->link_newer;
|
||||
|
||||
if (w->batch == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (w->batch->HasMerge()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!allow_concurrent_memtable_write_) {
|
||||
auto batch_size = WriteBatchInternal::ByteSize(w->batch);
|
||||
if (size + batch_size > max_size) {
|
||||
// Do not make batch too big
|
||||
break;
|
||||
}
|
||||
size += batch_size;
|
||||
}
|
||||
|
||||
w->write_group = write_group;
|
||||
last_writer = w;
|
||||
write_group->size++;
|
||||
// Initialize and wake up the others
|
||||
while (w != pg->last_writer) {
|
||||
// Writers that won't write don't get sequence allotment
|
||||
if (!w->CallbackFailed() && w->ShouldWriteToMemtable()) {
|
||||
// There is a sequence number of each written key
|
||||
sequence += WriteBatchInternal::Count(w->batch);
|
||||
}
|
||||
}
|
||||
w = w->link_newer;
|
||||
|
||||
write_group->last_writer = last_writer;
|
||||
write_group->last_sequence =
|
||||
last_writer->sequence + WriteBatchInternal::Count(last_writer->batch) - 1;
|
||||
}
|
||||
|
||||
void WriteThread::ExitAsMemTableWriter(Writer* self, WriteGroup& write_group) {
|
||||
Writer* leader = write_group.leader;
|
||||
Writer* last_writer = write_group.last_writer;
|
||||
|
||||
Writer* newest_writer = last_writer;
|
||||
if (!newest_memtable_writer_.compare_exchange_strong(newest_writer,
|
||||
nullptr)) {
|
||||
CreateMissingNewerLinks(newest_writer);
|
||||
Writer* next_leader = last_writer->link_newer;
|
||||
assert(next_leader != nullptr);
|
||||
next_leader->link_older = nullptr;
|
||||
SetState(next_leader, STATE_MEMTABLE_WRITER_LEADER);
|
||||
}
|
||||
Writer* w = leader;
|
||||
while (true) {
|
||||
if (!write_group.status.ok()) {
|
||||
w->status = write_group.status;
|
||||
}
|
||||
Writer* next = w->link_newer;
|
||||
if (w != leader) {
|
||||
SetState(w, STATE_COMPLETED);
|
||||
}
|
||||
if (w == last_writer) {
|
||||
break;
|
||||
}
|
||||
w = next;
|
||||
}
|
||||
// Note that leader has to exit last, since it owns the write group.
|
||||
SetState(leader, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
void WriteThread::LaunchParallelMemTableWriters(WriteGroup* write_group) {
|
||||
assert(write_group != nullptr);
|
||||
write_group->running.store(write_group->size);
|
||||
for (auto w : *write_group) {
|
||||
SetState(w, STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
w->sequence = sequence; // sequence number for the first key in the batch
|
||||
w->parallel_group = pg;
|
||||
SetState(w, STATE_PARALLEL_FOLLOWER);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is called by both the leader and parallel followers
|
||||
bool WriteThread::CompleteParallelMemTableWriter(Writer* w) {
|
||||
static AdaptationContext ctx("CompleteParallelMemTableWriter");
|
||||
bool WriteThread::CompleteParallelWorker(Writer* w) {
|
||||
static AdaptationContext ctx("CompleteParallelWorker");
|
||||
|
||||
auto* write_group = w->write_group;
|
||||
auto* pg = w->parallel_group;
|
||||
if (!w->status.ok()) {
|
||||
std::lock_guard<std::mutex> guard(write_group->leader->StateMutex());
|
||||
write_group->status = w->status;
|
||||
std::lock_guard<std::mutex> guard(pg->leader->StateMutex());
|
||||
pg->status = w->status;
|
||||
}
|
||||
|
||||
if (write_group->running-- > 1) {
|
||||
auto leader = pg->leader;
|
||||
auto early_exit_allowed = pg->early_exit_allowed;
|
||||
|
||||
if (pg->running.load(std::memory_order_acquire) > 1 && pg->running-- > 1) {
|
||||
// we're not the last one
|
||||
AwaitState(w, STATE_COMPLETED, &ctx);
|
||||
|
||||
// Caller only needs to perform exit duties if early exit doesn't
|
||||
// apply and this is the leader. Can't touch pg here. Whoever set
|
||||
// our state to STATE_COMPLETED copied pg->status to w.status for us.
|
||||
return w == leader && !(early_exit_allowed && w->status.ok());
|
||||
}
|
||||
// else we're the last parallel worker
|
||||
|
||||
// Errors (if there is any) must be handled by leader before waking up others
|
||||
if (w == leader || (early_exit_allowed && pg->status.ok())) {
|
||||
// this thread should perform exit duties
|
||||
w->status = pg->status;
|
||||
return true;
|
||||
} else {
|
||||
// We're the last parallel follower but early commit is not
|
||||
// applicable. Wake up the leader and then wait for it to exit.
|
||||
assert(w->state == STATE_PARALLEL_FOLLOWER);
|
||||
SetState(leader, STATE_COMPLETED);
|
||||
AwaitState(w, STATE_COMPLETED, &ctx);
|
||||
return false;
|
||||
}
|
||||
// else we're the last parallel worker and should perform exit duties.
|
||||
w->status = write_group->status;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteThread::ExitAsBatchGroupFollower(Writer* w) {
|
||||
auto* write_group = w->write_group;
|
||||
void WriteThread::EarlyExitParallelGroup(Writer* w) {
|
||||
auto* pg = w->parallel_group;
|
||||
|
||||
assert(w->state == STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
assert(write_group->status.ok());
|
||||
ExitAsBatchGroupLeader(*write_group, write_group->status);
|
||||
assert(w->state == STATE_PARALLEL_FOLLOWER);
|
||||
assert(pg->status.ok());
|
||||
ExitAsBatchGroupLeader(pg->leader, pg->last_writer, pg->status);
|
||||
assert(w->status.ok());
|
||||
assert(w->state == STATE_COMPLETED);
|
||||
SetState(write_group->leader, STATE_COMPLETED);
|
||||
SetState(pg->leader, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
void WriteThread::ExitAsBatchGroupLeader(WriteGroup& write_group,
|
||||
void WriteThread::ExitAsBatchGroupLeader(Writer* leader, Writer* last_writer,
|
||||
Status status) {
|
||||
static AdaptationContext ctx("ExitAsBatchGroupLeader");
|
||||
Writer* leader = write_group.leader;
|
||||
Writer* last_writer = write_group.last_writer;
|
||||
assert(leader->link_older == nullptr);
|
||||
|
||||
if (enable_pipelined_write_) {
|
||||
// Notify writers don't write to memtable to exit.
|
||||
for (Writer* w = last_writer; w != leader;) {
|
||||
Writer* next = w->link_older;
|
||||
w->status = status;
|
||||
if (!w->ShouldWriteToMemtable()) {
|
||||
CompleteFollower(w, write_group);
|
||||
}
|
||||
w = next;
|
||||
}
|
||||
if (!leader->ShouldWriteToMemtable()) {
|
||||
CompleteLeader(write_group);
|
||||
}
|
||||
// Link the ramaining of the group to memtable writer list.
|
||||
if (write_group.size > 0) {
|
||||
if (LinkGroup(write_group, &newest_memtable_writer_)) {
|
||||
// The leader can now be different from current writer.
|
||||
SetState(write_group.leader, STATE_MEMTABLE_WRITER_LEADER);
|
||||
}
|
||||
}
|
||||
// Reset newest_writer_ and wake up the next leader.
|
||||
Writer* newest_writer = last_writer;
|
||||
if (!newest_writer_.compare_exchange_strong(newest_writer, nullptr)) {
|
||||
Writer* next_leader = newest_writer;
|
||||
while (next_leader->link_older != last_writer) {
|
||||
next_leader = next_leader->link_older;
|
||||
assert(next_leader != nullptr);
|
||||
}
|
||||
next_leader->link_older = nullptr;
|
||||
SetState(next_leader, STATE_GROUP_LEADER);
|
||||
}
|
||||
AwaitState(leader, STATE_MEMTABLE_WRITER_LEADER |
|
||||
STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED,
|
||||
&ctx);
|
||||
} else {
|
||||
Writer* head = newest_writer_.load(std::memory_order_acquire);
|
||||
if (head != last_writer ||
|
||||
!newest_writer_.compare_exchange_strong(head, nullptr)) {
|
||||
// Either w wasn't the head during the load(), or it was the head
|
||||
// during the load() but somebody else pushed onto the list before
|
||||
// we did the compare_exchange_strong (causing it to fail). In the
|
||||
// latter case compare_exchange_strong has the effect of re-reading
|
||||
// its first param (head). No need to retry a failing CAS, because
|
||||
// only a departing leader (which we are at the moment) can remove
|
||||
// nodes from the list.
|
||||
assert(head != last_writer);
|
||||
Writer* head = newest_writer_.load(std::memory_order_acquire);
|
||||
if (head != last_writer ||
|
||||
!newest_writer_.compare_exchange_strong(head, nullptr)) {
|
||||
// Either w wasn't the head during the load(), or it was the head
|
||||
// during the load() but somebody else pushed onto the list before
|
||||
// we did the compare_exchange_strong (causing it to fail). In the
|
||||
// latter case compare_exchange_strong has the effect of re-reading
|
||||
// its first param (head). No need to retry a failing CAS, because
|
||||
// only a departing leader (which we are at the moment) can remove
|
||||
// nodes from the list.
|
||||
assert(head != last_writer);
|
||||
|
||||
// After walking link_older starting from head (if not already done)
|
||||
// we will be able to traverse w->link_newer below. This function
|
||||
// can only be called from an active leader, only a leader can
|
||||
// clear newest_writer_, we didn't, and only a clear newest_writer_
|
||||
// could cause the next leader to start their work without a call
|
||||
// to MarkJoined, so we can definitely conclude that no other leader
|
||||
// work is going on here (with or without db mutex).
|
||||
CreateMissingNewerLinks(head);
|
||||
assert(last_writer->link_newer->link_older == last_writer);
|
||||
last_writer->link_newer->link_older = nullptr;
|
||||
// After walking link_older starting from head (if not already done)
|
||||
// we will be able to traverse w->link_newer below. This function
|
||||
// can only be called from an active leader, only a leader can
|
||||
// clear newest_writer_, we didn't, and only a clear newest_writer_
|
||||
// could cause the next leader to start their work without a call
|
||||
// to MarkJoined, so we can definitely conclude that no other leader
|
||||
// work is going on here (with or without db mutex).
|
||||
CreateMissingNewerLinks(head);
|
||||
assert(last_writer->link_newer->link_older == last_writer);
|
||||
last_writer->link_newer->link_older = nullptr;
|
||||
|
||||
// Next leader didn't self-identify, because newest_writer_ wasn't
|
||||
// nullptr when they enqueued (we were definitely enqueued before them
|
||||
// and are still in the list). That means leader handoff occurs when
|
||||
// we call MarkJoined
|
||||
SetState(last_writer->link_newer, STATE_GROUP_LEADER);
|
||||
}
|
||||
// else nobody else was waiting, although there might already be a new
|
||||
// leader now
|
||||
// Next leader didn't self-identify, because newest_writer_ wasn't
|
||||
// nullptr when they enqueued (we were definitely enqueued before them
|
||||
// and are still in the list). That means leader handoff occurs when
|
||||
// we call MarkJoined
|
||||
SetState(last_writer->link_newer, STATE_GROUP_LEADER);
|
||||
}
|
||||
// else nobody else was waiting, although there might already be a new
|
||||
// leader now
|
||||
|
||||
while (last_writer != leader) {
|
||||
last_writer->status = status;
|
||||
// we need to read link_older before calling SetState, because as soon
|
||||
// as it is marked committed the other thread's Await may return and
|
||||
// deallocate the Writer.
|
||||
auto next = last_writer->link_older;
|
||||
SetState(last_writer, STATE_COMPLETED);
|
||||
while (last_writer != leader) {
|
||||
last_writer->status = status;
|
||||
// we need to read link_older before calling SetState, because as soon
|
||||
// as it is marked committed the other thread's Await may return and
|
||||
// deallocate the Writer.
|
||||
auto next = last_writer->link_older;
|
||||
SetState(last_writer, STATE_COMPLETED);
|
||||
|
||||
last_writer = next;
|
||||
}
|
||||
last_writer = next;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::EnterUnbatched(Writer* w, InstrumentedMutex* mu) {
|
||||
static AdaptationContext ctx("EnterUnbatched");
|
||||
assert(w != nullptr && w->batch == nullptr);
|
||||
mu->Unlock();
|
||||
bool linked_as_leader = LinkOne(w, &newest_writer_);
|
||||
|
||||
assert(w->batch == nullptr);
|
||||
bool linked_as_leader;
|
||||
LinkOne(w, &linked_as_leader);
|
||||
if (!linked_as_leader) {
|
||||
mu->Unlock();
|
||||
TEST_SYNC_POINT("WriteThread::EnterUnbatched:Wait");
|
||||
// Last leader will not pick us as a follower since our batch is nullptr
|
||||
AwaitState(w, STATE_GROUP_LEADER, &ctx);
|
||||
mu->Lock();
|
||||
}
|
||||
if (enable_pipelined_write_) {
|
||||
WaitForMemTableWriters();
|
||||
}
|
||||
mu->Lock();
|
||||
}
|
||||
|
||||
void WriteThread::ExitUnbatched(Writer* w) {
|
||||
assert(w != nullptr);
|
||||
Writer* newest_writer = w;
|
||||
if (!newest_writer_.compare_exchange_strong(newest_writer, nullptr)) {
|
||||
CreateMissingNewerLinks(newest_writer);
|
||||
Writer* next_leader = w->link_newer;
|
||||
assert(next_leader != nullptr);
|
||||
next_leader->link_older = nullptr;
|
||||
SetState(next_leader, STATE_GROUP_LEADER);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::WaitForMemTableWriters() {
|
||||
static AdaptationContext ctx("WaitForMemTableWriters");
|
||||
assert(enable_pipelined_write_);
|
||||
if (newest_memtable_writer_.load() == nullptr) {
|
||||
return;
|
||||
}
|
||||
Writer w;
|
||||
if (!LinkOne(&w, &newest_memtable_writer_)) {
|
||||
AwaitState(&w, STATE_MEMTABLE_WRITER_LEADER, &ctx);
|
||||
}
|
||||
newest_memtable_writer_.store(nullptr);
|
||||
Status dummy_status;
|
||||
ExitAsBatchGroupLeader(w, w, dummy_status);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+60
-137
@@ -47,66 +47,33 @@ class WriteThread {
|
||||
// the leader to STATE_COMPLETED.
|
||||
STATE_GROUP_LEADER = 2,
|
||||
|
||||
// The state used to inform a waiting writer that it has become the
|
||||
// leader of memtable writer group. The leader will either write
|
||||
// memtable for the whole group, or launch a parallel group write
|
||||
// to memtable by calling LaunchParallelMemTableWrite.
|
||||
STATE_MEMTABLE_WRITER_LEADER = 4,
|
||||
|
||||
// The state used to inform a waiting writer that it has become a
|
||||
// parallel memtable writer. It can be the group leader who launch the
|
||||
// praallel writer group, or one of the followers. The writer should then
|
||||
// apply its batch to the memtable concurrently and call
|
||||
// CompleteParallelMemTableWriter.
|
||||
STATE_PARALLEL_MEMTABLE_WRITER = 8,
|
||||
// A Writer that has returned as a follower in a parallel group.
|
||||
// It should apply its batch to the memtable and then call
|
||||
// CompleteParallelWorker. When someone calls ExitAsBatchGroupLeader
|
||||
// or EarlyExitParallelGroup this state will get transitioned to
|
||||
// STATE_COMPLETED.
|
||||
STATE_PARALLEL_FOLLOWER = 4,
|
||||
|
||||
// A follower whose writes have been applied, or a parallel leader
|
||||
// whose followers have all finished their work. This is a terminal
|
||||
// state.
|
||||
STATE_COMPLETED = 16,
|
||||
STATE_COMPLETED = 8,
|
||||
|
||||
// A state indicating that the thread may be waiting using StateMutex()
|
||||
// and StateCondVar()
|
||||
STATE_LOCKED_WAITING = 32,
|
||||
STATE_LOCKED_WAITING = 16,
|
||||
};
|
||||
|
||||
struct Writer;
|
||||
|
||||
struct WriteGroup {
|
||||
Writer* leader = nullptr;
|
||||
Writer* last_writer = nullptr;
|
||||
struct ParallelGroup {
|
||||
Writer* leader;
|
||||
Writer* last_writer;
|
||||
SequenceNumber last_sequence;
|
||||
bool early_exit_allowed;
|
||||
// before running goes to zero, status needs leader->StateMutex()
|
||||
Status status;
|
||||
std::atomic<size_t> running;
|
||||
size_t size = 0;
|
||||
|
||||
struct Iterator {
|
||||
Writer* writer;
|
||||
Writer* last_writer;
|
||||
|
||||
explicit Iterator(Writer* w, Writer* last)
|
||||
: writer(w), last_writer(last) {}
|
||||
|
||||
Writer* operator*() const { return writer; }
|
||||
|
||||
Iterator& operator++() {
|
||||
assert(writer != nullptr);
|
||||
if (writer == last_writer) {
|
||||
writer = nullptr;
|
||||
} else {
|
||||
writer = writer->link_newer;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& other) const {
|
||||
return writer != other.writer;
|
||||
}
|
||||
};
|
||||
|
||||
Iterator begin() const { return Iterator(leader, last_writer); }
|
||||
Iterator end() const { return Iterator(nullptr, nullptr); }
|
||||
std::atomic<uint32_t> running;
|
||||
};
|
||||
|
||||
// Information kept for every waiting writer.
|
||||
@@ -118,10 +85,11 @@ class WriteThread {
|
||||
bool disable_memtable;
|
||||
uint64_t log_used; // log number that this batch was inserted into
|
||||
uint64_t log_ref; // log number that memtable insert should reference
|
||||
bool in_batch_group;
|
||||
WriteCallback* callback;
|
||||
bool made_waitable; // records lazy construction of mutex and cv
|
||||
std::atomic<uint8_t> state; // write under StateMutex() or pre-link
|
||||
WriteGroup* write_group;
|
||||
ParallelGroup* parallel_group;
|
||||
SequenceNumber sequence; // the sequence number to use for the first key
|
||||
Status status; // status of memtable inserter
|
||||
Status callback_status; // status returned by callback->Callback()
|
||||
@@ -138,10 +106,11 @@ class WriteThread {
|
||||
disable_memtable(false),
|
||||
log_used(0),
|
||||
log_ref(0),
|
||||
in_batch_group(false),
|
||||
callback(nullptr),
|
||||
made_waitable(false),
|
||||
state(STATE_INIT),
|
||||
write_group(nullptr),
|
||||
parallel_group(nullptr),
|
||||
link_older(nullptr),
|
||||
link_newer(nullptr) {}
|
||||
|
||||
@@ -154,10 +123,11 @@ class WriteThread {
|
||||
disable_memtable(_disable_memtable),
|
||||
log_used(0),
|
||||
log_ref(_log_ref),
|
||||
in_batch_group(false),
|
||||
callback(_callback),
|
||||
made_waitable(false),
|
||||
state(STATE_INIT),
|
||||
write_group(nullptr),
|
||||
parallel_group(nullptr),
|
||||
link_older(nullptr),
|
||||
link_newer(nullptr) {}
|
||||
|
||||
@@ -211,12 +181,10 @@ class WriteThread {
|
||||
}
|
||||
|
||||
bool ShouldWriteToMemtable() {
|
||||
return status.ok() && !CallbackFailed() && !disable_memtable;
|
||||
return !CallbackFailed() && !disable_memtable;
|
||||
}
|
||||
|
||||
bool ShouldWriteToWAL() {
|
||||
return status.ok() && !CallbackFailed() && !disable_wal;
|
||||
}
|
||||
bool ShouldWriteToWAL() { return !CallbackFailed() && !disable_wal; }
|
||||
|
||||
// No other mutexes may be acquired while holding StateMutex(), it is
|
||||
// always last in the order
|
||||
@@ -232,16 +200,7 @@ class WriteThread {
|
||||
}
|
||||
};
|
||||
|
||||
struct AdaptationContext {
|
||||
const char* name;
|
||||
std::atomic<int32_t> value;
|
||||
|
||||
explicit AdaptationContext(const char* name0) : name(name0), value(0) {}
|
||||
};
|
||||
|
||||
explicit WriteThread(const ImmutableDBOptions& db_options);
|
||||
|
||||
virtual ~WriteThread() = default;
|
||||
WriteThread(uint64_t max_yield_usec, uint64_t slow_yield_usec);
|
||||
|
||||
// IMPORTANT: None of the methods in this class rely on the db mutex
|
||||
// for correctness. All of the methods except JoinBatchGroup and
|
||||
@@ -266,45 +225,42 @@ class WriteThread {
|
||||
// Constructs a write batch group led by leader, which should be a
|
||||
// Writer passed to JoinBatchGroup on the current thread.
|
||||
//
|
||||
// Writer* leader: Writer that is STATE_GROUP_LEADER
|
||||
// WriteGroup* write_group: Out-param of group members
|
||||
// returns: Total batch group byte size
|
||||
size_t EnterAsBatchGroupLeader(Writer* leader, WriteGroup* write_group);
|
||||
|
||||
// Unlinks the Writer-s in a batch group, wakes up the non-leaders,
|
||||
// and wakes up the next leader (if any).
|
||||
//
|
||||
// WriteGroup* write_group: the write group
|
||||
// Status status: Status of write operation
|
||||
void ExitAsBatchGroupLeader(WriteGroup& write_group, Status status);
|
||||
|
||||
// Exit batch group on behalf of batch group leader.
|
||||
void ExitAsBatchGroupFollower(Writer* w);
|
||||
|
||||
// Constructs a write batch group led by leader from newest_memtable_writers_
|
||||
// list. The leader should either write memtable for the whole group and
|
||||
// call ExitAsMemTableWriter, or launch parallel memtable write through
|
||||
// LaunchParallelMemTableWriters.
|
||||
void EnterAsMemTableWriter(Writer* leader, WriteGroup* write_grup);
|
||||
|
||||
// Memtable writer group leader, or the last finished writer in a parallel
|
||||
// write group, exit from the newest_memtable_writers_ list, and wake up
|
||||
// the next leader if needed.
|
||||
void ExitAsMemTableWriter(Writer* self, WriteGroup& write_group);
|
||||
// Writer* leader: Writer that is STATE_GROUP_LEADER
|
||||
// Writer** last_writer: Out-param that identifies the last follower
|
||||
// autovector<WriteBatch*>* write_batch_group: Out-param of group members
|
||||
// returns: Total batch group byte size
|
||||
size_t EnterAsBatchGroupLeader(
|
||||
Writer* leader, Writer** last_writer,
|
||||
autovector<WriteThread::Writer*>* write_batch_group);
|
||||
|
||||
// Causes JoinBatchGroup to return STATE_PARALLEL_FOLLOWER for all of the
|
||||
// non-leader members of this write batch group. Sets Writer::sequence
|
||||
// before waking them up.
|
||||
//
|
||||
// WriteGroup* write_group: Extra state used to coordinate the parallel add
|
||||
void LaunchParallelMemTableWriters(WriteGroup* write_group);
|
||||
// ParallalGroup* pg: Extra state used to coordinate the parallel add
|
||||
// SequenceNumber sequence: Starting sequence number to assign to Writer-s
|
||||
void LaunchParallelFollowers(ParallelGroup* pg, SequenceNumber sequence);
|
||||
|
||||
// Reports the completion of w's batch to the parallel group leader, and
|
||||
// waits for the rest of the parallel batch to complete. Returns true
|
||||
// if this thread is the last to complete, and hence should advance
|
||||
// the sequence number and then call EarlyExitParallelGroup, false if
|
||||
// someone else has already taken responsibility for that.
|
||||
bool CompleteParallelMemTableWriter(Writer* w);
|
||||
bool CompleteParallelWorker(Writer* w);
|
||||
|
||||
// This method performs an early completion of a parallel write group,
|
||||
// where the cleanup work of the leader is performed by a follower who
|
||||
// happens to be the last parallel worker to complete.
|
||||
void EarlyExitParallelGroup(Writer* w);
|
||||
|
||||
// Unlinks the Writer-s in a batch group, wakes up the non-leaders,
|
||||
// and wakes up the next leader (if any).
|
||||
//
|
||||
// Writer* leader: From EnterAsBatchGroupLeader
|
||||
// Writer* last_writer: Value of out-param of EnterAsBatchGroupLeader
|
||||
// Status status: Status of write operation
|
||||
void ExitAsBatchGroupLeader(Writer* leader, Writer* last_writer,
|
||||
Status status);
|
||||
|
||||
// Waits for all preceding writers (unlocking mu while waiting), then
|
||||
// registers w as the currently proceeding writer.
|
||||
@@ -318,40 +274,21 @@ class WriteThread {
|
||||
// writers.
|
||||
void ExitUnbatched(Writer* w);
|
||||
|
||||
// Wait for all parallel memtable writers to finish, in case pipelined
|
||||
// write is enabled.
|
||||
void WaitForMemTableWriters();
|
||||
struct AdaptationContext {
|
||||
const char* name;
|
||||
std::atomic<int32_t> value;
|
||||
|
||||
SequenceNumber UpdateLastSequence(SequenceNumber sequence) {
|
||||
if (sequence > last_sequence_) {
|
||||
last_sequence_ = sequence;
|
||||
}
|
||||
return last_sequence_;
|
||||
}
|
||||
explicit AdaptationContext(const char* name0) : name(name0), value(0) {}
|
||||
};
|
||||
|
||||
private:
|
||||
// See AwaitState.
|
||||
const uint64_t max_yield_usec_;
|
||||
const uint64_t slow_yield_usec_;
|
||||
uint64_t max_yield_usec_;
|
||||
uint64_t slow_yield_usec_;
|
||||
|
||||
// Allow multiple writers write to memtable concurrently.
|
||||
const bool allow_concurrent_memtable_write_;
|
||||
|
||||
// Enable pipelined write to WAL and memtable.
|
||||
const bool enable_pipelined_write_;
|
||||
|
||||
// Points to the newest pending writer. Only leader can remove
|
||||
// elements, adding can be done lock-free by anybody.
|
||||
// Points to the newest pending Writer. Only leader can remove
|
||||
// elements, adding can be done lock-free by anybody
|
||||
std::atomic<Writer*> newest_writer_;
|
||||
|
||||
// Points to the newest pending memtable writer. Used only when pipelined
|
||||
// write is enabled.
|
||||
std::atomic<Writer*> newest_memtable_writer_;
|
||||
|
||||
// The last sequence that have been consumed by a writer. The sequence
|
||||
// is not necessary visible to reads because the writer can be ongoing.
|
||||
SequenceNumber last_sequence_;
|
||||
|
||||
// Waits for w->state & goal_mask using w->StateMutex(). Returns
|
||||
// the state that satisfies goal_mask.
|
||||
uint8_t BlockingAwaitState(Writer* w, uint8_t goal_mask);
|
||||
@@ -362,30 +299,16 @@ class WriteThread {
|
||||
// a context-dependent static.
|
||||
uint8_t AwaitState(Writer* w, uint8_t goal_mask, AdaptationContext* ctx);
|
||||
|
||||
// Set writer state and wake the writer up if it is waiting.
|
||||
void SetState(Writer* w, uint8_t new_state);
|
||||
|
||||
// Links w into the newest_writer list. Return true if w was linked directly
|
||||
// into the leader position. Safe to call from multiple threads without
|
||||
// external locking.
|
||||
bool LinkOne(Writer* w, std::atomic<Writer*>* newest_writer);
|
||||
|
||||
// Link write group into the newest_writer list as a whole, while keeping the
|
||||
// order of the writers unchanged. Return true if the group was linked
|
||||
// directly into the leader position.
|
||||
bool LinkGroup(WriteGroup& write_group, std::atomic<Writer*>* newest_writer);
|
||||
// Links w into the newest_writer_ list. Sets *linked_as_leader to
|
||||
// true if w was linked directly into the leader position. Safe to
|
||||
// call from multiple threads without external locking.
|
||||
void LinkOne(Writer* w, bool* linked_as_leader);
|
||||
|
||||
// Computes any missing link_newer links. Should not be called
|
||||
// concurrently with itself.
|
||||
void CreateMissingNewerLinks(Writer* head);
|
||||
|
||||
// Set the leader in write_group to completed state and remove it from the
|
||||
// write group.
|
||||
void CompleteLeader(WriteGroup& write_group);
|
||||
|
||||
// Set a follower in write_group to completed state and remove it from the
|
||||
// write group.
|
||||
void CompleteFollower(Writer* w, WriteGroup& write_group);
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -1,385 +0,0 @@
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More_considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public licenses.
|
||||
Notwithstanding, Creative Commons may elect to apply one of its public
|
||||
licenses to material it publishes and in those instances will be
|
||||
considered the "Licensor." Except for the limited purpose of indicating
|
||||
that material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the public
|
||||
licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
|
||||
@@ -40,15 +40,3 @@ rven2:
|
||||
yiwu:
|
||||
full_name: Yi Wu
|
||||
fbid: 100000476362039
|
||||
|
||||
maysamyabandeh:
|
||||
full_name: Maysam Yabandeh
|
||||
fbid: 100003482360101
|
||||
|
||||
IslamAbdelRahman:
|
||||
full_name: Islam AbdelRahman
|
||||
fbid: 642759407
|
||||
|
||||
ajkr:
|
||||
full_name: Andrew Kryczka
|
||||
fbid: 100010829806660
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Bulkloading by ingesting external SST files
|
||||
title: Bulkoading by ingesting external SST files
|
||||
layout: post
|
||||
author: IslamAbdelRahman
|
||||
category: blog
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
title: Partitioned Index/Filters
|
||||
layout: post
|
||||
author: maysamyabandeh
|
||||
category: blog
|
||||
---
|
||||
|
||||
As DB/mem ratio gets larger, the memory footprint of filter/index blocks becomes non-trivial. Although `cache_index_and_filter_blocks` allows storing only a subset of them in block cache, their relatively large size negatively affects the performance by i) occupying the block cache space that could otherwise be used for caching data, ii) increasing the load on the disk storage by loading them into the cache after a miss. Here we illustrate these problems in more detail and explain how partitioning index/filters alleviates the overhead.
|
||||
|
||||
### How large are the index/filter blocks?
|
||||
|
||||
RocksDB has by default one index/filter block per SST file. The size of the index/filter varies based on the configuration but for a SST of size 256MB the index/filter block of size 0.5/5MB is typical, which is much larger than the typical data block size of 4-32KB. That is fine when all index/filters fit perfectly into memory and hence are read once per SST lifetime, not so much when they compete with data blocks for the block cache space and are also likely to be re-read many times from the disk.
|
||||
|
||||
### What is the big deal with large index/filter blocks?
|
||||
|
||||
When index/filter blocks are stored in block cache they are effectively competing with data blocks (as well as with each other) on this scarce resource. A filter of size 5MB is occupying the space that could otherwise be used to cache 1000s of data blocks (of size 4KB). This would result in more cache misses for data blocks. The large index/filters also kick each other out of the block cache more often and exacerbate their own cache miss rate too. This is while only a small part of the index/filter block might have been actually used during its lifetime in the cache.
|
||||
|
||||
After the cache miss of an index/filter, it has to be reloaded from the disk, and its large size is not helping in reducing the IO cost. While a simple point lookup might need at most a couple of data block reads (of size 4KB) one from each layer of LSM, it might end up also loading multiple megabytes of index/filter blocks. If that happens often then the disk is spending more time serving index/filters rather than the actual data blocks.
|
||||
|
||||
## What is partitioned index/filters?
|
||||
|
||||
With partitioning, the index/filter of a SST file is partitioned into smaller blocks with an additional top-level index on them. When reading an index/filter, only top-level index is loaded into memory. The partitioned index/filter then uses the top-level index to load on demand into the block cache the partitions that are required to perform the index/filter query. The top-level index, which has much smaller memory footprint, can be stored in heap or block cache depending on the `cache_index_and_filter_blocks` setting.
|
||||
|
||||
### Success stories
|
||||
|
||||
#### HDD, 100TB DB
|
||||
|
||||
In this example we have a DB of size 86G on HDD and emulate the small memory that is present to a node with 100TB of data by using direct IO (skipping OS file cache) and a very small block cache of size 60MB. Partitioning improves throughput by 11x from 5 op/s to 55 op/s.
|
||||
|
||||
#### SSD, Linkbench
|
||||
|
||||
In this example we have a DB of size 300G on SSD and emulate the small memory that would be available in presence of other DBs on the same node by by using direct IO (skipping OS file cache) and block cache of size 6G and 2G. Without partitioning the linkbench throughput drops from 38k tps to 23k when reducing block cache size from 6G to 2G. With partitioning the throughput drops from 38k to only 30k.
|
||||
|
||||
Learn more (here)[https://github.com/facebook/rocksdb/wiki/Partitioned-Index-Filters].
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
title: Core-local Statistics
|
||||
layout: post
|
||||
author: ajkr
|
||||
category: blog
|
||||
---
|
||||
|
||||
## Origins: Global Atomics
|
||||
|
||||
Until RocksDB 4.12, ticker/histogram statistics were implemented with std::atomic values shared across the entire program. A ticker consists of a single atomic, while a histogram consists of several atomics to represent things like min/max/per-bucket counters. These statistics could be updated by all user/background threads.
|
||||
|
||||
For concurrent/high-throughput workloads, cache line bouncing of atomics caused high CPU utilization. For example, we have tickers that count block cache hits and misses. Almost every user read increments these tickers a few times. Many concurrent user reads would cause the cache lines containing these atomics to bounce between cores.
|
||||
|
||||
### Performance
|
||||
|
||||
Here are perf results for 32 reader threads where most reads (99%+) are served by uncompressed block cache. Such a scenario stresses the statistics code heavily.
|
||||
|
||||
Benchmark command: `TEST_TMPDIR=/dev/shm/ perf record -g ./db_bench -statistics -use_existing_db=true -benchmarks=readrandom -threads=32 -cache_size=1048576000 -num=1000000 -reads=1000000 && perf report -g --children`
|
||||
|
||||
Perf snippet for "cycles" event:
|
||||
|
||||
```
|
||||
Children Self Command Shared Object Symbol
|
||||
+ 30.33% 30.17% db_bench db_bench [.] rocksdb::StatisticsImpl::recordTick
|
||||
+ 3.65% 0.98% db_bench db_bench [.] rocksdb::StatisticsImpl::measureTime
|
||||
```
|
||||
|
||||
Perf snippet for "cache-misses" event:
|
||||
|
||||
```
|
||||
Children Self Command Shared Object Symbol
|
||||
+ 19.54% 19.50% db_bench db_bench [.] rocksdb::StatisticsImpl::recordTick
|
||||
+ 3.44% 0.57% db_bench db_bench [.] rocksdb::StatisticsImpl::measureTime
|
||||
```
|
||||
|
||||
The high CPU overhead for updating tickers and histograms corresponds well to the high cache misses.
|
||||
|
||||
## Thread-locals: Faster Updates
|
||||
|
||||
Since RocksDB 4.12, ticker/histogram statistics use thread-local storage. Each thread has a local set of atomic values that no other thread can update. This prevents the cache line bouncing problem described above. Even though updates to a given value are always made by the same thread, atomics are still useful to synchronize with aggregations for querying statistics.
|
||||
|
||||
Implementing this approach involved a couple challenges. First, each query for a statistic's global value must aggregate all threads' local values. This adds some overhead, which may pass unnoticed if statistics are queried infrequently. Second, exited threads' local values are still needed to provide accurate statistics. We handle this by merging a thread's local values into process-wide variables upon thread exit.
|
||||
|
||||
### Performance
|
||||
|
||||
Update benchmark setup is same as before. CPU overhead improved 7.8x compared to global atomics, corresponding to a 17.8x reduction in cache-misses overhead.
|
||||
|
||||
Perf snippet for "cycles" event:
|
||||
|
||||
```
|
||||
Children Self Command Shared Object Symbol
|
||||
+ 2.96% 0.87% db_bench db_bench [.] rocksdb::StatisticsImpl::recordTick
|
||||
+ 1.37% 0.10% db_bench db_bench [.] rocksdb::StatisticsImpl::measureTime
|
||||
```
|
||||
|
||||
Perf snippet for "cache-misses" event:
|
||||
|
||||
```
|
||||
Children Self Command Shared Object Symbol
|
||||
+ 1.21% 0.65% db_bench db_bench [.] rocksdb::StatisticsImpl::recordTick
|
||||
0.08% 0.00% db_bench db_bench [.] rocksdb::StatisticsImpl::measureTime
|
||||
```
|
||||
|
||||
To measure statistics query latency, we ran sysbench with 4K OLTP clients concurrently with one client that queries statistics repeatedly. Times shown are in milliseconds.
|
||||
|
||||
```
|
||||
min: 18.45
|
||||
avg: 27.91
|
||||
max: 231.65
|
||||
95th percentile: 55.82
|
||||
```
|
||||
|
||||
## Core-locals: Faster Querying
|
||||
|
||||
The thread-local approach is working well for applications calling RocksDB from only a few threads, or polling statistics infrequently. Eventually, though, we found use cases where those assumptions do not hold. For example, one application has per-connection threads and typically runs into performance issues when connection count grows very high. For debugging such issues, they want high-frequency statistics polling to correlate issues in their application with changes in RocksDB's state.
|
||||
|
||||
Once [PR #2258](https://github.com/facebook/rocksdb/pull/2258) lands, ticker/histogram statistics will be local to each CPU core. Similarly to thread-local, each core updates only its local values, thus avoiding cache line bouncing. Local values are still atomics to make aggregation possible. With this change, query work depends only on number of cores, not the number of threads. So, applications with many more threads than cores can no longer impact statistics query latency.
|
||||
|
||||
### Performance
|
||||
|
||||
Update benchmark setup is same as before. CPU overhead worsened ~23% compared to thread-local, while cache performance was unchanged.
|
||||
|
||||
Perf snippet for "cycles" event:
|
||||
|
||||
```
|
||||
Children Self Command Shared Object Symbol
|
||||
+ 2.96% 0.87% db_bench db_bench [.] rocksdb::StatisticsImpl::recordTick
|
||||
+ 1.37% 0.10% db_bench db_bench [.] rocksdb::StatisticsImpl::measureTime
|
||||
```
|
||||
|
||||
Perf snippet for "cache-misses" event:
|
||||
|
||||
```
|
||||
Children Self Command Shared Object Symbol
|
||||
+ 1.21% 0.65% db_bench db_bench [.] rocksdb::StatisticsImpl::recordTick
|
||||
0.08% 0.00% db_bench db_bench [.] rocksdb::StatisticsImpl::measureTime
|
||||
```
|
||||
|
||||
Query latency is measured same as before with times in milliseconds. Average latency improved by 6.3x compared to thread-local.
|
||||
|
||||
```
|
||||
min: 2.47
|
||||
avg: 4.45
|
||||
max: 91.13
|
||||
95th percentile: 7.56
|
||||
```
|
||||
Vendored
-12
@@ -340,18 +340,6 @@ EnvOptions Env::OptimizeForManifestWrite(const EnvOptions& env_options) const {
|
||||
return env_options;
|
||||
}
|
||||
|
||||
EnvOptions Env::OptimizeForLogRead(const EnvOptions& env_options) const {
|
||||
EnvOptions optimized_env_options(env_options);
|
||||
optimized_env_options.use_direct_reads = false;
|
||||
return optimized_env_options;
|
||||
}
|
||||
|
||||
EnvOptions Env::OptimizeForManifestRead(const EnvOptions& env_options) const {
|
||||
EnvOptions optimized_env_options(env_options);
|
||||
optimized_env_options.use_direct_reads = false;
|
||||
return optimized_env_options;
|
||||
}
|
||||
|
||||
EnvOptions Env::OptimizeForCompactionTableWrite(
|
||||
const EnvOptions& env_options, const ImmutableDBOptions& db_options) const {
|
||||
EnvOptions optimized_env_options(env_options);
|
||||
|
||||
Vendored
-14
@@ -24,18 +24,11 @@ class ChrootEnv : public EnvWrapper {
|
||||
public:
|
||||
ChrootEnv(Env* base_env, const std::string& chroot_dir)
|
||||
: EnvWrapper(base_env) {
|
||||
#if defined(OS_AIX)
|
||||
char resolvedName[PATH_MAX];
|
||||
char* real_chroot_dir = realpath(chroot_dir.c_str(), resolvedName);
|
||||
#else
|
||||
char* real_chroot_dir = realpath(chroot_dir.c_str(), nullptr);
|
||||
#endif
|
||||
// chroot_dir must exist so realpath() returns non-nullptr.
|
||||
assert(real_chroot_dir != nullptr);
|
||||
chroot_dir_ = real_chroot_dir;
|
||||
#if !defined(OS_AIX)
|
||||
free(real_chroot_dir);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Status NewSequentialFile(const std::string& fname,
|
||||
@@ -265,12 +258,7 @@ class ChrootEnv : public EnvWrapper {
|
||||
}
|
||||
std::pair<Status, std::string> res;
|
||||
res.second = chroot_dir_ + path;
|
||||
#if defined(OS_AIX)
|
||||
char resolvedName[PATH_MAX];
|
||||
char* normalized_path = realpath(res.second.c_str(), resolvedName);
|
||||
#else
|
||||
char* normalized_path = realpath(res.second.c_str(), nullptr);
|
||||
#endif
|
||||
if (normalized_path == nullptr) {
|
||||
res.first = Status::NotFound(res.second, strerror(errno));
|
||||
} else if (strlen(normalized_path) < chroot_dir_.size() ||
|
||||
@@ -281,9 +269,7 @@ class ChrootEnv : public EnvWrapper {
|
||||
} else {
|
||||
res.first = Status::OK();
|
||||
}
|
||||
#if !defined(OS_AIX)
|
||||
free(normalized_path);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+5
-23
@@ -159,7 +159,7 @@ class PosixEnv : public Env {
|
||||
#ifdef ROCKSDB_LITE
|
||||
return Status::IOError(fname, "Direct I/O not supported in RocksDB lite");
|
||||
#endif // !ROCKSDB_LITE
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD)
|
||||
flags |= O_DIRECT;
|
||||
#endif
|
||||
}
|
||||
@@ -206,7 +206,7 @@ class PosixEnv : public Env {
|
||||
#ifdef ROCKSDB_LITE
|
||||
return Status::IOError(fname, "Direct I/O not supported in RocksDB lite");
|
||||
#endif // !ROCKSDB_LITE
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD)
|
||||
flags |= O_DIRECT;
|
||||
TEST_SYNC_POINT_CALLBACK("NewRandomAccessFile:O_DIRECT", &flags);
|
||||
#endif
|
||||
@@ -272,7 +272,7 @@ class PosixEnv : public Env {
|
||||
return Status::IOError(fname, "Direct I/O not supported in RocksDB lite");
|
||||
#endif // ROCKSDB_LITE
|
||||
flags |= O_WRONLY;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD)
|
||||
flags |= O_DIRECT;
|
||||
#endif
|
||||
TEST_SYNC_POINT_CALLBACK("NewWritableFile:O_DIRECT", &flags);
|
||||
@@ -313,14 +313,6 @@ class PosixEnv : public Env {
|
||||
s = IOError(fname, errno);
|
||||
return s;
|
||||
}
|
||||
#elif defined(OS_SOLARIS)
|
||||
if (directio(fd, DIRECTIO_ON) == -1) {
|
||||
if (errno != ENOTTY) { // ZFS filesystems don't support DIRECTIO_ON
|
||||
close(fd);
|
||||
s = IOError(fname, errno);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
result->reset(new PosixWritableFile(fname, fd, options));
|
||||
} else {
|
||||
@@ -359,7 +351,7 @@ class PosixEnv : public Env {
|
||||
return Status::IOError(fname, "Direct I/O not supported in RocksDB lite");
|
||||
#endif // !ROCKSDB_LITE
|
||||
flags |= O_WRONLY;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_OPENBSD)
|
||||
flags |= O_DIRECT;
|
||||
#endif
|
||||
TEST_SYNC_POINT_CALLBACK("NewWritableFile:O_DIRECT", &flags);
|
||||
@@ -406,14 +398,6 @@ class PosixEnv : public Env {
|
||||
s = IOError(fname, errno);
|
||||
return s;
|
||||
}
|
||||
#elif defined(OS_SOLARIS)
|
||||
if (directio(fd, DIRECTIO_ON) == -1) {
|
||||
if (errno != ENOTTY) { // ZFS filesystems don't support DIRECTIO_ON
|
||||
close(fd);
|
||||
s = IOError(fname, errno);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
result->reset(new PosixWritableFile(fname, fd, options));
|
||||
} else {
|
||||
@@ -697,12 +681,10 @@ class PosixEnv : public Env {
|
||||
}
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_AIX)
|
||||
#if defined(OS_LINUX) || defined(OS_FREEBSD)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
|
||||
#elif defined(OS_SOLARIS)
|
||||
return gethrtime();
|
||||
#elif defined(__MACH__)
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t ts;
|
||||
|
||||
Vendored
+6
-6
@@ -958,7 +958,7 @@ TEST_P(EnvPosixTestWithParam, InvalidateCache) {
|
||||
// Create file.
|
||||
{
|
||||
unique_ptr<WritableFile> wfile;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && !defined(OS_AIX)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN)
|
||||
if (soptions.use_direct_writes) {
|
||||
soptions.use_direct_writes = false;
|
||||
}
|
||||
@@ -974,7 +974,7 @@ TEST_P(EnvPosixTestWithParam, InvalidateCache) {
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
auto scratch = NewAligned(kSectorSize, 0);
|
||||
Slice result;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && !defined(OS_AIX)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN)
|
||||
if (soptions.use_direct_reads) {
|
||||
soptions.use_direct_reads = false;
|
||||
}
|
||||
@@ -991,7 +991,7 @@ TEST_P(EnvPosixTestWithParam, InvalidateCache) {
|
||||
unique_ptr<SequentialFile> file;
|
||||
auto scratch = NewAligned(kSectorSize, 0);
|
||||
Slice result;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && !defined(OS_AIX)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN)
|
||||
if (soptions.use_direct_reads) {
|
||||
soptions.use_direct_reads = false;
|
||||
}
|
||||
@@ -1136,7 +1136,7 @@ TEST_P(EnvPosixTestWithParam, Preallocation) {
|
||||
unique_ptr<WritableFile> srcfile;
|
||||
EnvOptions soptions;
|
||||
soptions.use_direct_reads = soptions.use_direct_writes = direct_io_;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && !defined(OS_AIX)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN)
|
||||
if (soptions.use_direct_writes) {
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile:O_DIRECT", [&](void* arg) {
|
||||
@@ -1198,7 +1198,7 @@ TEST_P(EnvPosixTestWithParam, ConsistentChildrenAttributes) {
|
||||
oss << test::TmpDir(env_) << "/testfile_" << i;
|
||||
const std::string path = oss.str();
|
||||
unique_ptr<WritableFile> file;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && !defined(OS_AIX)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN)
|
||||
if (soptions.use_direct_writes) {
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile:O_DIRECT", [&](void* arg) {
|
||||
@@ -1231,7 +1231,7 @@ TEST_P(EnvPosixTestWithParam, ConsistentChildrenAttributes) {
|
||||
ASSERT_EQ(size, 4096 * i);
|
||||
ASSERT_EQ(size, file_attrs_iter->size_bytes);
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
}
|
||||
|
||||
// Test that all WritableFileWrapper forwards all calls to WritableFile.
|
||||
|
||||
Vendored
+5
-8
@@ -268,7 +268,7 @@ size_t PosixHelper::GetUniqueIdFromFile(int fd, char* id, size_t max_size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX) || defined(OS_AIX)
|
||||
#if defined(OS_MACOSX)
|
||||
size_t PosixHelper::GetUniqueIdFromFile(int fd, char* id, size_t max_size) {
|
||||
if (max_size < kMaxVarint64Length * 3) {
|
||||
return 0;
|
||||
@@ -797,10 +797,11 @@ Status PosixWritableFile::Close() {
|
||||
// While we work with Travis-CI team to figure out if this is a
|
||||
// quirk of Docker/AUFS, we will comment this out.
|
||||
struct stat file_stats;
|
||||
fstat(fd_, &file_stats);
|
||||
int result = fstat(fd_, &file_stats);
|
||||
// After ftruncate, we check whether ftruncate has the correct behavior.
|
||||
// If not, we should hack it with FALLOC_FL_PUNCH_HOLE
|
||||
if ((file_stats.st_size + file_stats.st_blksize - 1) /
|
||||
if (result == 0 &&
|
||||
(file_stats.st_size + file_stats.st_blksize - 1) /
|
||||
file_stats.st_blksize !=
|
||||
file_stats.st_blocks / (file_stats.st_blksize / 512)) {
|
||||
IOSTATS_TIMER_GUARD(allocate_nanos);
|
||||
@@ -876,7 +877,7 @@ Status PosixWritableFile::Allocate(uint64_t offset, uint64_t len) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ROCKSDB_RANGESYNC_PRESENT
|
||||
#ifdef OS_LINUX
|
||||
Status PosixWritableFile::RangeSync(uint64_t offset, uint64_t nbytes) {
|
||||
assert(offset <= std::numeric_limits<off_t>::max());
|
||||
assert(nbytes <= std::numeric_limits<off_t>::max());
|
||||
@@ -887,9 +888,7 @@ Status PosixWritableFile::RangeSync(uint64_t offset, uint64_t nbytes) {
|
||||
return IOError(filename_, errno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
size_t PosixWritableFile::GetUniqueId(char* id, size_t max_size) const {
|
||||
return PosixHelper::GetUniqueIdFromFile(fd_, id, max_size);
|
||||
}
|
||||
@@ -991,11 +990,9 @@ Status PosixRandomRWFile::Close() {
|
||||
PosixDirectory::~PosixDirectory() { close(fd_); }
|
||||
|
||||
Status PosixDirectory::Fsync() {
|
||||
#ifndef OS_AIX
|
||||
if (fsync(fd_) == -1) {
|
||||
return IOError("directory", errno);
|
||||
}
|
||||
#endif
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
Vendored
+2
-4
@@ -15,7 +15,7 @@
|
||||
|
||||
// For non linux platform, the following macros are used only as place
|
||||
// holder.
|
||||
#if !(defined OS_LINUX) && !(defined CYGWIN) && !(defined OS_AIX)
|
||||
#if !(defined OS_LINUX) && !(defined CYGWIN)
|
||||
#define POSIX_FADV_NORMAL 0 /* [MC1] no further special treatment */
|
||||
#define POSIX_FADV_RANDOM 1 /* [MC1] expect random page refs */
|
||||
#define POSIX_FADV_SEQUENTIAL 2 /* [MC1] expect sequential page refs */
|
||||
@@ -129,10 +129,8 @@ class PosixWritableFile : public WritableFile {
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
virtual Status Allocate(uint64_t offset, uint64_t len) override;
|
||||
#endif
|
||||
#ifdef ROCKSDB_RANGESYNC_PRESENT
|
||||
virtual Status RangeSync(uint64_t offset, uint64_t nbytes) override;
|
||||
#endif
|
||||
#ifdef OS_LINUX
|
||||
virtual Status RangeSync(uint64_t offset, uint64_t nbytes) override;
|
||||
virtual size_t GetUniqueId(char* id, size_t max_size) const override;
|
||||
#endif
|
||||
};
|
||||
|
||||
Loaded 100 of 240 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user