mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-08 11:55:21 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acf935e40f | ||
|
|
8254e9b57c | ||
|
|
74f18c1301 | ||
|
|
5de98f2d50 | ||
|
|
3f5888430a | ||
|
|
e5a1b727c0 | ||
|
|
6f051e0c71 | ||
|
|
ac098a4626 | ||
|
|
666a005f9b | ||
|
|
ad77ee0ea0 | ||
|
|
b87ee6f773 | ||
|
|
25df24254b | ||
|
|
64f8484356 | ||
|
|
0cecf8155b | ||
|
|
6a9de43477 | ||
|
|
23c7d13540 | ||
|
|
1fbad84b69 | ||
|
|
7848f0b24c | ||
|
|
47ed3bfc3b | ||
|
|
d97a72d63f | ||
|
|
36375de76f | ||
|
|
bdc056f8aa | ||
|
|
a9a4e89c38 | ||
|
|
4ca11b4b07 | ||
|
|
c9804e007a | ||
|
|
20dc5e74f2 | ||
|
|
0d4a2b7330 | ||
|
|
627c9f1abb | ||
|
|
dce6d5a838 | ||
|
|
4f81ab38bf | ||
|
|
92afe830f9 | ||
|
|
5883a1ae24 | ||
|
|
cc01985db0 | ||
|
|
0b814ba92d | ||
|
|
2c45ada4c4 | ||
|
|
58410aee44 | ||
|
|
c3d5c4d38a | ||
|
|
060ccd4f84 | ||
|
|
3453870677 | ||
|
|
1900771bd2 | ||
|
|
3218edc573 | ||
|
|
6a36b3a7b9 | ||
|
|
a84cee8127 | ||
|
|
21696ba502 | ||
|
|
e85f2c64cb | ||
|
|
ac748c57ed | ||
|
|
aaf42fe775 | ||
|
|
aace46516b | ||
|
|
50a969131f | ||
|
|
6083bc79f8 | ||
|
|
710411aea6 | ||
|
|
3a3fb00b7a | ||
|
|
fca4d6da17 | ||
|
|
8f553d3c52 | ||
|
|
7f6d012d70 | ||
|
|
d12691b86f | ||
|
|
f33f113683 | ||
|
|
e7697b8ce8 | ||
|
|
3ce20e985b | ||
|
|
c281b44829 | ||
|
|
9980de262c | ||
|
|
2289d38115 | ||
|
|
30b58cf71a | ||
|
|
fe1a5559f3 | ||
|
|
addbd279c2 | ||
|
|
30edff308e | ||
|
|
2b259c9d49 | ||
|
|
277f6f23d4 | ||
|
|
5e731a1382 | ||
|
|
9b11d4345a | ||
|
|
06f1917449 | ||
|
|
ea8ad4f678 | ||
|
|
34112aeffd | ||
|
|
1d8aa2961c | ||
|
|
a4c42e8007 | ||
|
|
16e0388205 | ||
|
|
216644c61c | ||
|
|
e67b35c076 | ||
|
|
a34b2e388e | ||
|
|
72502cf227 | ||
|
|
1d7048c598 | ||
|
|
534c255c7a | ||
|
|
63163a8c6e | ||
|
|
0302da47a7 | ||
|
|
3e5ea29a83 | ||
|
|
a22b9cc6fe | ||
|
|
ffd2a2eefd | ||
|
|
3e6e863b1f | ||
|
|
36651d14ee | ||
|
|
6e3ee015fb | ||
|
|
ecff9d5e33 | ||
|
|
ae28634e9f | ||
|
|
33b1de82a7 | ||
|
|
2f375154ea | ||
|
|
ddb22ac59c | ||
|
|
0c03a7f17d | ||
|
|
7ac184c6d7 | ||
|
|
0655b58582 | ||
|
|
00464a3140 | ||
|
|
b2dd192fed | ||
|
|
cbaab30449 | ||
|
|
f1a056e005 | ||
|
|
4a2e4891fe | ||
|
|
a7321fc97f | ||
|
|
3c327ac2d0 | ||
|
|
132013366d | ||
|
|
ccf5f08f88 | ||
|
|
67510eeff3 | ||
|
|
4267eb00d4 | ||
|
|
5bfb67d90f | ||
|
|
20a691d98f | ||
|
|
98d1a5510b | ||
|
|
26ce69b195 | ||
|
|
43e4eef77f | ||
|
|
21b17d7686 | ||
|
|
70440f7a63 | ||
|
|
7550255c54 | ||
|
|
7a0b5de771 | ||
|
|
000bf0af38 | ||
|
|
269d383d5d | ||
|
|
c32f27223b | ||
|
|
fcd99d27c9 | ||
|
|
87128bd5c5 | ||
|
|
8f927e5f75 | ||
|
|
643b787c7b | ||
|
|
56656e12d6 | ||
|
|
b5fb85ec51 | ||
|
|
657df29eaf | ||
|
|
a43c053ad8 | ||
|
|
7c4a9e6c92 | ||
|
|
98669b5356 | ||
|
|
0013bf14ef | ||
|
|
521b4c28b9 | ||
|
|
33042573db | ||
|
|
f6b9d9355e | ||
|
|
45b9bb0331 | ||
|
|
f4ae1bab02 | ||
|
|
1cb8c6de63 | ||
|
|
7604b463b5 | ||
|
|
1e34d07e18 | ||
|
|
d310e0f339 | ||
|
|
e9f91a5176 | ||
|
|
c1b375e96a | ||
|
|
67b417d624 | ||
|
|
afbef65187 | ||
|
|
397ab11152 | ||
|
|
18c63af6ef | ||
|
|
01534db24c | ||
|
|
1cd45cd1b3 | ||
|
|
982cec22af | ||
|
|
b49b371092 | ||
|
|
e517bfa2c2 | ||
|
|
dc3d2e4d21 | ||
|
|
89468c01d4 | ||
|
|
51778612c9 | ||
|
|
7061912c23 | ||
|
|
2a9cd87997 | ||
|
|
0025a36409 | ||
|
|
5c97a7c066 | ||
|
|
4cee11f4e3 | ||
|
|
857e9960be | ||
|
|
d757355cbf | ||
|
|
8e6345d2df | ||
|
|
499ebb3ab5 | ||
|
|
0ac4afb975 | ||
|
|
521724ba82 | ||
|
|
71f5bcb730 | ||
|
|
88cd2d96e7 | ||
|
|
6837a17621 | ||
|
|
af1746751f | ||
|
|
9467eb6141 | ||
|
|
048446fc74 | ||
|
|
a21db161c9 | ||
|
|
c430d69eed | ||
|
|
0d278456c9 | ||
|
|
cbd825deea | ||
|
|
2c98b06bff | ||
|
|
c80c6115de | ||
|
|
6a3377f454 | ||
|
|
53dda8797d | ||
|
|
ae8571f5c2 | ||
|
|
7a380deff7 | ||
|
|
89ad9f3adb | ||
|
|
6b5a5dc5d8 | ||
|
|
0f228be3bb | ||
|
|
c217e0b9c7 | ||
|
|
91e2aa3ce2 | ||
|
|
6f4154d693 | ||
|
|
5d5a28a98c | ||
|
|
0175d58c3c | ||
|
|
7a270069b3 | ||
|
|
d713471da8 | ||
|
|
9bb91e9328 | ||
|
|
27b450165e | ||
|
|
e97304c681 | ||
|
|
5582123dee | ||
|
|
db818d2d1a | ||
|
|
a472c4ae4c | ||
|
|
bc09c8a0db | ||
|
|
6d0f22e42f | ||
|
|
c2012d488f | ||
|
|
85dace2afa | ||
|
|
d4f7731b61 |
No files matched your search
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"project_id" : "rocksdb",
|
||||
"conduit_uri" : "https://phabricator.fb.com/api/",
|
||||
"copyright_holder" : "Facebook",
|
||||
"load" : [
|
||||
"arcanist_util"
|
||||
],
|
||||
"lint.engine" : "FacebookFbcodeLintEngine",
|
||||
"lint.engine.single.linter" : "FbcodeCppLinter",
|
||||
"unit.engine" : "FacebookFbcodeUnitTestEngine",
|
||||
"arcanist_configuration" : "FacebookArcanistConfiguration",
|
||||
"base" : "git:HEAD^, hg:.^",
|
||||
"git.default-relative-commit" : "HEAD^",
|
||||
"git:arc.feature.start.default" : "origin/master",
|
||||
"arc.feature.start.default" : "master",
|
||||
"history.immutable" : false
|
||||
}
|
||||
+1
-1
@@ -57,7 +57,7 @@ before_script:
|
||||
|
||||
script:
|
||||
- ${CXX} --version
|
||||
- if [ "${TEST_GROUP}" == 'platform_dependent' ]; then OPT=-DTRAVIS V=1 make -j4 all; OPT=-DTRAVIS V=1 ROCKSDBTESTS_END=db_block_cache_test make -j4 check_some; fi
|
||||
- if [ "${TEST_GROUP}" == 'platform_dependent' ]; then ccache -C && OPT=-DTRAVIS V=1 ROCKSDBTESTS_END=db_block_cache_test make -j4 all_but_some_tests 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
|
||||
|
||||
+15
-12
@@ -174,7 +174,6 @@ 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")
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-momit-leaf-frame-pointer" HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
@@ -380,6 +379,7 @@ set(SOURCES
|
||||
db/write_thread.cc
|
||||
env/env.cc
|
||||
env/env_chroot.cc
|
||||
env/env_encryption.cc
|
||||
env/env_hdfs.cc
|
||||
env/mock_env.cc
|
||||
memtable/alloc_tracker.cc
|
||||
@@ -476,12 +476,15 @@ set(SOURCES
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/blob_db/blob_db_impl.cc
|
||||
utilities/blob_db/blob_db_options_impl.cc
|
||||
utilities/blob_db/blob_dump_tool.cc
|
||||
utilities/blob_db/blob_file.cc
|
||||
utilities/blob_db/blob_log_reader.cc
|
||||
utilities/blob_db/blob_log_writer.cc
|
||||
utilities/blob_db/blob_log_format.cc
|
||||
utilities/blob_db/ttl_extractor.cc
|
||||
utilities/cassandra/cassandra_compaction_filter.cc
|
||||
utilities/cassandra/format.cc
|
||||
utilities/cassandra/merge_operator.cc
|
||||
utilities/checkpoint/checkpoint_impl.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/col_buf_encoder.cc
|
||||
@@ -500,8 +503,6 @@ set(SOURCES
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/cassandra/format.cc
|
||||
utilities/merge_operators/cassandra/merge_operator.cc
|
||||
utilities/merge_operators/string_append/stringappend.cc
|
||||
utilities/merge_operators/string_append/stringappend2.cc
|
||||
utilities/merge_operators/uint64add.cc
|
||||
@@ -517,13 +518,14 @@ set(SOURCES
|
||||
utilities/spatialdb/spatial_db.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
utilities/transactions/optimistic_transaction_impl.cc
|
||||
utilities/transactions/optimistic_transaction.cc
|
||||
utilities/transactions/transaction_base.cc
|
||||
utilities/transactions/transaction_db_impl.cc
|
||||
utilities/transactions/pessimistic_transaction_db.cc
|
||||
utilities/transactions/transaction_db_mutex_impl.cc
|
||||
utilities/transactions/transaction_impl.cc
|
||||
utilities/transactions/pessimistic_transaction.cc
|
||||
utilities/transactions/transaction_lock_mgr.cc
|
||||
utilities/transactions/transaction_util.cc
|
||||
utilities/transactions/write_prepared_txn.cc
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
@@ -705,6 +707,10 @@ set(TESTS
|
||||
util/thread_local_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/cassandra/cassandra_functional_test.cc
|
||||
utilities/cassandra/cassandra_format_test.cc
|
||||
utilities/cassandra/cassandra_row_merge_test.cc
|
||||
utilities/cassandra/cassandra_serialize_test.cc
|
||||
utilities/checkpoint/checkpoint_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
@@ -713,10 +719,6 @@ set(TESTS
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/lua/rocks_lua_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/cassandra/cassandra_merge_test.cc
|
||||
utilities/merge_operators/cassandra/cassandra_format_test.cc
|
||||
utilities/merge_operators/cassandra/cassandra_row_merge_test.cc
|
||||
utilities/merge_operators/cassandra/cassandra_serialize_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
utilities/option_change_migration/option_change_migration_test.cc
|
||||
@@ -725,6 +727,7 @@ set(TESTS
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/redis/redis_lists_test.cc
|
||||
utilities/spatialdb/spatial_db_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
@@ -757,7 +760,7 @@ set(TESTUTIL_SOURCE
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
util/fault_injection_test_env.cc
|
||||
utilities/merge_operators/cassandra/test_utils.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
# test utilities are only build in debug
|
||||
enable_testing()
|
||||
|
||||
+20
-3
@@ -1,11 +1,28 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.6.2 (08/12/2017)
|
||||
## Unreleased
|
||||
### New Features
|
||||
* Add Iterator::Refresh(), which allows users to update the iterator state so that they can avoid some initialization costs of recreating iterators.
|
||||
* Replace dynamic_cast<> (except unit test) so people can choose to build with RTTI off. With make, release mode is by default built with -fno-rtti and debug mode is built without it. Users can override it by setting USE_RTTI=0 or 1.
|
||||
* Universal compactions including the bottom level can be executed in a dedicated thread pool. This alleviates head-of-line blocking in the compaction queue, which cause write stalling, particularly in multi-instance use cases. Users can enable this feature via `Env::SetBackgroundThreads(N, Env::Priority::BOTTOM)`, where `N > 0`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix wrong latencies in `rocksdb.db.get.micros`, `rocksdb.db.write.micros`, and `rocksdb.sst.read.micros`.
|
||||
* Fix incorrect dropping of deletions during intra-L0 compaction.
|
||||
|
||||
## 5.6.1 (07/25/2017)
|
||||
## 5.7.0 (07/13/2017)
|
||||
### Public API Change
|
||||
* DB property "rocksdb.sstables" now prints keys in hex form.
|
||||
|
||||
### New Features
|
||||
* Measure estimated number of reads per file. The information can be accessed through DB::GetColumnFamilyMetaData or "rocksdb.sstables" DB property.
|
||||
* RateLimiter support for throttling background reads, or throttling the sum of background reads and writes. This can give more predictable I/O usage when compaction reads more data than it writes, e.g., due to lots of deletions.
|
||||
* [Experimental] FIFO compaction with TTL support. It can be enabled by setting CompactionOptionsFIFO.ttl > 0.
|
||||
* Introduce `EventListener::OnBackgroundError()` callback. Users can implement it to be notified of errors causing the DB to enter read-only mode, and optionally override them.
|
||||
* Partitioned Index/Filters exiting the experimental mode. To enable partitioned indexes set index_type to kTwoLevelIndexSearch and to further enable partitioned filters set partition_filters to true. To configure the partition size set metadata_block_size.
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
* Fix lite build.
|
||||
* Fix discarding empty compaction output files when `DeleteRange()` is used together with subcompactions.
|
||||
|
||||
## 5.6.0 (06/06/2017)
|
||||
### Public API Change
|
||||
|
||||
@@ -32,6 +32,7 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
* You can link RocksDB with following compression libraries:
|
||||
- [zlib](http://www.zlib.net/) - a library for data compression.
|
||||
- [bzip2](http://www.bzip.org/) - a library for data compression.
|
||||
- [lz4](https://github.com/lz4/lz4) - a library for extremely fast data compression.
|
||||
- [snappy](http://google.github.io/snappy/) - a library for fast
|
||||
data compression.
|
||||
- [zstandard](http://www.zstd.net) - Fast real-time compression
|
||||
@@ -53,6 +54,7 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
`sudo apt-get install libsnappy-dev`.
|
||||
* Install zlib. Try: `sudo apt-get install zlib1g-dev`.
|
||||
* Install bzip2: `sudo apt-get install libbz2-dev`.
|
||||
* Install lz4: `sudo apt-get install liblz4-dev`.
|
||||
* Install zstandard: `sudo apt-get install libzstd-dev`.
|
||||
|
||||
* **Linux - CentOS / RHEL**
|
||||
@@ -81,6 +83,10 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
|
||||
sudo yum install bzip2 bzip2-devel
|
||||
|
||||
* Install lz4:
|
||||
|
||||
sudo yum install lz4-devel
|
||||
|
||||
* Install ASAN (optional for debugging):
|
||||
|
||||
sudo yum install libasan
|
||||
@@ -107,6 +113,8 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
* **Windows**:
|
||||
* For building with MS Visual Studio 13 you will need Update 4 installed.
|
||||
* Read and follow the instructions at CMakeLists.txt
|
||||
* Or install via [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* run `vcpkg install rocksdb`
|
||||
|
||||
* **AIX 6.1**
|
||||
* Install AIX Toolbox rpms with gcc
|
||||
|
||||
@@ -101,7 +101,19 @@ endif
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
DISABLE_WARNING_AS_ERROR=1
|
||||
|
||||
ifneq ($(USE_RTTI), 1)
|
||||
CXXFLAGS += -fno-rtti
|
||||
else
|
||||
CXXFLAGS += -DROCKSDB_USE_RTTI
|
||||
endif
|
||||
else
|
||||
ifneq ($(USE_RTTI), 0)
|
||||
CXXFLAGS += -DROCKSDB_USE_RTTI
|
||||
else
|
||||
CXXFLAGS += -fno-rtti
|
||||
endif
|
||||
|
||||
$(warning Warning: Compiling in debug mode. Don't use the resulting binary in production)
|
||||
endif
|
||||
|
||||
@@ -220,6 +232,10 @@ ifndef DISABLE_JEMALLOC
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
endif
|
||||
ifdef WITH_JEMALLOC_FLAG
|
||||
PLATFORM_LDFLAGS += -ljemalloc
|
||||
JAVA_LDFLAGS += -ljemalloc
|
||||
endif
|
||||
EXEC_LDFLAGS := $(JEMALLOC_LIB) $(EXEC_LDFLAGS)
|
||||
PLATFORM_CXXFLAGS += $(JEMALLOC_INCLUDE)
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE)
|
||||
@@ -243,6 +259,19 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
CCVERSION = $(shell $(CXX) -dumpversion)
|
||||
CCNAME = $(shell $(CXX) --version | awk 'NR==1' | cut -f1 -d " ")
|
||||
|
||||
ifeq ($(CCNAME), clang)
|
||||
ifeq ($(CCVERSION), 4*)
|
||||
CXXFLAGS += -faligned-new
|
||||
endif
|
||||
else
|
||||
ifeq ($(CCVERSION), 7)
|
||||
CXXFLAGS += -faligned-new
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef DISABLE_WARNING_AS_ERROR
|
||||
WARNING_FLAGS += -Werror
|
||||
endif
|
||||
@@ -323,6 +352,7 @@ EXPOBJECTS = $(EXP_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
db_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
external_sst_file_basic_test \
|
||||
auto_roll_logger_test \
|
||||
@@ -404,7 +434,7 @@ TESTS = \
|
||||
write_buffer_manager_test \
|
||||
stringappend_test \
|
||||
cassandra_format_test \
|
||||
cassandra_merge_test \
|
||||
cassandra_functional_test \
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
ttl_test \
|
||||
@@ -574,6 +604,8 @@ endif # PLATFORM_SHARED_EXT
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
|
||||
all_but_some_tests: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(SUBSET)
|
||||
|
||||
static_lib: $(LIBRARY)
|
||||
|
||||
shared_lib: $(SHARED)
|
||||
@@ -777,8 +809,8 @@ ldb_tests: ldb
|
||||
crash_test: whitebox_crash_test blackbox_crash_test
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox
|
||||
python -u tools/db_crashtest.py blackbox
|
||||
python -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
@@ -786,9 +818,9 @@ endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check:
|
||||
$(MAKE) clean
|
||||
@@ -999,16 +1031,16 @@ option_change_migration_test: utilities/option_change_migration/option_change_mi
|
||||
stringappend_test: utilities/merge_operators/string_append/stringappend_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cassandra_format_test: utilities/merge_operators/cassandra/cassandra_format_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cassandra_format_test: utilities/cassandra/cassandra_format_test.o utilities/cassandra/test_utils.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cassandra_merge_test: utilities/merge_operators/cassandra/cassandra_merge_test.o utilities/merge_operators/cassandra/test_utils.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cassandra_functional_test: utilities/cassandra/cassandra_functional_test.o utilities/cassandra/test_utils.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cassandra_row_merge_test: utilities/merge_operators/cassandra/cassandra_row_merge_test.o utilities/merge_operators/cassandra/test_utils.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cassandra_row_merge_test: utilities/cassandra/cassandra_row_merge_test.o utilities/cassandra/test_utils.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cassandra_serialize_test: utilities/merge_operators/cassandra/cassandra_serialize_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cassandra_serialize_test: utilities/cassandra/cassandra_serialize_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
redis_test: utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1035,6 +1067,9 @@ slice_transform_test: util/slice_transform_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_basic_test: db/db_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_encryption_test: db/db_encryption_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_test: db/db_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ REPO_PATH = TARGETS_PATH[(TARGETS_PATH.find('fbcode/') + len('fbcode/')):] + "/"
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
rocksdb_compiler_flags = [
|
||||
"-msse",
|
||||
"-msse4.2",
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
@@ -18,6 +16,7 @@ rocksdb_compiler_flags = [
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
"-DHAVE_SSE42",
|
||||
"-DOS_LINUX",
|
||||
"-DROCKSDB_UBSAN_RUN",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
@@ -110,6 +109,7 @@ cpp_library(
|
||||
"db/write_thread.cc",
|
||||
"env/env.cc",
|
||||
"env/env_chroot.cc",
|
||||
"env/env_encryption.cc",
|
||||
"env/env_hdfs.cc",
|
||||
"env/env_posix.cc",
|
||||
"env/io_posix.cc",
|
||||
@@ -206,11 +206,14 @@ cpp_library(
|
||||
"utilities/backupable/backupable_db.cc",
|
||||
"utilities/blob_db/blob_db.cc",
|
||||
"utilities/blob_db/blob_db_impl.cc",
|
||||
"utilities/blob_db/blob_db_options_impl.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/blob_db/blob_log_reader.cc",
|
||||
"utilities/blob_db/blob_log_writer.cc",
|
||||
"utilities/blob_db/blob_log_format.cc",
|
||||
"utilities/blob_db/ttl_extractor.cc",
|
||||
"utilities/cassandra/cassandra_compaction_filter.cc",
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
@@ -225,8 +228,6 @@ cpp_library(
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/lua/rocks_lua_compaction_filter.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators/cassandra/format.cc",
|
||||
"utilities/merge_operators/cassandra/merge_operator.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
"utilities/merge_operators/put.cc",
|
||||
"utilities/merge_operators/string_append/stringappend.cc",
|
||||
@@ -244,13 +245,14 @@ cpp_library(
|
||||
"utilities/spatialdb/spatial_db.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/optimistic_transaction_impl.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/transaction_base.cc",
|
||||
"utilities/transactions/transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction_db.cc",
|
||||
"utilities/transactions/transaction_db_mutex_impl.cc",
|
||||
"utilities/transactions/transaction_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
"utilities/transactions/transaction_lock_mgr.cc",
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/transactions/write_prepared_txn.cc",
|
||||
"utilities/ttl/db_ttl_impl.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
@@ -274,7 +276,7 @@ cpp_library(
|
||||
"util/testharness.cc",
|
||||
"util/testutil.cc",
|
||||
"db/db_test_util.cc",
|
||||
"utilities/merge_operators/cassandra/test_utils.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
"utilities/col_buf_encoder.cc",
|
||||
"utilities/col_buf_decoder.cc",
|
||||
"utilities/column_aware_encoding_util.cc",
|
||||
@@ -324,16 +326,16 @@ ROCKS_TESTS = [['arena_test', 'util/arena_test.cc', 'serial'],
|
||||
['c_test', 'db/c_test.c', 'serial'],
|
||||
['cache_test', 'cache/cache_test.cc', 'serial'],
|
||||
['cassandra_format_test',
|
||||
'utilities/merge_operators/cassandra/cassandra_format_test.cc',
|
||||
'utilities/cassandra/cassandra_format_test.cc',
|
||||
'serial'],
|
||||
['cassandra_merge_test',
|
||||
'utilities/merge_operators/cassandra/cassandra_merge_test.cc',
|
||||
['cassandra_functional_test',
|
||||
'utilities/cassandra/cassandra_functional_test.cc',
|
||||
'serial'],
|
||||
['cassandra_row_merge_test',
|
||||
'utilities/merge_operators/cassandra/cassandra_row_merge_test.cc',
|
||||
'utilities/cassandra/cassandra_row_merge_test.cc',
|
||||
'serial'],
|
||||
['cassandra_serialize_test',
|
||||
'utilities/merge_operators/cassandra/cassandra_serialize_test.cc',
|
||||
'utilities/cassandra/cassandra_serialize_test.cc',
|
||||
'serial'],
|
||||
['checkpoint_test', 'utilities/checkpoint/checkpoint_test.cc', 'serial'],
|
||||
['cleanable_test', 'table/cleanable_test.cc', 'serial'],
|
||||
|
||||
@@ -76,7 +76,10 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
[Dgraph](https://github.com/dgraph-io/dgraph) is an open-source, scalable, distributed, low latency, high throughput Graph database .They use RocksDB to store state locally on a machine.
|
||||
|
||||
## Uber
|
||||
[Uber](http://eng.uber.com/cherami/) uses RocksDB as a durable and and scalable task queue.
|
||||
[Uber](http://eng.uber.com/cherami/) uses RocksDB as a durable and scalable task queue.
|
||||
|
||||
## 360 Pika
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been widely used in many company
|
||||
|
||||
## LzLabs
|
||||
LzLabs is using RocksDB as a storage engine in their multi-database distributed framework to store application configuration and user data.
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Create a tmp directory for the test to use
|
||||
TEST_DIR=$(mktemp -d /dev/shm/fbcode_rocksdb_XXXXXXX)
|
||||
TEST_TMPDIR="$TEST_DIR" $@ && rm -rf "$TEST_DIR"
|
||||
@@ -10,8 +10,6 @@ REPO_PATH = TARGETS_PATH[(TARGETS_PATH.find('fbcode/') + len('fbcode/')):] + "/"
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
rocksdb_compiler_flags = [
|
||||
"-msse",
|
||||
"-msse4.2",
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
@@ -22,6 +20,7 @@ rocksdb_compiler_flags = [
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
"-DHAVE_SSE42",
|
||||
"-DOS_LINUX",
|
||||
"-DROCKSDB_UBSAN_RUN",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. 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).
|
||||
|
||||
// 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"
|
||||
);
|
||||
|
||||
// This fixes "FATAL: ThreadSanitizer can not mmap the shadow memory"
|
||||
// Source:
|
||||
// https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual#FAQ
|
||||
$fix_kernel_issue = array(
|
||||
"name" => "Fix kernel issue with tsan",
|
||||
"shell" => "echo 2 >/proc/sys/kernel/randomize_va_space",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $fix_git_ignore;
|
||||
$steps[] = $fix_kernel_issue;
|
||||
|
||||
// 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", "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 */);
|
||||
}
|
||||
@@ -51,13 +51,11 @@ 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 [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -319,9 +317,11 @@ EOF
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ljemalloc"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ljemalloc"
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
# JEMALLOC can be enabled either using the flag (like here) or by
|
||||
# providing direct link to the jemalloc library
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
else
|
||||
# jemalloc is not available. Let's try tcmalloc
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null \
|
||||
@@ -519,7 +519,14 @@ echo "ROCKSDB_PATCH=$ROCKSDB_PATCH" >> "$OUTPUT"
|
||||
echo "CLANG_SCAN_BUILD=$CLANG_SCAN_BUILD" >> "$OUTPUT"
|
||||
echo "CLANG_ANALYZER=$CLANG_ANALYZER" >> "$OUTPUT"
|
||||
echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
# This will enable some related identifiers for the preprocessor
|
||||
if test -n "$JEMALLOC"; then
|
||||
echo "JEMALLOC=1" >> "$OUTPUT"
|
||||
fi
|
||||
# Indicates that jemalloc should be enabled using -ljemalloc flag
|
||||
# The alternative is to porvide a direct link to the library via JEMALLOC_LIB
|
||||
# and JEMALLOC_INCLUDE
|
||||
if test -n "$WITH_JEMALLOC_FLAG"; then
|
||||
echo "WITH_JEMALLOC_FLAG=$WITH_JEMALLOC_FLAG" >> "$OUTPUT"
|
||||
fi
|
||||
echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) 2016, Facebook. All rights reserved.
|
||||
#
|
||||
@@ -67,7 +67,7 @@ function update_repo_status {
|
||||
#
|
||||
|
||||
# Path to the determinator from the root of the RocksDB repo.
|
||||
CONTRUN_DETERMINATOR=./arcanist_util/config/RocksDBCommonHelper.php
|
||||
CONTRUN_DETERMINATOR=./build_tools/RocksDBCommonHelper.php
|
||||
|
||||
# Value of the previous commit.
|
||||
PREV_COMMIT=
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/60d6f124a78798b73944f5ba87c2306ae3460153/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/3ee276cbacfad3074e3f07bf826ac47f06970f4e/1.3.5/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/9c910d36d6235cc40e8ff559358f1833452300ca/master/platform007/5b0f53e
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/92ff90349e2f43ea0a8246d8b1cf17b6869013e3/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
docker run -v $PWD:/rocks -w /rocks buildpack-deps make
|
||||
@@ -1,157 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# This script is executed by Sandcastle
|
||||
# to determine next steps to run
|
||||
|
||||
@@ -85,9 +85,8 @@ NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
# in gcc-5 there are known problems with TSAN like https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71090.
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
LITE="OPT=\"-DROCKSDB_LITE -g\""
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
DISABLE_JEMALLOC="DISABLE_JEMALLOC=1"
|
||||
HTTP_PROXY="https_proxy=http://fwdproxy.29.prn1:8080 http_proxy=http://fwdproxy.29.prn1:8080 ftp_proxy=http://fwdproxy.29.prn1:8080"
|
||||
@@ -131,35 +130,6 @@ DISABLE_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test in parallel
|
||||
# Currently we always have noise in our parallel runs. This job is to help
|
||||
# manage the noise
|
||||
#
|
||||
PARALLEL_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Parallel Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version under gcc-4.8.1',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check_gcc481 $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test
|
||||
#
|
||||
@@ -594,7 +564,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN CRASH_TEST_KILL_ODD=1887 CRASH_TEST_EXT_ARGS=--log2_keys_per_lock=22 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -613,39 +583,7 @@ run_format_compatible()
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
|
||||
echo '
|
||||
if [ -e "build_tools/build_detect_platform" ]
|
||||
then
|
||||
sed "s/tcmalloc/nothingnothingnothing/g" build_tools/build_detect_platform > $TEST_TMPDIR/temp_build_file
|
||||
rm -rf build_tools/build_detect_platform
|
||||
cp $TEST_TMPDIR/temp_build_file build_tools/build_detect_platform
|
||||
chmod +x build_tools/build_detect_platform
|
||||
fi
|
||||
|
||||
if [ -e "build_detect_platform" ]
|
||||
then
|
||||
sed "s/tcmalloc/nothingnothingnothing/g" build_detect_platform > $TEST_TMPDIR/temp_build_file
|
||||
rm -rf build_detect_platform
|
||||
cp $TEST_TMPDIR/temp_build_file build_detect_platform
|
||||
chmod +x build_detect_platform
|
||||
fi
|
||||
|
||||
make ldb -j32
|
||||
|
||||
if [ -e "build_detect_platform" ]
|
||||
then
|
||||
git checkout -- build_detect_platform
|
||||
fi
|
||||
|
||||
if [ -e "build_tools/build_detect_platform" ]
|
||||
then
|
||||
git checkout -- build_tools/build_detect_platform
|
||||
fi
|
||||
' > temp_build_ldb.sh
|
||||
|
||||
sed "s/make ldb -j32/source temp_build_ldb.sh/g" tools/check_format_compatible.sh > tools/temp_check_format_compatible.sh
|
||||
chmod +x tools/temp_check_format_compatible.sh
|
||||
tools/temp_check_format_compatible.sh
|
||||
tools/check_format_compatible.sh
|
||||
}
|
||||
|
||||
FORMAT_COMPATIBLE_COMMANDS="[
|
||||
@@ -777,9 +715,6 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
|
||||
|
||||
case $1 in
|
||||
punit)
|
||||
echo $PARALLEL_UNIT_TEST_COMMANDS
|
||||
;;
|
||||
unit)
|
||||
echo $UNIT_TEST_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -52,45 +52,6 @@ function get_lib_base()
|
||||
log_variable $__res_var
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# platform007 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 7.x platform007
|
||||
get_lib_base glibc 2.26 platform007
|
||||
get_lib_base snappy LATEST platform007
|
||||
get_lib_base zlib LATEST platform007
|
||||
get_lib_base bzip2 LATEST platform007
|
||||
get_lib_base lz4 LATEST platform007
|
||||
get_lib_base zstd LATEST platform007
|
||||
get_lib_base gflags LATEST platform007
|
||||
get_lib_base jemalloc LATEST platform007
|
||||
get_lib_base numa LATEST platform007
|
||||
get_lib_base libunwind LATEST platform007
|
||||
get_lib_base tbb LATEST platform007
|
||||
|
||||
get_lib_base kernel-headers fb platform007
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform007
|
||||
get_lib_base lua 5.3.4 platform007
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
###########################################################
|
||||
|
||||
Vendored
+5
@@ -27,6 +27,11 @@ std::shared_ptr<Cache> NewClockCache(size_t capacity, int num_shard_bits,
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
|
||||
// "tbb/concurrent_hash_map.h" requires RTTI if exception is enabled.
|
||||
// Disable it so users can chooose to disable RTTI.
|
||||
#ifndef ROCKSDB_USE_RTTI
|
||||
#define TBB_USE_EXCEPTIONS 0
|
||||
#endif
|
||||
#include "tbb/concurrent_hash_map.h"
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
|
||||
Vendored
+38
-5
@@ -22,7 +22,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
LRUHandleTable::LRUHandleTable() : length_(0), elems_(0), list_(nullptr) {
|
||||
LRUHandleTable::LRUHandleTable() : list_(nullptr), length_(0), elems_(0) {
|
||||
Resize();
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ void LRUHandleTable::Resize() {
|
||||
}
|
||||
|
||||
LRUCacheShard::LRUCacheShard()
|
||||
: usage_(0), lru_usage_(0), high_pri_pool_usage_(0) {
|
||||
: high_pri_pool_usage_(0), usage_(0), lru_usage_(0) {
|
||||
// Make empty circular linked list
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
@@ -157,6 +157,16 @@ void LRUCacheShard::TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri) {
|
||||
*lru_low_pri = lru_low_pri_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::TEST_GetLRUSize() {
|
||||
LRUHandle* lru_handle = lru_.next;
|
||||
size_t lru_size = 0;
|
||||
while (lru_handle != &lru_) {
|
||||
lru_size++;
|
||||
lru_handle = lru_handle->next;
|
||||
}
|
||||
return lru_size;
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
||||
assert(e->next != nullptr);
|
||||
assert(e->prev != nullptr);
|
||||
@@ -223,6 +233,14 @@ void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
}
|
||||
}
|
||||
|
||||
void* LRUCacheShard::operator new(size_t size) {
|
||||
return rocksdb::port::cacheline_aligned_alloc(size);
|
||||
}
|
||||
|
||||
void LRUCacheShard::operator delete(void *memblock) {
|
||||
rocksdb::port::cacheline_aligned_free(memblock);
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
@@ -438,11 +456,18 @@ std::string LRUCacheShard::GetPrintableOptions() const {
|
||||
LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
||||
int num_shards = 1 << num_shard_bits;
|
||||
shards_ = new LRUCacheShard[num_shards];
|
||||
num_shards_ = 1 << num_shard_bits;
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4316) // We've validated the alignment with the new operators
|
||||
#endif
|
||||
shards_ = new LRUCacheShard[num_shards_];
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
SetCapacity(capacity);
|
||||
SetStrictCapacityLimit(strict_capacity_limit);
|
||||
for (int i = 0; i < num_shards; i++) {
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
shards_[i].SetHighPriorityPoolRatio(high_pri_pool_ratio);
|
||||
}
|
||||
}
|
||||
@@ -471,6 +496,14 @@ uint32_t LRUCache::GetHash(Handle* handle) const {
|
||||
|
||||
void LRUCache::DisownData() { shards_ = nullptr; }
|
||||
|
||||
size_t LRUCache::TEST_GetLRUSize() {
|
||||
size_t lru_size_of_all_shards = 0;
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
lru_size_of_all_shards += shards_[i].TEST_GetLRUSize();
|
||||
}
|
||||
return lru_size_of_all_shards;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio) {
|
||||
|
||||
Vendored
+37
-13
@@ -148,13 +148,13 @@ class LRUHandleTable {
|
||||
|
||||
// The table consists of an array of buckets where each bucket is
|
||||
// a linked list of cache entries that hash into the bucket.
|
||||
LRUHandle** list_;
|
||||
uint32_t length_;
|
||||
uint32_t elems_;
|
||||
LRUHandle** list_;
|
||||
};
|
||||
|
||||
// A single shard of sharded cache.
|
||||
class LRUCacheShard : public CacheShard {
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
public:
|
||||
LRUCacheShard();
|
||||
virtual ~LRUCacheShard();
|
||||
@@ -198,6 +198,15 @@ class LRUCacheShard : public CacheShard {
|
||||
|
||||
void TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri);
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
// not threadsafe
|
||||
size_t TEST_GetLRUSize();
|
||||
|
||||
// Overloading to aligned it to cache line size
|
||||
void* operator new(size_t);
|
||||
|
||||
void operator delete(void *);
|
||||
|
||||
private:
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Insert(LRUHandle* e);
|
||||
@@ -219,12 +228,6 @@ class LRUCacheShard : public CacheShard {
|
||||
// Initialized before use.
|
||||
size_t capacity_;
|
||||
|
||||
// Memory size for entries residing in the cache
|
||||
size_t usage_;
|
||||
|
||||
// Memory size for entries residing only in the LRU list
|
||||
size_t lru_usage_;
|
||||
|
||||
// Memory size for entries in high-pri pool.
|
||||
size_t high_pri_pool_usage_;
|
||||
|
||||
@@ -238,11 +241,6 @@ class LRUCacheShard : public CacheShard {
|
||||
// Remember the value to avoid recomputing each time.
|
||||
double high_pri_pool_capacity_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
// We don't count mutex_ as the cache's internal state so semantically we
|
||||
// don't mind mutex_ invoking the non-const actions.
|
||||
mutable port::Mutex mutex_;
|
||||
|
||||
// Dummy head of LRU list.
|
||||
// lru.prev is newest entry, lru.next is oldest entry.
|
||||
// LRU contains items which can be evicted, ie reference only by cache
|
||||
@@ -251,7 +249,29 @@ class LRUCacheShard : public CacheShard {
|
||||
// Pointer to head of low-pri pool in LRU list.
|
||||
LRUHandle* lru_low_pri_;
|
||||
|
||||
// ------------^^^^^^^^^^^^^-----------
|
||||
// Not frequently modified data members
|
||||
// ------------------------------------
|
||||
//
|
||||
// We separate data members that are updated frequently from the ones that
|
||||
// are not frequently updated so that they don't share the same cache line
|
||||
// which will lead into false cache sharing
|
||||
//
|
||||
// ------------------------------------
|
||||
// Frequently modified data members
|
||||
// ------------vvvvvvvvvvvvv-----------
|
||||
LRUHandleTable table_;
|
||||
|
||||
// Memory size for entries residing in the cache
|
||||
size_t usage_;
|
||||
|
||||
// Memory size for entries residing only in the LRU list
|
||||
size_t lru_usage_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
// We don't count mutex_ as the cache's internal state so semantically we
|
||||
// don't mind mutex_ invoking the non-const actions.
|
||||
mutable port::Mutex mutex_;
|
||||
};
|
||||
|
||||
class LRUCache : public ShardedCache {
|
||||
@@ -267,8 +287,12 @@ class LRUCache : public ShardedCache {
|
||||
virtual uint32_t GetHash(Handle* handle) const override;
|
||||
virtual void DisownData() override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
size_t TEST_GetLRUSize();
|
||||
|
||||
private:
|
||||
LRUCacheShard* shards_;
|
||||
int num_shards_ = 0;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
Vendored
+10
-1
@@ -17,7 +17,16 @@ class LRUCacheTest : public testing::Test {
|
||||
~LRUCacheTest() {}
|
||||
|
||||
void NewCache(size_t capacity, double high_pri_pool_ratio = 0.0) {
|
||||
cache_.reset(new LRUCacheShard());
|
||||
cache_.reset(
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4316) // We've validated the alignment with the new operators
|
||||
#endif
|
||||
new LRUCacheShard()
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
);
|
||||
cache_->SetCapacity(capacity);
|
||||
cache_->SetStrictCapacityLimit(false);
|
||||
cache_->SetHighPriorityPoolRatio(high_pri_pool_ratio);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit on error.
|
||||
set -e
|
||||
|
||||
+8
-6
@@ -45,9 +45,9 @@ TableBuilder* NewTableBuilder(
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
int level,
|
||||
const std::string* compression_dict, const bool skip_filters) {
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const std::string* compression_dict, const bool skip_filters,
|
||||
const uint64_t creation_time) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -55,7 +55,7 @@ TableBuilder* NewTableBuilder(
|
||||
TableBuilderOptions(ioptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, compression_dict, skip_filters,
|
||||
column_family_name, level),
|
||||
column_family_name, level, creation_time),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@ Status BuildTable(
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, int level) {
|
||||
TableProperties* table_properties, int level,
|
||||
const uint64_t creation_time) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -123,7 +124,8 @@ Status BuildTable(
|
||||
builder = NewTableBuilder(
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
column_family_id, column_family_name, file_writer.get(), compression,
|
||||
compression_opts, level);
|
||||
compression_opts, level, nullptr /* compression_dict */,
|
||||
false /* skip_filters */, creation_time);
|
||||
}
|
||||
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
|
||||
+4
-4
@@ -48,10 +48,9 @@ TableBuilder* NewTableBuilder(
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
int level,
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const std::string* compression_dict = nullptr,
|
||||
const bool skip_filters = false);
|
||||
const bool skip_filters = false, const uint64_t creation_time = 0);
|
||||
|
||||
// Build a Table file from the contents of *iter. The generated file
|
||||
// will be named according to number specified in meta. On success, the rest of
|
||||
@@ -77,6 +76,7 @@ extern Status BuildTable(
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr, int level = -1);
|
||||
TableProperties* table_properties = nullptr, int level = -1,
|
||||
const uint64_t creation_time = 0);
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -52,6 +52,7 @@ using rocksdb::CompressionType;
|
||||
using rocksdb::WALRecoveryMode;
|
||||
using rocksdb::DB;
|
||||
using rocksdb::DBOptions;
|
||||
using rocksdb::DbPath;
|
||||
using rocksdb::Env;
|
||||
using rocksdb::EnvOptions;
|
||||
using rocksdb::InfoLogLevel;
|
||||
@@ -382,6 +383,10 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
}
|
||||
};
|
||||
|
||||
struct rocksdb_dbpath_t {
|
||||
DbPath rep;
|
||||
};
|
||||
|
||||
struct rocksdb_env_t {
|
||||
Env* rep;
|
||||
bool is_default;
|
||||
@@ -1372,24 +1377,23 @@ void rocksdb_writebatch_put_log_data(
|
||||
b->rep.PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
|
||||
void rocksdb_writebatch_iterate(
|
||||
rocksdb_writebatch_t* b,
|
||||
void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen)) {
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
H handler;
|
||||
handler.state_ = state;
|
||||
handler.put_ = put;
|
||||
@@ -1634,6 +1638,18 @@ void rocksdb_writebatch_wi_iterate(
|
||||
void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen)) {
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
H handler;
|
||||
handler.state_ = state;
|
||||
handler.put_ = put;
|
||||
@@ -1998,6 +2014,16 @@ void rocksdb_options_set_paranoid_checks(
|
||||
opt->rep.paranoid_checks = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
size_t num_paths) {
|
||||
std::vector<DbPath> db_paths(num_paths);
|
||||
for (size_t i = 0; i < num_paths; ++i) {
|
||||
db_paths[i] = dbpath_values[i]->rep;
|
||||
}
|
||||
opt->rep.db_paths = db_paths;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
|
||||
opt->rep.env = (env ? env->rep : nullptr);
|
||||
}
|
||||
@@ -2076,6 +2102,10 @@ void rocksdb_options_enable_statistics(rocksdb_options_t* opt) {
|
||||
opt->rep.statistics = rocksdb::CreateDBStatistics();
|
||||
}
|
||||
|
||||
void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt, unsigned char val) {
|
||||
opt->rep.skip_stats_update_on_db_open = val;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_num_levels(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.num_levels = n;
|
||||
}
|
||||
@@ -2778,6 +2808,17 @@ size_t rocksdb_cache_get_pinned_usage(rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetPinnedUsage();
|
||||
}
|
||||
|
||||
rocksdb_dbpath_t* rocksdb_dbpath_create(const char* path, uint64_t target_size) {
|
||||
rocksdb_dbpath_t* result = new rocksdb_dbpath_t;
|
||||
result->rep.path = std::string(path);
|
||||
result->rep.target_size = target_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_dbpath_destroy(rocksdb_dbpath_t* dbpath) {
|
||||
delete dbpath;
|
||||
}
|
||||
|
||||
rocksdb_env_t* rocksdb_create_default_env() {
|
||||
rocksdb_env_t* result = new rocksdb_env_t;
|
||||
result->rep = Env::Default();
|
||||
@@ -2955,21 +2996,20 @@ void rocksdb_slicetransform_destroy(rocksdb_slicetransform_t* st) {
|
||||
delete st;
|
||||
}
|
||||
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t prefixLen) {
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = rocksdb::NewFixedPrefixTransform(prefixLen);
|
||||
wrapper->state_ = nullptr;
|
||||
@@ -2978,6 +3018,19 @@ rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t pref
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create_noop() {
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = rocksdb::NewNoopTransform();
|
||||
wrapper->state_ = nullptr;
|
||||
@@ -3200,6 +3253,17 @@ void rocksdb_transaction_options_set_max_write_batch_size(
|
||||
opt->rep.max_write_batch_size = size;
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_transactiondb_create_column_family(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_options_t* column_family_options,
|
||||
const char* column_family_name, char** errptr) {
|
||||
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
|
||||
SaveError(errptr, txn_db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
return handle;
|
||||
}
|
||||
|
||||
rocksdb_transactiondb_t* rocksdb_transactiondb_open(
|
||||
const rocksdb_options_t* options,
|
||||
const rocksdb_transactiondb_options_t* txn_db_options, const char* name,
|
||||
@@ -3276,6 +3340,27 @@ char* rocksdb_transaction_get(rocksdb_transaction_t* txn,
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_transaction_get_cf(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, size_t* vlen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s =
|
||||
txn->rep->Get(options->rep, column_family->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,
|
||||
@@ -3298,6 +3383,26 @@ char* rocksdb_transactiondb_get(
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_transactiondb_get_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, size_t* vallen, char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = txn_db->rep->Get(options->rep, column_family->rep,
|
||||
Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vallen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vallen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -3305,6 +3410,14 @@ void rocksdb_transaction_put(rocksdb_transaction_t* txn, const char* key,
|
||||
SaveError(errptr, txn->rep->Put(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_put_cf(rocksdb_transaction_t* txn,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Put(column_family->rep, 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,
|
||||
@@ -3314,12 +3427,37 @@ void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
|
||||
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_put_cf(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Put(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
//Write batch into transaction db
|
||||
void rocksdb_transactiondb_write(
|
||||
rocksdb_transactiondb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_t* batch,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
// 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)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_delete_cf(
|
||||
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Delete(column_family->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
// Delete a key outside a transaction
|
||||
void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -3327,6 +3465,14 @@ void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, Slice(key, klen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_delete_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
// Create an iterator inside a transaction
|
||||
rocksdb_iterator_t* rocksdb_transaction_create_iterator(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options) {
|
||||
|
||||
+64
@@ -41,6 +41,7 @@ static char dbname[200];
|
||||
static char sstfilename[200];
|
||||
static char dbbackupname[200];
|
||||
static char dbcheckpointname[200];
|
||||
static char dbpathname[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
@@ -347,10 +348,25 @@ static void CheckTxnDBGet(
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGetCF(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transactiondb_get_cf(txn_db, options, column_family, 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;
|
||||
rocksdb_cache_t* cache;
|
||||
rocksdb_dbpath_t *dbpath;
|
||||
rocksdb_env_t* env;
|
||||
rocksdb_options_t* options;
|
||||
rocksdb_compactoptions_t* coptions;
|
||||
@@ -385,8 +401,14 @@ int main(int argc, char** argv) {
|
||||
GetTempDir(),
|
||||
((int)geteuid()));
|
||||
|
||||
snprintf(dbpathname, sizeof(dbpathname),
|
||||
"%s/rocksdb_c_test-%d-dbpath",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
StartPhase("create_objects");
|
||||
cmp = rocksdb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
|
||||
dbpath = rocksdb_dbpath_create(dbpathname, 1024 * 1024);
|
||||
env = rocksdb_create_default_env();
|
||||
cache = rocksdb_cache_create_lru(100000);
|
||||
|
||||
@@ -1357,6 +1379,18 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", NULL);
|
||||
|
||||
// write batch into TransactionDB
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_put(wb, "foo", 3, "a", 1);
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_put(wb, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put(wb, "box", 3, "c", 1);
|
||||
rocksdb_writebatch_delete(wb, "bar", 3);
|
||||
rocksdb_transactiondb_write(txn_db, woptions, wb, &err);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
CheckTxnDBGet(txn_db, roptions, "box", "c");
|
||||
CheckNoError(err);
|
||||
|
||||
// begin a transaction
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
// put
|
||||
@@ -1413,6 +1447,23 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "bar", NULL);
|
||||
|
||||
// Column families.
|
||||
rocksdb_column_family_handle_t* cfh;
|
||||
cfh = rocksdb_transactiondb_create_column_family(txn_db, options,
|
||||
"txn_db_cf", &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_transactiondb_put_cf(txn_db, woptions, cfh, "cf_foo", 6, "cf_hello",
|
||||
8, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", "cf_hello");
|
||||
|
||||
rocksdb_transactiondb_delete_cf(txn_db, woptions, cfh, "cf_foo", 6, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
|
||||
rocksdb_column_family_handle_destroy(cfh);
|
||||
|
||||
// close and destroy
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_transactiondb_close(txn_db);
|
||||
@@ -1440,6 +1491,18 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
// Simple sanity check that options setting db_paths work.
|
||||
StartPhase("open_db_paths");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
|
||||
const rocksdb_dbpath_t* paths[1] = {dbpath};
|
||||
rocksdb_options_set_db_paths(options, paths, 1);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
@@ -1449,6 +1512,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_compactoptions_destroy(coptions);
|
||||
rocksdb_cache_destroy(cache);
|
||||
rocksdb_comparator_destroy(cmp);
|
||||
rocksdb_dbpath_destroy(dbpath);
|
||||
rocksdb_env_destroy(env);
|
||||
|
||||
fprintf(stderr, "PASS\n");
|
||||
|
||||
+2
-2
@@ -353,8 +353,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
mutable_cf_options_(initial_cf_options_),
|
||||
is_delete_range_supported_(strcmp(cf_options.table_factory->Name(),
|
||||
BlockBasedTableFactory().Name()) == 0),
|
||||
is_delete_range_supported_(
|
||||
cf_options.table_factory->IsDeleteRangeSupported()),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
mem_(nullptr),
|
||||
imm_(ioptions_.min_write_buffer_number_to_merge,
|
||||
|
||||
@@ -302,6 +302,7 @@ class ColumnFamilyTest : public testing::Test {
|
||||
ASSERT_OK(Put(cf, key, RandomString(&rnd_, key_value_size - 10)));
|
||||
}
|
||||
}
|
||||
db_->FlushWAL(false);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE // TEST functions in DB are not supported in lite
|
||||
@@ -581,6 +582,7 @@ TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest) {
|
||||
Flush(0);
|
||||
ASSERT_OK(Put(1, "bar", "v3")); // seqID 4
|
||||
ASSERT_OK(Put(1, "foo", "v4")); // seqID 5
|
||||
db_->FlushWAL(false);
|
||||
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
@@ -643,6 +645,7 @@ TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest2) {
|
||||
// Write to log file D
|
||||
ASSERT_OK(Put(1, "bar", "v4")); // seqID 7
|
||||
ASSERT_OK(Put(1, "bar", "v5")); // seqID 8
|
||||
db_->FlushWAL(false);
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
|
||||
@@ -169,6 +169,7 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
bottommost_level_(IsBottommostLevel(output_level_, vstorage, inputs_)),
|
||||
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
|
||||
is_manual_compaction_(_manual_compaction),
|
||||
is_trivial_move_(false),
|
||||
compaction_reason_(_compaction_reason) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
@@ -463,4 +464,17 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Compaction::MaxInputFileCreationTime() const {
|
||||
uint64_t max_creation_time = 0;
|
||||
for (const auto& file : inputs_[0].files) {
|
||||
if (file->fd.table_reader != nullptr &&
|
||||
file->fd.table_reader->GetTableProperties() != nullptr) {
|
||||
uint64_t creation_time =
|
||||
file->fd.table_reader->GetTableProperties()->creation_time;
|
||||
max_creation_time = std::max(max_creation_time, creation_time);
|
||||
}
|
||||
}
|
||||
return max_creation_time;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+4
-1
@@ -99,7 +99,8 @@ class Compaction {
|
||||
// input level.
|
||||
// REQUIREMENT: "compaction_input_level" must be >= 0 and
|
||||
// < "input_levels()"
|
||||
const std::vector<FileMetaData*>* inputs(size_t compaction_input_level) {
|
||||
const std::vector<FileMetaData*>* inputs(
|
||||
size_t compaction_input_level) const {
|
||||
assert(compaction_input_level < inputs_.size());
|
||||
return &inputs_[compaction_input_level].files;
|
||||
}
|
||||
@@ -240,6 +241,8 @@ class Compaction {
|
||||
|
||||
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
|
||||
|
||||
uint64_t MaxInputFileCreationTime() const;
|
||||
|
||||
private:
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool mark_as_compacted);
|
||||
|
||||
+55
-13
@@ -594,6 +594,9 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
|
||||
double read_write_amp = 0.0;
|
||||
double write_amp = 0.0;
|
||||
double bytes_read_per_sec = 0;
|
||||
double bytes_written_per_sec = 0;
|
||||
|
||||
if (stats.bytes_read_non_output_levels > 0) {
|
||||
read_write_amp = (stats.bytes_written + stats.bytes_read_output_level +
|
||||
stats.bytes_read_non_output_levels) /
|
||||
@@ -601,17 +604,22 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
write_amp = stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
}
|
||||
if (stats.micros > 0) {
|
||||
bytes_read_per_sec =
|
||||
(stats.bytes_read_non_output_levels + stats.bytes_read_output_level) /
|
||||
static_cast<double>(stats.micros);
|
||||
bytes_written_per_sec =
|
||||
stats.bytes_written / static_cast<double>(stats.micros);
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
"files in(%d, %d) out(%d) "
|
||||
"MB in(%.1f, %.1f) out(%.1f), read-write-amplify(%.1f) "
|
||||
"write-amplify(%.1f) %s, records in: %d, records dropped: %d\n",
|
||||
cfd->GetName().c_str(), vstorage->LevelSummary(&tmp),
|
||||
(stats.bytes_read_non_output_levels + stats.bytes_read_output_level) /
|
||||
static_cast<double>(stats.micros),
|
||||
stats.bytes_written / static_cast<double>(stats.micros),
|
||||
compact_->compaction->output_level(),
|
||||
cfd->GetName().c_str(), vstorage->LevelSummary(&tmp), bytes_read_per_sec,
|
||||
bytes_written_per_sec, compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
stats.num_input_files_in_output_level, stats.num_output_files,
|
||||
stats.bytes_read_non_output_levels / 1048576.0,
|
||||
@@ -1023,7 +1031,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
uint64_t output_number = sub_compact->current_output()->meta.fd.GetNumber();
|
||||
assert(output_number != 0);
|
||||
|
||||
TableProperties table_properties;
|
||||
// Check for iterator errors
|
||||
Status s = input_status;
|
||||
auto meta = &sub_compact->current_output()->meta;
|
||||
@@ -1079,6 +1086,23 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
sub_compact->outfile.reset();
|
||||
|
||||
if (s.ok() && current_entries == 0) {
|
||||
// If there is nothing to output, no necessary to generate a sst file.
|
||||
// This happens when the output level is bottom level, at the same time
|
||||
// the sub_compact output nothing.
|
||||
std::string fname = TableFileName(
|
||||
db_options_.db_paths, meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
env_->DeleteFile(fname);
|
||||
|
||||
// Also need to remove the file from outputs, or it will be added to the
|
||||
// VersionEdit.
|
||||
assert(!sub_compact->outputs.empty());
|
||||
sub_compact->outputs.pop_back();
|
||||
sub_compact->builder.reset();
|
||||
sub_compact->current_output_file_size = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
TableProperties tp;
|
||||
if (s.ok() && current_entries > 0) {
|
||||
@@ -1131,12 +1155,22 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
meta->fd.GetPathId());
|
||||
sfm->OnAddFile(fn);
|
||||
if (sfm->IsMaxAllowedSpaceReached()) {
|
||||
// TODO(ajkr): should we return OK() if max space was reached by the final
|
||||
// compaction output file (similarly to how flush works when full)?
|
||||
s = Status::IOError("Max allowed space was reached");
|
||||
TEST_SYNC_POINT(
|
||||
"CompactionJob::FinishCompactionOutputFile:"
|
||||
"MaxAllowedSpaceReached");
|
||||
InstrumentedMutexLock l(db_mutex_);
|
||||
if (db_bg_error_->ok()) {
|
||||
s = Status::IOError("Max allowed space was reached");
|
||||
*db_bg_error_ = s;
|
||||
TEST_SYNC_POINT(
|
||||
"CompactionJob::FinishCompactionOutputFile:MaxAllowedSpaceReached");
|
||||
Status new_bg_error = s;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(
|
||||
cfd->ioptions()->listeners, BackgroundErrorReason::kCompaction,
|
||||
&new_bg_error, db_mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
*db_bg_error_ = new_bg_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1251,14 +1285,22 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
// data is going to be found
|
||||
bool skip_filters =
|
||||
cfd->ioptions()->optimize_filters_for_hits && bottommost_level_;
|
||||
|
||||
uint64_t output_file_creation_time =
|
||||
sub_compact->compaction->MaxInputFileCreationTime();
|
||||
if (output_file_creation_time == 0) {
|
||||
int64_t _current_time = 0;
|
||||
db_options_.env->GetCurrentTime(&_current_time); // ignore error
|
||||
output_file_creation_time = static_cast<uint64_t>(_current_time);
|
||||
}
|
||||
|
||||
sub_compact->builder.reset(NewTableBuilder(
|
||||
*cfd->ioptions(), cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
sub_compact->outfile.get(), sub_compact->compaction->output_compression(),
|
||||
cfd->ioptions()->compression_opts,
|
||||
sub_compact->compaction->output_level(),
|
||||
&sub_compact->compression_dict,
|
||||
skip_filters));
|
||||
sub_compact->compaction->output_level(), &sub_compact->compression_dict,
|
||||
skip_filters, output_file_creation_time));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
|
||||
void SetLastSequence(const SequenceNumber sequence_number) {
|
||||
versions_->SetLastToBeWrittenSequence(sequence_number + 1);
|
||||
versions_->SetLastSequence(sequence_number + 1);
|
||||
}
|
||||
|
||||
@@ -337,6 +338,24 @@ TEST_F(CompactionJobTest, SimpleDeletion) {
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, OutputNothing) {
|
||||
NewDB();
|
||||
|
||||
auto file1 = mock::MakeMockFile({{KeyStr("a", 1U, kTypeValue), "val"}});
|
||||
|
||||
AddMockFile(file1);
|
||||
|
||||
auto file2 = mock::MakeMockFile({{KeyStr("a", 2U, kTypeDeletion), ""}});
|
||||
|
||||
AddMockFile(file2);
|
||||
|
||||
auto expected_results = mock::MakeMockFile();
|
||||
|
||||
SetLastSequence(4U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, SimpleOverwrite) {
|
||||
NewDB();
|
||||
|
||||
|
||||
+130
-39
@@ -234,11 +234,6 @@ bool CompactionPicker::ExpandInputsToCleanCut(const std::string& cf_name,
|
||||
// If, after the expansion, there are files that are already under
|
||||
// compaction, then we must drop/cancel this compaction.
|
||||
if (AreFilesInCompaction(inputs->files)) {
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] ExpandWhileOverlapping() failure because some of the necessary"
|
||||
" compaction input files are currently being compacted.",
|
||||
cf_name.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1106,11 +1101,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
}
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
if (PickFileToCompact() &&
|
||||
compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_) &&
|
||||
!compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_)) {
|
||||
if (PickFileToCompact()) {
|
||||
// found the compaction!
|
||||
if (start_level_ == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
@@ -1332,12 +1323,10 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
|
||||
// record the first file that is not yet compacted
|
||||
int nextIndex = -1;
|
||||
|
||||
for (unsigned int i = vstorage_->NextCompactionIndex(start_level_);
|
||||
i < file_size.size(); i++) {
|
||||
int index = file_size[i];
|
||||
unsigned int cmp_idx;
|
||||
for (cmp_idx = vstorage_->NextCompactionIndex(start_level_);
|
||||
cmp_idx < file_size.size(); cmp_idx++) {
|
||||
int index = file_size[cmp_idx];
|
||||
auto* f = level_files[index];
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
@@ -1346,27 +1335,42 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// remember the startIndex for the next call to PickCompaction
|
||||
if (nextIndex == -1) {
|
||||
nextIndex = i;
|
||||
}
|
||||
|
||||
// Do not pick this file if its parents at level+1 are being compacted.
|
||||
// Maybe we can avoid redoing this work in SetupOtherInputs
|
||||
parent_index_ = -1;
|
||||
if (compaction_picker_->IsRangeInCompaction(vstorage_, &f->smallest,
|
||||
&f->largest, output_level_,
|
||||
&parent_index_)) {
|
||||
continue;
|
||||
}
|
||||
start_level_inputs_.files.push_back(f);
|
||||
start_level_inputs_.level = start_level_;
|
||||
if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_)) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that input level is fully expanded, we check whether any output files
|
||||
// are locked due to pending compaction.
|
||||
//
|
||||
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
|
||||
// level files are locked, not just the extra ones pulled in for user-key
|
||||
// overlap.
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (!output_level_inputs.empty() &&
|
||||
!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&output_level_inputs)) {
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
base_index_ = index;
|
||||
break;
|
||||
}
|
||||
|
||||
// store where to start the iteration in the next call to PickCompaction
|
||||
vstorage_->SetNextCompactionIndex(start_level_, nextIndex);
|
||||
vstorage_->SetNextCompactionIndex(start_level_, cmp_idx);
|
||||
|
||||
return start_level_inputs_.size() > 0;
|
||||
}
|
||||
@@ -1403,16 +1407,87 @@ bool FIFOCompactionPicker::NeedsCompaction(
|
||||
return vstorage->CompactionScore(kLevel0) >= 1;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
namespace {
|
||||
uint64_t GetTotalFilesSize(
|
||||
const std::vector<FileMetaData*>& files) {
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& f : files) {
|
||||
total_size += f->fd.file_size;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(vstorage->num_levels() == 1);
|
||||
assert(ioptions_.compaction_options_fifo.ttl > 0);
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& file : level_files) {
|
||||
total_size += file->fd.file_size;
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions_.env->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
"Not doing compactions based on TTL. ",
|
||||
cf_name.c_str(), status.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
f->fd.table_reader->GetTableProperties() != nullptr) {
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >=
|
||||
(current_time - ioptions_.compaction_options_fifo.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a nullptr and proceed to size-based FIFO compaction if:
|
||||
// 1. there are no files older than ttl OR
|
||||
// 2. there are a few files older than ttl, but deleting them will not bring
|
||||
// the total size to be less than max_table_files_size threshold.
|
||||
if (inputs[0].files.empty() ||
|
||||
total_size > ioptions_.compaction_options_fifo.max_table_files_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(),
|
||||
f->fd.table_reader->GetTableProperties()->creation_time);
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, {}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size ||
|
||||
level_files.size() == 0) {
|
||||
@@ -1433,7 +1508,6 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -1458,24 +1532,41 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
// delete old files (FIFO)
|
||||
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
char tmp_fsize[16];
|
||||
AppendHumanBytes(f->fd.GetFileSize(), tmp_fsize, sizeof(tmp_fsize));
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with size %s for deletion",
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with size %s for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), tmp_fsize);
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, {}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(vstorage->num_levels() == 1);
|
||||
|
||||
Compaction* c = nullptr;
|
||||
if (ioptions_.compaction_options_fifo.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
c = PickSizeCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -242,6 +242,17 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
Compaction* PickTTLCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
Compaction* PickSizeCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
|
||||
class NullCompactionPicker : public CompactionPicker {
|
||||
|
||||
@@ -512,7 +512,7 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
kFileSize, 0, i * 100, i * 100 + 99);
|
||||
current_size += kFileSize;
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(level_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
vstorage_->CompactionScore(0) >= 1);
|
||||
}
|
||||
}
|
||||
@@ -852,6 +852,80 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys9) {
|
||||
ASSERT_EQ(8U, compaction->input(1, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys10) {
|
||||
// Locked file encountered when pulling in extra input-level files with same
|
||||
// user keys. Verify we pick the next-best file from the same input level.
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_compaction_bytes = 100000000000u;
|
||||
|
||||
// file_number 2U is largest and thus first choice. But it overlaps with
|
||||
// file_number 1U which is being compacted. So instead we pick the next-
|
||||
// biggest file, 3U, which is eligible for compaction.
|
||||
Add(1 /* level */, 1U /* file_number */, "100" /* smallest */,
|
||||
"150" /* largest */, 1U /* file_size */);
|
||||
file_map_[1U].first->being_compacted = true;
|
||||
Add(1 /* level */, 2U /* file_number */, "150" /* smallest */,
|
||||
"200" /* largest */, 1000000000U /* file_size */, 0 /* smallest_seq */,
|
||||
0 /* largest_seq */);
|
||||
Add(1 /* level */, 3U /* file_number */, "201" /* smallest */,
|
||||
"250" /* largest */, 900000000U /* file_size */);
|
||||
Add(2 /* level */, 4U /* file_number */, "100" /* smallest */,
|
||||
"150" /* largest */, 1U /* file_size */);
|
||||
Add(2 /* level */, 5U /* file_number */, "151" /* smallest */,
|
||||
"200" /* largest */, 1U /* file_size */);
|
||||
Add(2 /* level */, 6U /* file_number */, "201" /* smallest */,
|
||||
"250" /* largest */, 1U /* file_size */);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys11) {
|
||||
// Locked file encountered when pulling in extra output-level files with same
|
||||
// user keys. Expected to skip that compaction and pick the next-best choice.
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_compaction_bytes = 100000000000u;
|
||||
|
||||
// score(L1) = 3.7
|
||||
// score(L2) = 1.85
|
||||
// There is no eligible file in L1 to compact since both candidates pull in
|
||||
// file_number 5U, which overlaps with a file pending compaction (6U). The
|
||||
// first eligible compaction is from L2->L3.
|
||||
Add(1 /* level */, 2U /* file_number */, "151" /* smallest */,
|
||||
"200" /* largest */, 1000000000U /* file_size */);
|
||||
Add(1 /* level */, 3U /* file_number */, "201" /* smallest */,
|
||||
"250" /* largest */, 1U /* file_size */);
|
||||
Add(2 /* level */, 4U /* file_number */, "100" /* smallest */,
|
||||
"149" /* largest */, 5000000000U /* file_size */);
|
||||
Add(2 /* level */, 5U /* file_number */, "150" /* smallest */,
|
||||
"201" /* largest */, 1U /* file_size */);
|
||||
Add(2 /* level */, 6U /* file_number */, "201" /* smallest */,
|
||||
"249" /* largest */, 1U /* file_size */, 0 /* smallest_seq */,
|
||||
0 /* largest_seq */);
|
||||
file_map_[6U].first->being_compacted = true;
|
||||
Add(3 /* level */, 7U /* file_number */, "100" /* smallest */,
|
||||
"149" /* largest */, 1U /* file_size */);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(4U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NotScheduleL1IfL0WithHigherPri1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
@@ -1316,6 +1390,49 @@ TEST_F(CompactionPickerTest, IsTrivialMoveOff) {
|
||||
ASSERT_FALSE(compaction->IsTrivialMove());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CacheNextCompactionIndex) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_compaction_bytes = 100000000000u;
|
||||
|
||||
Add(1 /* level */, 1U /* file_number */, "100" /* smallest */,
|
||||
"149" /* largest */, 1000000000U /* file_size */);
|
||||
file_map_[1U].first->being_compacted = true;
|
||||
Add(1 /* level */, 2U /* file_number */, "150" /* smallest */,
|
||||
"199" /* largest */, 900000000U /* file_size */);
|
||||
Add(1 /* level */, 3U /* file_number */, "200" /* smallest */,
|
||||
"249" /* largest */, 800000000U /* file_size */);
|
||||
Add(1 /* level */, 4U /* file_number */, "250" /* smallest */,
|
||||
"299" /* largest */, 700000000U /* file_size */);
|
||||
Add(2 /* level */, 5U /* file_number */, "150" /* smallest */,
|
||||
"199" /* largest */, 1U /* file_size */);
|
||||
file_map_[5U].first->being_compacted = true;
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(0U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2, vstorage_->NextCompactionIndex(1 /* level */));
|
||||
|
||||
compaction.reset(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(0U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(4U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(3, vstorage_->NextCompactionIndex(1 /* level */));
|
||||
|
||||
compaction.reset(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
ASSERT_EQ(4, vstorage_->NextCompactionIndex(1 /* level */));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+33
-2
@@ -9,19 +9,50 @@
|
||||
#include "rocksdb/convenience.h"
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
void CancelAllBackgroundWork(DB* db, bool wait) {
|
||||
(dynamic_cast<DBImpl*>(db->GetRootDB()))->CancelAllBackgroundWork(wait);
|
||||
(static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
->CancelAllBackgroundWork(wait);
|
||||
}
|
||||
|
||||
Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
return (dynamic_cast<DBImpl*>(db->GetRootDB()))
|
||||
return (static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
->DeleteFilesInRange(column_family, begin, end);
|
||||
}
|
||||
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
const std::string& file_path) {
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
uint64_t file_size;
|
||||
InternalKeyComparator internal_comparator(options.comparator);
|
||||
ImmutableCFOptions ioptions(options);
|
||||
|
||||
Status s = ioptions.env->NewRandomAccessFile(file_path, &file, env_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions.env->GetFileSize(file_path, &file_size);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
unique_ptr<TableReader> table_reader;
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), file_path));
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions, env_options, internal_comparator,
|
||||
false /* skip_filters */, -1 /* level */),
|
||||
std::move(file_reader), file_size, &table_reader,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = table_reader->VerifyChecksum();
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
+13
-2
@@ -20,6 +20,7 @@
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
@@ -152,7 +153,7 @@ class CorruptionTest : public testing::Test {
|
||||
struct stat sbuf;
|
||||
if (stat(fname.c_str(), &sbuf) != 0) {
|
||||
const char* msg = strerror(errno);
|
||||
ASSERT_TRUE(false) << fname << ": " << msg;
|
||||
FAIL() << fname << ": " << msg;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
@@ -179,6 +180,9 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
s = WriteStringToFile(Env::Default(), contents, fname);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
Options options;
|
||||
EnvOptions env_options;
|
||||
ASSERT_NOK(VerifySstFileChecksum(options, env_options, fname));
|
||||
}
|
||||
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
@@ -213,7 +217,7 @@ class CorruptionTest : public testing::Test {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(false) << "no file found at level";
|
||||
FAIL() << "no file found at level";
|
||||
}
|
||||
|
||||
|
||||
@@ -312,6 +316,7 @@ TEST_F(CorruptionTest, TableFile) {
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
Check(99, 99);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
@@ -327,9 +332,11 @@ TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
// corrupt an index block of an entire file
|
||||
Corrupt(kTableFile, -2000, 500);
|
||||
Reopen();
|
||||
dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
// one full file should be readable, since only one was corrupted
|
||||
// the other file should be fully non-readable, since index was corrupted
|
||||
Check(5000, 5000);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, MissingDescriptor) {
|
||||
@@ -389,10 +396,12 @@ TEST_F(CorruptionTest, CompactionInputError) {
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
Check(9, 9);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
|
||||
// Force compactions by writing lots of values
|
||||
Build(10000);
|
||||
Check(10000, 10000);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
@@ -424,6 +433,7 @@ TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
|
||||
CorruptTableFileAtLevel(0, 100, 1);
|
||||
Check(9, 9);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
|
||||
// Write must eventually fail because of corrupted table
|
||||
Status s;
|
||||
@@ -445,6 +455,7 @@ TEST_F(CorruptionTest, UnrelatedKeys) {
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_FlushMemTable();
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
|
||||
std::string tmp1, tmp2;
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
|
||||
|
||||
@@ -175,6 +175,8 @@ TEST_F(DBBasicTest, LevelLimitReopen) {
|
||||
int i = 0;
|
||||
while (NumTableFilesAtLevel(2, 1) == 0) {
|
||||
ASSERT_OK(Put(1, Key(i++), value));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
options.num_levels = 1;
|
||||
@@ -822,6 +824,9 @@ TEST_F(DBBasicTest, ChecksumTest) {
|
||||
// sense to run
|
||||
#ifndef OS_WIN
|
||||
TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
if (!IsMemoryMappedAccessSupported()) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.use_direct_reads = true;
|
||||
|
||||
@@ -497,7 +497,7 @@ TEST_F(DBBlockCacheTest, CompressedCache) {
|
||||
options.compression = kNoCompression;
|
||||
break;
|
||||
default:
|
||||
ASSERT_TRUE(false);
|
||||
FAIL();
|
||||
}
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
// default column family doesn't have block cache
|
||||
@@ -560,7 +560,7 @@ TEST_F(DBBlockCacheTest, CompressedCache) {
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_COMPRESSED_HIT), 0);
|
||||
break;
|
||||
default:
|
||||
ASSERT_TRUE(false);
|
||||
FAIL();
|
||||
}
|
||||
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -1917,7 +1917,7 @@ TEST_F(DBCompactionTest, ManualAutoRace) {
|
||||
|
||||
// The auto compaction is scheduled but waited until here
|
||||
TEST_SYNC_POINT("DBCompactionTest::ManualAutoRace:1");
|
||||
// The auto compaction will wait until the the manual compaction is registerd
|
||||
// The auto compaction will wait until the manual compaction is registerd
|
||||
// before processing so that it will be cancelled.
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr);
|
||||
ASSERT_EQ("0,1", FilesPerLevel(1));
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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).
|
||||
//
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBEncryptionTest : public DBTestBase {
|
||||
public:
|
||||
DBEncryptionTest() : DBTestBase("/db_encryption_test") {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
ASSERT_OK(Put("foo567", "v1.fetdq"));
|
||||
ASSERT_OK(Put("bar123", "v2.dfgkjdfghsd"));
|
||||
Close();
|
||||
|
||||
// Open all files and look for the values we've put in there.
|
||||
// They should not be found if encrypted, otherwise
|
||||
// they should be found.
|
||||
std::vector<std::string> fileNames;
|
||||
auto status = env_->GetChildren(dbname_, &fileNames);
|
||||
ASSERT_OK(status);
|
||||
|
||||
auto defaultEnv = Env::Default();
|
||||
int hits = 0;
|
||||
for (auto it = fileNames.begin() ; it != fileNames.end(); ++it) {
|
||||
if ((*it == "..") || (*it == ".")) {
|
||||
continue;
|
||||
}
|
||||
auto filePath = dbname_ + "/" + *it;
|
||||
unique_ptr<SequentialFile> seqFile;
|
||||
auto envOptions = EnvOptions(CurrentOptions());
|
||||
status = defaultEnv->NewSequentialFile(filePath, &seqFile, envOptions);
|
||||
ASSERT_OK(status);
|
||||
|
||||
uint64_t fileSize;
|
||||
status = defaultEnv->GetFileSize(filePath, &fileSize);
|
||||
ASSERT_OK(status);
|
||||
|
||||
std::string scratch;
|
||||
scratch.reserve(fileSize);
|
||||
Slice data;
|
||||
status = seqFile->Read(fileSize, &data, (char*)scratch.data());
|
||||
ASSERT_OK(status);
|
||||
|
||||
if (data.ToString().find("foo567") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("v1.fetdq") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("bar123") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("v2.dfgkjdfghsd") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("dfgk") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
}
|
||||
if (encrypted_env_) {
|
||||
ASSERT_EQ(hits, 0);
|
||||
} else {
|
||||
ASSERT_GE(hits, 4);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+102
-15
@@ -67,6 +67,7 @@
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
@@ -80,6 +81,7 @@
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "tools/sst_dump_tool_imp.h"
|
||||
#include "util/auto_roll_logger.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/build_version.h"
|
||||
@@ -158,6 +160,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
is_snapshot_supported_(true),
|
||||
write_buffer_manager_(immutable_db_options_.write_buffer_manager.get()),
|
||||
write_thread_(immutable_db_options_),
|
||||
nonmem_write_thread_(immutable_db_options_),
|
||||
write_controller_(mutable_db_options_.delayed_write_rate),
|
||||
// Use delayed_write_rate as a base line to determine the initial
|
||||
// low pri write rate limit. It may be adjusted later.
|
||||
@@ -167,6 +170,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
last_batch_group_size_(0),
|
||||
unscheduled_flushes_(0),
|
||||
unscheduled_compactions_(0),
|
||||
bg_bottom_compaction_scheduled_(0),
|
||||
bg_compaction_scheduled_(0),
|
||||
num_running_compactions_(0),
|
||||
bg_flush_scheduled_(0),
|
||||
@@ -187,7 +191,9 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
bg_work_paused_(0),
|
||||
bg_compaction_paused_(0),
|
||||
refitting_level_(false),
|
||||
opened_successfully_(false) {
|
||||
opened_successfully_(false),
|
||||
concurrent_prepare_(options.concurrent_prepare),
|
||||
manual_wal_flush_(options.manual_wal_flush) {
|
||||
env_->GetAbsolutePath(dbname, &db_absolute_path_);
|
||||
|
||||
// Reserve ten files or so for other uses and give the rest to TableCache.
|
||||
@@ -239,7 +245,8 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
return;
|
||||
}
|
||||
// Wait for background work to finish
|
||||
while (bg_compaction_scheduled_ || bg_flush_scheduled_) {
|
||||
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
@@ -249,15 +256,18 @@ DBImpl::~DBImpl() {
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
CancelAllBackgroundWork(false);
|
||||
int bottom_compactions_unscheduled =
|
||||
env_->UnSchedule(this, Env::Priority::BOTTOM);
|
||||
int compactions_unscheduled = env_->UnSchedule(this, Env::Priority::LOW);
|
||||
int flushes_unscheduled = env_->UnSchedule(this, Env::Priority::HIGH);
|
||||
mutex_.Lock();
|
||||
bg_bottom_compaction_scheduled_ -= bottom_compactions_unscheduled;
|
||||
bg_compaction_scheduled_ -= compactions_unscheduled;
|
||||
bg_flush_scheduled_ -= flushes_unscheduled;
|
||||
|
||||
// Wait for background work to finish
|
||||
while (bg_compaction_scheduled_ || bg_flush_scheduled_ ||
|
||||
bg_purge_scheduled_) {
|
||||
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_) {
|
||||
TEST_SYNC_POINT("DBImpl::~DBImpl:WaitJob");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
@@ -610,6 +620,26 @@ int DBImpl::FindMinimumEmptyLevelFitting(ColumnFamilyData* cfd,
|
||||
return minimum_level;
|
||||
}
|
||||
|
||||
Status DBImpl::FlushWAL(bool sync) {
|
||||
{
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
auto s = cur_log_writer->WriteBuffer();
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL flush error %s",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
if (!sync) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "FlushWAL sync=false");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
// sync = true
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "FlushWAL sync=true");
|
||||
return SyncWAL();
|
||||
}
|
||||
|
||||
Status DBImpl::SyncWAL() {
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
bool need_log_dir_sync;
|
||||
@@ -648,6 +678,7 @@ Status DBImpl::SyncWAL() {
|
||||
need_log_dir_sync = !log_dir_synced_;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
Status status;
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
@@ -659,6 +690,7 @@ Status DBImpl::SyncWAL() {
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
status = directories_.GetWalDir()->Fsync();
|
||||
}
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
{
|
||||
@@ -1377,8 +1409,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
return NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), iter,
|
||||
kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number);
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations);
|
||||
#endif
|
||||
} else {
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
@@ -1433,9 +1464,10 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
// likely that any iterator pointer is close to the iterator it points to so
|
||||
// that they are likely to be in the same cache line and/or page.
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), snapshot,
|
||||
env_, read_options, *cfd->ioptions(), snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number);
|
||||
sv->version_number,
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd);
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
@@ -1486,8 +1518,7 @@ Status DBImpl::NewIterators(
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), iter,
|
||||
kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number));
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
@@ -1505,9 +1536,10 @@ Status DBImpl::NewIterators(
|
||||
: latest_snapshot;
|
||||
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(),
|
||||
snapshot, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number);
|
||||
env_, read_options, *cfd->ioptions(), snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number,
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd);
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator());
|
||||
@@ -2268,7 +2300,7 @@ Status DestroyDB(const std::string& dbname, const Options& options) {
|
||||
// Delete log files in the WAL dir
|
||||
for (const auto& file : walDirFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del = env->DeleteFile(soptions.wal_dir + "/" + file);
|
||||
Status del = env->DeleteFile(LogFileName(soptions.wal_dir, number));
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -2632,9 +2664,13 @@ Status DBImpl::IngestExternalFile(
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:MutexLock");
|
||||
|
||||
// Stop writes to the DB
|
||||
// Stop writes to the DB by entering both write threads
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (concurrent_prepare_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
num_running_ingest_file_++;
|
||||
|
||||
@@ -2675,6 +2711,9 @@ Status DBImpl::IngestExternalFile(
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
if (concurrent_prepare_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
|
||||
// Update stats
|
||||
@@ -2703,6 +2742,54 @@ Status DBImpl::IngestExternalFile(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyChecksum() {
|
||||
Status s;
|
||||
Options options;
|
||||
EnvOptions env_options;
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized()) {
|
||||
cfd->Ref();
|
||||
cfd_list.push_back(cfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<SuperVersion*> sv_list;
|
||||
for (auto cfd : cfd_list) {
|
||||
sv_list.push_back(cfd->GetReferencedSuperVersion(&mutex_));
|
||||
}
|
||||
for (auto& sv : sv_list) {
|
||||
VersionStorageInfo* vstorage = sv->current->storage_info();
|
||||
for (int i = 0; i < vstorage->num_non_empty_levels() && s.ok(); i++) {
|
||||
for (size_t j = 0; j < vstorage->LevelFilesBrief(i).num_files && s.ok();
|
||||
j++) {
|
||||
const auto& fd = vstorage->LevelFilesBrief(i).files[j].fd;
|
||||
std::string fname = TableFileName(immutable_db_options_.db_paths,
|
||||
fd.GetNumber(), fd.GetPathId());
|
||||
s = rocksdb::VerifySstFileChecksum(options, env_options, fname);
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto sv : sv_list) {
|
||||
if (sv && sv->Unref()) {
|
||||
sv->Cleanup();
|
||||
delete sv;
|
||||
}
|
||||
}
|
||||
for (auto cfd : cfd_list) {
|
||||
cfd->Unref();
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnExternalFileIngested(
|
||||
ColumnFamilyData* cfd, const ExternalSstFileIngestionJob& ingestion_job) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
+107
-32
@@ -197,6 +197,7 @@ class DBImpl : public DB {
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status FlushWAL(bool sync) override;
|
||||
virtual Status SyncWAL() override;
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
@@ -234,11 +235,11 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
|
||||
// experimental API
|
||||
Status SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
const Slice* begin, const Slice* end) override;
|
||||
|
||||
Status PromoteL0(ColumnFamilyHandle* column_family, int target_level);
|
||||
Status PromoteL0(ColumnFamilyHandle* column_family,
|
||||
int target_level) override;
|
||||
|
||||
// Similar to Write() but will call the callback once on the single write
|
||||
// thread to determine whether it is safe to perform the write.
|
||||
@@ -292,6 +293,8 @@ class DBImpl : public DB {
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
virtual Status VerifyChecksum() override;
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Similar to GetSnapshot(), but also lets the db know that this snapshot
|
||||
@@ -493,6 +496,12 @@ class DBImpl : public DB {
|
||||
|
||||
const WriteController& write_controller() { return write_controller_; }
|
||||
|
||||
InternalIterator* NewInternalIterator(const ReadOptions&,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
// hollow transactions shell used for recovery.
|
||||
// these will then be passed to TransactionDB so that
|
||||
// locks can be reacquired before writing can resume.
|
||||
@@ -551,6 +560,7 @@ class DBImpl : public DB {
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
InstrumentedMutex* mutex() { return &mutex_; }
|
||||
|
||||
Status NewDB();
|
||||
|
||||
@@ -565,12 +575,6 @@ class DBImpl : public DB {
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
recovered_transactions_;
|
||||
|
||||
InternalIterator* NewInternalIterator(const ReadOptions&,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
// Except in DB::Open(), WriteOptionsFile can only be called when:
|
||||
// Persist options to options file.
|
||||
// If need_mutex_lock = false, the method will lock DB mutex.
|
||||
@@ -619,13 +623,19 @@ class DBImpl : public DB {
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false);
|
||||
|
||||
Status WriteImplWALOnly(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0);
|
||||
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
uint64_t FindMinPrepLogReferencedByMemTable();
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
friend class InternalStats;
|
||||
friend class TransactionImpl;
|
||||
friend class PessimisticTransaction;
|
||||
friend class WriteCommittedTxn;
|
||||
friend class WritePreparedTxn;
|
||||
#ifndef ROCKSDB_LITE
|
||||
friend class ForwardIterator;
|
||||
#endif
|
||||
@@ -650,6 +660,7 @@ class DBImpl : public DB {
|
||||
}
|
||||
};
|
||||
|
||||
struct PrepickedCompaction;
|
||||
struct PurgeFileInfo;
|
||||
|
||||
// Recover the descriptor from persistent storage. May do a significant
|
||||
@@ -744,12 +755,23 @@ class DBImpl : public DB {
|
||||
Status PreprocessWrite(const WriteOptions& write_options, bool* need_log_sync,
|
||||
WriteContext* write_context);
|
||||
|
||||
WriteBatch* MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal);
|
||||
|
||||
Status WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
|
||||
Status WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, bool need_log_sync,
|
||||
bool need_log_dir_sync, SequenceNumber sequence);
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence);
|
||||
|
||||
Status ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used, SequenceNumber* last_sequence,
|
||||
int total_count);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void ParanoidCheck(const Status& status);
|
||||
void WriteCallbackStatusCheck(const Status& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
@@ -780,14 +802,19 @@ class DBImpl : public DB {
|
||||
void SchedulePendingPurge(std::string fname, FileType type, uint64_t number,
|
||||
uint32_t path_id, int job_id);
|
||||
static void BGWorkCompaction(void* arg);
|
||||
// Runs a pre-chosen universal compaction involving bottom level in a
|
||||
// separate, bottom-pri thread pool.
|
||||
static void BGWorkBottomCompaction(void* arg);
|
||||
static void BGWorkFlush(void* db);
|
||||
static void BGWorkPurge(void* arg);
|
||||
static void UnscheduleCallback(void* arg);
|
||||
void BackgroundCallCompaction(void* arg);
|
||||
void BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
Env::Priority bg_thread_pri);
|
||||
void BackgroundCallFlush();
|
||||
void BackgroundCallPurge();
|
||||
Status BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer, void* m = 0);
|
||||
LogBuffer* log_buffer,
|
||||
PrepickedCompaction* prepicked_compaction);
|
||||
Status BackgroundFlush(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -825,11 +852,16 @@ class DBImpl : public DB {
|
||||
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
|
||||
FileLock* db_lock_;
|
||||
|
||||
// The mutex for options file related operations.
|
||||
// NOTE: should never acquire options_file_mutex_ and mutex_ at the
|
||||
// same time.
|
||||
InstrumentedMutex options_files_mutex_;
|
||||
// In addition to mutex_, log_write_mutex_ protected writes to logs_ and
|
||||
// logfile_number_. With concurrent_prepare it also protects alive_log_files_,
|
||||
// and log_empty_. Refer to the definition of each variable below for more
|
||||
// details.
|
||||
InstrumentedMutex log_write_mutex_;
|
||||
// State below is protected by mutex_
|
||||
// With concurrent_prepare enabled, some of the variables that accessed during
|
||||
// WriteToWAL need different synchronization: log_empty_, alive_log_files_,
|
||||
// logs_, logfile_number_. Refer to the definition of each variable below for
|
||||
// more description.
|
||||
mutable InstrumentedMutex mutex_;
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
@@ -843,10 +875,20 @@ class DBImpl : public DB {
|
||||
// * whenever there is an error in background purge, flush or compaction
|
||||
// * whenever num_running_ingest_file_ goes to 0.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
// Writes are protected by locking both mutex_ and log_write_mutex_, and reads
|
||||
// must be under either mutex_ or log_write_mutex_. Since after ::Open,
|
||||
// logfile_number_ is currently updated only in write_thread_, it can be read
|
||||
// from the same write_thread_ without any locks.
|
||||
uint64_t logfile_number_;
|
||||
std::deque<uint64_t>
|
||||
log_recycle_files; // a list of log files that we can recycle
|
||||
bool log_dir_synced_;
|
||||
// Without concurrent_prepare, read and writes to log_empty_ are protected by
|
||||
// mutex_. Since it is currently updated/read only in write_thread_, it can be
|
||||
// accessed from the same write_thread_ without any locks. With
|
||||
// concurrent_prepare writes, where it can be updated in different threads,
|
||||
// read and writes are protected by log_write_mutex_ instead. This is to avoid
|
||||
// expesnive mutex_ lock during WAL write, which update log_empty_.
|
||||
bool log_empty_;
|
||||
ColumnFamilyHandleImpl* default_cf_handle_;
|
||||
InternalStats* default_cf_internal_stats_;
|
||||
@@ -881,14 +923,26 @@ class DBImpl : public DB {
|
||||
// true for some prefix of logs_
|
||||
bool getting_synced = false;
|
||||
};
|
||||
// Without concurrent_prepare, read and writes to alive_log_files_ are
|
||||
// protected by mutex_. However since back() is never popped, and push_back()
|
||||
// is done only from write_thread_, the same thread can access the item
|
||||
// reffered by back() without mutex_. With concurrent_prepare_, writes
|
||||
// are protected by locking both mutex_ and log_write_mutex_, and reads must
|
||||
// be under either mutex_ or log_write_mutex_.
|
||||
std::deque<LogFileNumberSize> alive_log_files_;
|
||||
// Log files that aren't fully synced, and the current log file.
|
||||
// Synchronization:
|
||||
// - push_back() is done from write thread with locked mutex_,
|
||||
// - pop_front() is done from any thread with locked mutex_,
|
||||
// - push_back() is done from write_thread_ with locked mutex_ and
|
||||
// log_write_mutex_
|
||||
// - pop_front() is done from any thread with locked mutex_ and
|
||||
// log_write_mutex_
|
||||
// - reads are done with either locked mutex_ or log_write_mutex_
|
||||
// - back() and items with getting_synced=true are not popped,
|
||||
// - it follows that write thread with unlocked mutex_ can safely access
|
||||
// back() and items with getting_synced=true.
|
||||
// - The same thread that sets getting_synced=true will reset it.
|
||||
// - it follows that the object referred by back() can be safely read from
|
||||
// the write_thread_ without using mutex
|
||||
// - it follows that the items with getting_synced=true can be safely read
|
||||
// from the same thread that has set getting_synced=true
|
||||
std::deque<LogWriterNumber> logs_;
|
||||
// Signaled when getting_synced becomes false for some of the logs_.
|
||||
InstrumentedCondVar log_sync_cv_;
|
||||
@@ -937,8 +991,10 @@ class DBImpl : public DB {
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
|
||||
WriteThread write_thread_;
|
||||
|
||||
WriteBatch tmp_batch_;
|
||||
// The write thread when the writers have no memtable write. This will be used
|
||||
// in 2PC to batch the prepares separately from the serial commit.
|
||||
WriteThread nonmem_write_thread_;
|
||||
|
||||
WriteController write_controller_;
|
||||
|
||||
@@ -946,6 +1002,8 @@ class DBImpl : public DB {
|
||||
|
||||
// Size of the last batch group. In slowdown mode, next write needs to
|
||||
// sleep if it uses up the quota.
|
||||
// Note: This is to protect memtable and compaction. If the batch only writes
|
||||
// to the WAL its size need not to be included in this.
|
||||
uint64_t last_batch_group_size_;
|
||||
|
||||
FlushScheduler flush_scheduler_;
|
||||
@@ -1009,6 +1067,10 @@ class DBImpl : public DB {
|
||||
int unscheduled_flushes_;
|
||||
int unscheduled_compactions_;
|
||||
|
||||
// count how many background compactions are running or have been scheduled in
|
||||
// the BOTTOM pool
|
||||
int bg_bottom_compaction_scheduled_;
|
||||
|
||||
// count how many background compactions are running or have been scheduled
|
||||
int bg_compaction_scheduled_;
|
||||
|
||||
@@ -1025,7 +1087,7 @@ class DBImpl : public DB {
|
||||
int bg_purge_scheduled_;
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompaction {
|
||||
struct ManualCompactionState {
|
||||
ColumnFamilyData* cfd;
|
||||
int input_level;
|
||||
int output_level;
|
||||
@@ -1041,13 +1103,21 @@ class DBImpl : public DB {
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
Compaction* compaction;
|
||||
};
|
||||
std::deque<ManualCompaction*> manual_compaction_dequeue_;
|
||||
struct PrepickedCompaction {
|
||||
// background compaction takes ownership of `compaction`.
|
||||
Compaction* compaction;
|
||||
// caller retains ownership of `manual_compaction_state` as it is reused
|
||||
// across background compactions.
|
||||
ManualCompactionState* manual_compaction_state; // nullptr if non-manual
|
||||
};
|
||||
std::deque<ManualCompactionState*> manual_compaction_dequeue_;
|
||||
|
||||
struct CompactionArg {
|
||||
// caller retains ownership of `db`.
|
||||
DBImpl* db;
|
||||
ManualCompaction* m;
|
||||
// background compaction takes ownership of `prepicked_compaction`.
|
||||
PrepickedCompaction* prepicked_compaction;
|
||||
};
|
||||
|
||||
// Have we encountered a background error in paranoid mode?
|
||||
@@ -1181,13 +1251,18 @@ class DBImpl : public DB {
|
||||
|
||||
bool HasPendingManualCompaction();
|
||||
bool HasExclusiveManualCompaction();
|
||||
void AddManualCompaction(ManualCompaction* m);
|
||||
void RemoveManualCompaction(ManualCompaction* m);
|
||||
bool ShouldntRunManualCompaction(ManualCompaction* m);
|
||||
void AddManualCompaction(ManualCompactionState* m);
|
||||
void RemoveManualCompaction(ManualCompactionState* m);
|
||||
bool ShouldntRunManualCompaction(ManualCompactionState* m);
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompaction* m, ManualCompaction* m1);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
|
||||
size_t GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
|
||||
// When set, we use a seprate queue for writes that dont write to memtable. In
|
||||
// 2PC these are the writes at Prepare phase.
|
||||
const bool concurrent_prepare_;
|
||||
const bool manual_wal_flush_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
+148
-51
@@ -14,6 +14,7 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
@@ -59,7 +60,14 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
// "number < current_log_number".
|
||||
MarkLogsSynced(current_log_number - 1, true, s);
|
||||
if (!s.ok()) {
|
||||
bg_error_ = s;
|
||||
Status new_bg_error = s;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kFlush,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
bg_error_ = new_bg_error;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return s;
|
||||
}
|
||||
@@ -134,9 +142,16 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
immutable_db_options_.paranoid_checks && bg_error_.ok()) {
|
||||
// if a bad error happened (not ShutdownInProgress) and paranoid_checks is
|
||||
// true, mark DB read-only
|
||||
bg_error_ = s;
|
||||
Status new_bg_error = s;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kFlush,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
// if a bad error happened (not ShutdownInProgress), paranoid_checks is
|
||||
// true, and the error isn't handled by callback, mark DB read-only
|
||||
bg_error_ = new_bg_error;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -151,10 +166,17 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
immutable_db_options_.db_paths[0].path, file_meta.fd.GetNumber());
|
||||
sfm->OnAddFile(file_path);
|
||||
if (sfm->IsMaxAllowedSpaceReached() && bg_error_.ok()) {
|
||||
bg_error_ = Status::IOError("Max allowed space was reached");
|
||||
Status new_bg_error = Status::IOError("Max allowed space was reached");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached",
|
||||
&bg_error_);
|
||||
&new_bg_error);
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kFlush,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
bg_error_ = new_bg_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -565,7 +587,14 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
job_context->job_id, status.ToString().c_str());
|
||||
if (immutable_db_options_.paranoid_checks && bg_error_.ok()) {
|
||||
bg_error_ = status;
|
||||
Status new_bg_error = status;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kCompaction,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
bg_error_ = new_bg_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,7 +612,8 @@ Status DBImpl::CompactFilesImpl(
|
||||
Status DBImpl::PauseBackgroundWork() {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
bg_compaction_paused_++;
|
||||
while (bg_compaction_scheduled_ > 0 || bg_flush_scheduled_ > 0) {
|
||||
while (bg_bottom_compaction_scheduled_ > 0 || bg_compaction_scheduled_ > 0 ||
|
||||
bg_flush_scheduled_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
bg_work_paused_++;
|
||||
@@ -779,7 +809,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
|
||||
bool scheduled = false;
|
||||
bool manual_conflict = false;
|
||||
ManualCompaction manual;
|
||||
ManualCompactionState manual;
|
||||
manual.cfd = cfd;
|
||||
manual.input_level = input_level;
|
||||
manual.output_level = output_level;
|
||||
@@ -829,7 +859,8 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
AddManualCompaction(&manual);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RunManualCompaction:NotScheduled", &mutex_);
|
||||
if (exclusive) {
|
||||
while (bg_compaction_scheduled_ > 0) {
|
||||
while (bg_bottom_compaction_scheduled_ > 0 ||
|
||||
bg_compaction_scheduled_ > 0) {
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:WaitScheduled");
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
@@ -849,14 +880,14 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
while (!manual.done) {
|
||||
assert(HasPendingManualCompaction());
|
||||
manual_conflict = false;
|
||||
Compaction* compaction;
|
||||
if (ShouldntRunManualCompaction(&manual) || (manual.in_progress == true) ||
|
||||
scheduled ||
|
||||
((manual.manual_end = &manual.tmp_storage1)&&(
|
||||
(manual.compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), manual.input_level,
|
||||
manual.output_level, manual.output_path_id, manual.begin,
|
||||
manual.end, &manual.manual_end, &manual_conflict)) ==
|
||||
nullptr) &&
|
||||
((manual.manual_end = &manual.tmp_storage1) &&
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), manual.input_level,
|
||||
manual.output_level, manual.output_path_id, manual.begin,
|
||||
manual.end, &manual.manual_end, &manual_conflict)) == nullptr) &&
|
||||
manual_conflict)) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
@@ -869,14 +900,16 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
manual.incomplete = false;
|
||||
}
|
||||
} else if (!scheduled) {
|
||||
if (manual.compaction == nullptr) {
|
||||
if (compaction == nullptr) {
|
||||
manual.done = true;
|
||||
bg_cv_.SignalAll();
|
||||
continue;
|
||||
}
|
||||
ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
ca->m = &manual;
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->manual_compaction_state = &manual;
|
||||
ca->prepicked_compaction->compaction = compaction;
|
||||
manual.incomplete = false;
|
||||
bg_compaction_scheduled_++;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
@@ -1018,7 +1051,7 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
unscheduled_compactions_ > 0) {
|
||||
CompactionArg* ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
ca->m = nullptr;
|
||||
ca->prepicked_compaction = nullptr;
|
||||
bg_compaction_scheduled_++;
|
||||
unscheduled_compactions_--;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
@@ -1123,7 +1156,23 @@ void DBImpl::BGWorkCompaction(void* arg) {
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::LOW);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
reinterpret_cast<DBImpl*>(ca.db)->BackgroundCallCompaction(ca.m);
|
||||
auto prepicked_compaction =
|
||||
static_cast<PrepickedCompaction*>(ca.prepicked_compaction);
|
||||
reinterpret_cast<DBImpl*>(ca.db)->BackgroundCallCompaction(
|
||||
prepicked_compaction, Env::Priority::LOW);
|
||||
delete prepicked_compaction;
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkBottomCompaction(void* arg) {
|
||||
CompactionArg ca = *(static_cast<CompactionArg*>(arg));
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::BOTTOM);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkBottomCompaction");
|
||||
auto* prepicked_compaction = ca.prepicked_compaction;
|
||||
assert(prepicked_compaction && prepicked_compaction->compaction &&
|
||||
!prepicked_compaction->manual_compaction_state);
|
||||
ca.db->BackgroundCallCompaction(prepicked_compaction, Env::Priority::BOTTOM);
|
||||
delete prepicked_compaction;
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkPurge(void* db) {
|
||||
@@ -1136,8 +1185,11 @@ void DBImpl::BGWorkPurge(void* db) {
|
||||
void DBImpl::UnscheduleCallback(void* arg) {
|
||||
CompactionArg ca = *(reinterpret_cast<CompactionArg*>(arg));
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
if ((ca.m != nullptr) && (ca.m->compaction != nullptr)) {
|
||||
delete ca.m->compaction;
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
if (ca.prepicked_compaction->compaction != nullptr) {
|
||||
delete ca.prepicked_compaction->compaction;
|
||||
}
|
||||
delete ca.prepicked_compaction;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleCallback");
|
||||
}
|
||||
@@ -1197,7 +1249,6 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
void DBImpl::BackgroundCallFlush() {
|
||||
bool made_progress = false;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
assert(bg_flush_scheduled_);
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:start");
|
||||
|
||||
@@ -1205,6 +1256,7 @@ void DBImpl::BackgroundCallFlush() {
|
||||
immutable_db_options_.info_log.get());
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(bg_flush_scheduled_);
|
||||
num_running_flushes_++;
|
||||
|
||||
auto pending_outputs_inserted_elem =
|
||||
@@ -1264,9 +1316,9 @@ void DBImpl::BackgroundCallFlush() {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::BackgroundCallCompaction(void* arg) {
|
||||
void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
Env::Priority bg_thread_pri) {
|
||||
bool made_progress = false;
|
||||
ManualCompaction* m = reinterpret_cast<ManualCompaction*>(arg);
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:0");
|
||||
MaybeDumpStats();
|
||||
@@ -1284,9 +1336,11 @@ void DBImpl::BackgroundCallCompaction(void* arg) {
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
|
||||
assert(bg_compaction_scheduled_);
|
||||
Status s =
|
||||
BackgroundCompaction(&made_progress, &job_context, &log_buffer, m);
|
||||
assert((bg_thread_pri == Env::Priority::BOTTOM &&
|
||||
bg_bottom_compaction_scheduled_) ||
|
||||
(bg_thread_pri == Env::Priority::LOW && bg_compaction_scheduled_));
|
||||
Status s = BackgroundCompaction(&made_progress, &job_context, &log_buffer,
|
||||
prepicked_compaction);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:1");
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
@@ -1332,17 +1386,24 @@ void DBImpl::BackgroundCallCompaction(void* arg) {
|
||||
|
||||
assert(num_running_compactions_ > 0);
|
||||
num_running_compactions_--;
|
||||
bg_compaction_scheduled_--;
|
||||
if (bg_thread_pri == Env::Priority::LOW) {
|
||||
bg_compaction_scheduled_--;
|
||||
} else {
|
||||
assert(bg_thread_pri == Env::Priority::BOTTOM);
|
||||
bg_bottom_compaction_scheduled_--;
|
||||
}
|
||||
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
if (made_progress || bg_compaction_scheduled_ == 0 ||
|
||||
if (made_progress ||
|
||||
(bg_compaction_scheduled_ == 0 &&
|
||||
bg_bottom_compaction_scheduled_ == 0) ||
|
||||
HasPendingManualCompaction()) {
|
||||
// signal if
|
||||
// * made_progress -- need to wakeup DelayWrite
|
||||
// * bg_compaction_scheduled_ == 0 -- need to wakeup ~DBImpl
|
||||
// * bg_{bottom,}_compaction_scheduled_ == 0 -- need to wakeup ~DBImpl
|
||||
// * HasPendingManualCompaction -- need to wakeup RunManualCompaction
|
||||
// If none of this is true, there is no need to signal since nobody is
|
||||
// waiting for it
|
||||
@@ -1357,14 +1418,23 @@ void DBImpl::BackgroundCallCompaction(void* arg) {
|
||||
|
||||
Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
JobContext* job_context,
|
||||
LogBuffer* log_buffer, void* arg) {
|
||||
ManualCompaction* manual_compaction =
|
||||
reinterpret_cast<ManualCompaction*>(arg);
|
||||
LogBuffer* log_buffer,
|
||||
PrepickedCompaction* prepicked_compaction) {
|
||||
ManualCompactionState* manual_compaction =
|
||||
prepicked_compaction == nullptr
|
||||
? nullptr
|
||||
: prepicked_compaction->manual_compaction_state;
|
||||
*made_progress = false;
|
||||
mutex_.AssertHeld();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:Start");
|
||||
|
||||
bool is_manual = (manual_compaction != nullptr);
|
||||
unique_ptr<Compaction> c;
|
||||
if (prepicked_compaction != nullptr &&
|
||||
prepicked_compaction->compaction != nullptr) {
|
||||
c.reset(prepicked_compaction->compaction);
|
||||
}
|
||||
bool is_prepicked = is_manual || c;
|
||||
|
||||
// (manual_compaction->in_progress == false);
|
||||
bool trivial_move_disallowed =
|
||||
@@ -1381,7 +1451,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->status = status;
|
||||
manual_compaction->done = true;
|
||||
manual_compaction->in_progress = false;
|
||||
delete manual_compaction->compaction;
|
||||
manual_compaction = nullptr;
|
||||
}
|
||||
return status;
|
||||
@@ -1392,13 +1461,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->in_progress = true;
|
||||
}
|
||||
|
||||
unique_ptr<Compaction> c;
|
||||
// InternalKey manual_end_storage;
|
||||
// InternalKey* manual_end = &manual_end_storage;
|
||||
if (is_manual) {
|
||||
ManualCompaction* m = manual_compaction;
|
||||
ManualCompactionState* m = manual_compaction;
|
||||
assert(m->in_progress);
|
||||
c.reset(std::move(m->compaction));
|
||||
if (!c) {
|
||||
m->done = true;
|
||||
m->manual_end = nullptr;
|
||||
@@ -1420,7 +1487,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
? "(end)"
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
}
|
||||
} else if (!compaction_queue_.empty()) {
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
if (HaveManualCompaction(compaction_queue_.front())) {
|
||||
// Can't compact right now, but try again later
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction()::Conflict");
|
||||
@@ -1572,6 +1639,28 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
// Clear Instrument
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
} else if (c->column_family_data()->ioptions()->compaction_style ==
|
||||
kCompactionStyleUniversal &&
|
||||
!is_prepicked && c->output_level() > 0 &&
|
||||
c->output_level() ==
|
||||
c->column_family_data()
|
||||
->current()
|
||||
->storage_info()
|
||||
->MaxOutputLevel(
|
||||
immutable_db_options_.allow_ingest_behind) &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
// Forward universal compactions involving last level to the bottom pool
|
||||
// if it exists, such that long-running compactions can't block short-
|
||||
// lived ones, like L0->L0s.
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:ForwardToBottomPriPool");
|
||||
CompactionArg* ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->compaction = c.release();
|
||||
ca->prepicked_compaction->manual_compaction_state = nullptr;
|
||||
++bg_bottom_compaction_scheduled_;
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca, Env::Priority::BOTTOM,
|
||||
this, &DBImpl::UnscheduleCallback);
|
||||
} else {
|
||||
int output_level __attribute__((unused)) = c->output_level();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:NonTrivial",
|
||||
@@ -1623,12 +1712,19 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "Compaction error: %s",
|
||||
status.ToString().c_str());
|
||||
if (immutable_db_options_.paranoid_checks && bg_error_.ok()) {
|
||||
bg_error_ = status;
|
||||
Status new_bg_error = status;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kCompaction,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
bg_error_ = new_bg_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_manual) {
|
||||
ManualCompaction* m = manual_compaction;
|
||||
ManualCompactionState* m = manual_compaction;
|
||||
if (!status.ok()) {
|
||||
m->status = status;
|
||||
m->done = true;
|
||||
@@ -1671,13 +1767,13 @@ bool DBImpl::HasPendingManualCompaction() {
|
||||
return (!manual_compaction_dequeue_.empty());
|
||||
}
|
||||
|
||||
void DBImpl::AddManualCompaction(DBImpl::ManualCompaction* m) {
|
||||
void DBImpl::AddManualCompaction(DBImpl::ManualCompactionState* m) {
|
||||
manual_compaction_dequeue_.push_back(m);
|
||||
}
|
||||
|
||||
void DBImpl::RemoveManualCompaction(DBImpl::ManualCompaction* m) {
|
||||
void DBImpl::RemoveManualCompaction(DBImpl::ManualCompactionState* m) {
|
||||
// Remove from queue
|
||||
std::deque<ManualCompaction*>::iterator it =
|
||||
std::deque<ManualCompactionState*>::iterator it =
|
||||
manual_compaction_dequeue_.begin();
|
||||
while (it != manual_compaction_dequeue_.end()) {
|
||||
if (m == (*it)) {
|
||||
@@ -1690,16 +1786,17 @@ void DBImpl::RemoveManualCompaction(DBImpl::ManualCompaction* m) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool DBImpl::ShouldntRunManualCompaction(ManualCompaction* m) {
|
||||
bool DBImpl::ShouldntRunManualCompaction(ManualCompactionState* m) {
|
||||
if (num_running_ingest_file_ > 0) {
|
||||
// We need to wait for other IngestExternalFile() calls to finish
|
||||
// before running a manual compaction.
|
||||
return true;
|
||||
}
|
||||
if (m->exclusive) {
|
||||
return (bg_compaction_scheduled_ > 0);
|
||||
return (bg_bottom_compaction_scheduled_ > 0 ||
|
||||
bg_compaction_scheduled_ > 0);
|
||||
}
|
||||
std::deque<ManualCompaction*>::iterator it =
|
||||
std::deque<ManualCompactionState*>::iterator it =
|
||||
manual_compaction_dequeue_.begin();
|
||||
bool seen = false;
|
||||
while (it != manual_compaction_dequeue_.end()) {
|
||||
@@ -1720,7 +1817,7 @@ bool DBImpl::ShouldntRunManualCompaction(ManualCompaction* m) {
|
||||
|
||||
bool DBImpl::HaveManualCompaction(ColumnFamilyData* cfd) {
|
||||
// Remove from priority queue
|
||||
std::deque<ManualCompaction*>::iterator it =
|
||||
std::deque<ManualCompactionState*>::iterator it =
|
||||
manual_compaction_dequeue_.begin();
|
||||
while (it != manual_compaction_dequeue_.end()) {
|
||||
if ((*it)->exclusive) {
|
||||
@@ -1728,7 +1825,7 @@ bool DBImpl::HaveManualCompaction(ColumnFamilyData* cfd) {
|
||||
}
|
||||
if ((cfd == (*it)->cfd) && (!((*it)->in_progress || (*it)->done))) {
|
||||
// Allow automatic compaction if manual compaction is
|
||||
// is in progress
|
||||
// in progress
|
||||
return true;
|
||||
}
|
||||
it++;
|
||||
@@ -1738,7 +1835,7 @@ bool DBImpl::HaveManualCompaction(ColumnFamilyData* cfd) {
|
||||
|
||||
bool DBImpl::HasExclusiveManualCompaction() {
|
||||
// Remove from priority queue
|
||||
std::deque<ManualCompaction*>::iterator it =
|
||||
std::deque<ManualCompactionState*>::iterator it =
|
||||
manual_compaction_dequeue_.begin();
|
||||
while (it != manual_compaction_dequeue_.end()) {
|
||||
if ((*it)->exclusive) {
|
||||
@@ -1749,7 +1846,7 @@ bool DBImpl::HasExclusiveManualCompaction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBImpl::MCOverlap(ManualCompaction* m, ManualCompaction* m1) {
|
||||
bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
if ((m->exclusive) || (m1->exclusive)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+3
-1
@@ -112,7 +112,9 @@ Status DBImpl::TEST_WaitForCompact() {
|
||||
// OR flush to finish.
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while ((bg_compaction_scheduled_ || bg_flush_scheduled_) && bg_error_.ok()) {
|
||||
while ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_) &&
|
||||
bg_error_.ok()) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
return bg_error_;
|
||||
|
||||
+13
-1
@@ -250,7 +250,13 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
job_context->size_log_to_delete += earliest.size;
|
||||
total_log_size_ -= earliest.size;
|
||||
if (concurrent_prepare_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
alive_log_files_.pop_front();
|
||||
if (concurrent_prepare_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
// Current log should always stay alive since it can't have
|
||||
// number < MinLogNumber().
|
||||
assert(alive_log_files_.size());
|
||||
@@ -263,7 +269,10 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
continue;
|
||||
}
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
logs_.pop_front();
|
||||
{
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
logs_.pop_front();
|
||||
}
|
||||
}
|
||||
// Current log cannot be obsolete.
|
||||
assert(!logs_.empty());
|
||||
@@ -359,6 +368,9 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(kDumbDbName, file->fd.GetNumber()),
|
||||
file->fd.GetPathId());
|
||||
if (file->table_reader_handle) {
|
||||
table_cache_->Release(file->table_reader_handle);
|
||||
}
|
||||
delete file;
|
||||
}
|
||||
|
||||
|
||||
+45
-9
@@ -16,6 +16,7 @@
|
||||
#include "db/builder.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -162,6 +163,19 @@ static Status ValidateOptions(
|
||||
"universal and level compaction styles. ");
|
||||
}
|
||||
}
|
||||
if (cfd.options.compaction_options_fifo.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"FIFO Compaction with TTL is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cfd.options.table_factory->Name() !=
|
||||
BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"FIFO Compaction with TTL is only supported in "
|
||||
"Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (db_options.db_paths.size() > 4) {
|
||||
@@ -723,6 +737,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
auto last_sequence = *next_sequence - 1;
|
||||
if ((*next_sequence != kMaxSequenceNumber) &&
|
||||
(versions_->LastSequence() <= last_sequence)) {
|
||||
versions_->SetLastToBeWrittenSequence(last_sequence);
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
}
|
||||
@@ -794,9 +809,15 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
if (data_seen && !flushed) {
|
||||
// Mark these as alive so they'll be considered for deletion later by
|
||||
// FindObsoleteFiles()
|
||||
if (concurrent_prepare_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
for (auto log_number : log_numbers) {
|
||||
alive_log_files_.push_back(LogFileNumberSize(log_number));
|
||||
}
|
||||
if (concurrent_prepare_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
event_logger_.Log() << "job" << job_id << "event"
|
||||
@@ -830,6 +851,11 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
bool paranoid_file_checks =
|
||||
cfd->GetLatestMutableCFOptions()->paranoid_file_checks;
|
||||
|
||||
int64_t _current_time = 0;
|
||||
env_->GetCurrentTime(&_current_time); // ignore error
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
{
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -849,7 +875,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
cfd->ioptions()->compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery,
|
||||
&event_logger_, job_id);
|
||||
&event_logger_, job_id, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
@@ -960,14 +987,17 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
if (s.ok()) {
|
||||
lfile->SetPreallocationBlockSize(
|
||||
impl->GetWalPreallocateBlockSize(max_write_buffer_size));
|
||||
impl->logfile_number_ = new_log_number;
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), opt_env_options));
|
||||
impl->logs_.emplace_back(
|
||||
new_log_number,
|
||||
new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
impl->immutable_db_options_.recycle_log_file_num > 0));
|
||||
{
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), opt_env_options));
|
||||
impl->logs_.emplace_back(
|
||||
new_log_number,
|
||||
new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
impl->immutable_db_options_.recycle_log_file_num > 0));
|
||||
}
|
||||
|
||||
// set column family handles
|
||||
for (auto cf : column_families) {
|
||||
@@ -1001,8 +1031,14 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
delete impl->InstallSuperVersionAndScheduleWork(
|
||||
cfd, nullptr, *cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
if (impl->concurrent_prepare_) {
|
||||
impl->log_write_mutex_.Lock();
|
||||
}
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
if (impl->concurrent_prepare_) {
|
||||
impl->log_write_mutex_.Unlock();
|
||||
}
|
||||
impl->DeleteObsoleteFiles();
|
||||
s = impl->directories_.GetDbDir()->Fsync();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/db_impl_readonly.h"
|
||||
|
||||
#include "db/compacted_db_impl.h"
|
||||
@@ -59,7 +58,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
SuperVersion* super_version = cfd->GetSuperVersion()->Ref();
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(),
|
||||
env_, read_options, *cfd->ioptions(),
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
@@ -88,7 +87,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(),
|
||||
env_, read_options, *cfd->ioptions(),
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
|
||||
+289
-51
@@ -12,6 +12,7 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include "db/event_helpers.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -63,6 +64,10 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
if (concurrent_prepare_ && immutable_db_options_.enable_pipelined_write) {
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with concurrent prepares");
|
||||
}
|
||||
|
||||
Status status;
|
||||
if (write_options.low_pri) {
|
||||
@@ -72,6 +77,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
}
|
||||
|
||||
if (concurrent_prepare_ && disable_memtable) {
|
||||
return WriteImplWALOnly(write_options, my_batch, callback, log_used,
|
||||
log_ref);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
return PipelinedWriteImpl(write_options, my_batch, callback, log_used,
|
||||
log_ref, disable_memtable);
|
||||
@@ -130,14 +140,22 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteContext write_context;
|
||||
WriteThread::WriteGroup write_group;
|
||||
bool in_parallel_group = false;
|
||||
uint64_t last_sequence = versions_->LastSequence();
|
||||
uint64_t last_sequence = kMaxSequenceNumber;
|
||||
if (!concurrent_prepare_) {
|
||||
last_sequence = versions_->LastSequence();
|
||||
}
|
||||
|
||||
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);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
if (!concurrent_prepare_ || !disable_memtable) {
|
||||
// With concurrent writes we do preprocess only in the write thread that
|
||||
// also does write to memtable to avoid sync issue on shared data structure
|
||||
// with the other thread
|
||||
status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
}
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -177,24 +195,24 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
}
|
||||
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += total_count;
|
||||
|
||||
const bool concurrent_update = concurrent_prepare_;
|
||||
// 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.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
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);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -205,14 +223,27 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
status = WriteToWAL(write_group, cur_log_writer, need_log_sync,
|
||||
need_log_dir_sync, current_sequence);
|
||||
if (log_used != nullptr) {
|
||||
*log_used = logfile_number_;
|
||||
if (!concurrent_prepare_) {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
status = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, last_sequence + 1);
|
||||
}
|
||||
} else {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastToBeWrittenSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
total_count);
|
||||
} else {
|
||||
// Otherwise we inc seq number for memtable writes
|
||||
last_sequence = versions_->FetchAddLastToBeWrittenSequence(total_count);
|
||||
}
|
||||
}
|
||||
assert(last_sequence != kMaxSequenceNumber);
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += total_count;
|
||||
|
||||
if (status.ok()) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
@@ -253,13 +284,22 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
ParanoidCheck(status);
|
||||
WriteCallbackStatusCheck(status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
mutex_.Unlock();
|
||||
// Requesting sync with concurrent_prepare_ is expected to be very rare. We
|
||||
// hance provide a simple implementation that is not necessarily efficient.
|
||||
if (concurrent_prepare_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
status = SyncWAL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool should_exit_batch_group = true;
|
||||
@@ -269,7 +309,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
should_exit_batch_group = write_thread_.CompleteParallelMemTableWriter(&w);
|
||||
}
|
||||
if (should_exit_batch_group) {
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
if (status.ok()) {
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
write_thread_.ExitAsBatchGroupLeader(write_group, w.status);
|
||||
}
|
||||
@@ -301,7 +343,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
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;
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
mutex_.Unlock();
|
||||
|
||||
// This can set non-OK status if callback fail.
|
||||
@@ -349,12 +391,12 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
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);
|
||||
w.status = WriteToWAL(wal_write_group, log_writer, log_used,
|
||||
need_log_sync, need_log_dir_sync, current_sequence);
|
||||
}
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
ParanoidCheck(w.status);
|
||||
WriteCallbackStatusCheck(w.status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -400,21 +442,109 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
assert(w.state == WriteThread::STATE_COMPLETED);
|
||||
if (log_used != nullptr) {
|
||||
*log_used = w.log_used;
|
||||
}
|
||||
|
||||
return w.FinalStatus();
|
||||
}
|
||||
|
||||
void DBImpl::ParanoidCheck(const Status& status) {
|
||||
Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref) {
|
||||
Status status;
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
true /* disable_memtable */);
|
||||
if (write_options.disableWAL) {
|
||||
return status;
|
||||
}
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
nonmem_write_thread_.JoinBatchGroup(&w);
|
||||
assert(w.state != WriteThread::STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
if (w.state == WriteThread::STATE_COMPLETED) {
|
||||
if (log_used != nullptr) {
|
||||
*log_used = w.log_used;
|
||||
}
|
||||
return w.FinalStatus();
|
||||
}
|
||||
// else we are the leader of the write batch group
|
||||
assert(w.state == WriteThread::STATE_GROUP_LEADER);
|
||||
WriteContext write_context;
|
||||
WriteThread::WriteGroup write_group;
|
||||
uint64_t last_sequence;
|
||||
nonmem_write_thread_.EnterAsBatchGroupLeader(&w, &write_group);
|
||||
// Note: no need to update last_batch_group_size_ here since the batch writes
|
||||
// to WAL only
|
||||
|
||||
uint64_t total_byte_size = 0;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
}
|
||||
}
|
||||
|
||||
const bool concurrent_update = true;
|
||||
// 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.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
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,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastToBeWrittenSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
0 /*total_count*/);
|
||||
if (status.ok() && write_options.sync) {
|
||||
// Requesting sync with concurrent_prepare_ is expected to be very rare. We
|
||||
// hance provide a simple implementation that is not necessarily efficient.
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
status = SyncWAL();
|
||||
}
|
||||
}
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteCallbackStatusCheck(status);
|
||||
}
|
||||
nonmem_write_thread_.ExitAsBatchGroupLeader(write_group, w.status);
|
||||
if (status.ok()) {
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void DBImpl::WriteCallbackStatusCheck(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()) {
|
||||
mutex_.Lock();
|
||||
if (bg_error_.ok()) {
|
||||
bg_error_ = status; // stop compaction & fail any further writes
|
||||
Status new_bg_error = status;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kWriteCallback,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
bg_error_ = new_bg_error; // stop compaction & fail any further writes
|
||||
}
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
@@ -429,7 +559,14 @@ void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
if (!status.ok()) {
|
||||
mutex_.Lock();
|
||||
assert(bg_error_.ok());
|
||||
bg_error_ = status;
|
||||
Status new_bg_error = status;
|
||||
// may temporarily unlock and lock the mutex.
|
||||
EventHelpers::NotifyOnBackgroundError(immutable_db_options_.listeners,
|
||||
BackgroundErrorReason::kMemTable,
|
||||
&new_bg_error, &mutex_);
|
||||
if (!new_bg_error.ok()) {
|
||||
bg_error_ = new_bg_error; // stop compaction & fail any further writes
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -502,13 +639,12 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, bool need_log_sync,
|
||||
bool need_log_dir_sync, SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal) {
|
||||
assert(write_with_wal != nullptr);
|
||||
assert(tmp_batch != nullptr);
|
||||
WriteBatch* merged_batch = nullptr;
|
||||
size_t write_with_wal = 0;
|
||||
*write_with_wal = 0;
|
||||
auto* leader = write_group.leader;
|
||||
if (write_group.size == 1 && leader->ShouldWriteToWAL() &&
|
||||
leader->batch->GetWalTerminationPoint().is_cleared()) {
|
||||
@@ -516,31 +652,64 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
// 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_;
|
||||
write_with_wal = 1;
|
||||
*write_with_wal = 1;
|
||||
} else {
|
||||
// WAL needs all of the batches flattened into a single batch.
|
||||
// We could avoid copying here with an iov-like AddRecord
|
||||
// interface
|
||||
merged_batch = &tmp_batch_;
|
||||
merged_batch = tmp_batch;
|
||||
for (auto writer : write_group) {
|
||||
if (writer->ShouldWriteToWAL()) {
|
||||
WriteBatchInternal::Append(merged_batch, writer->batch,
|
||||
/*WAL_only*/ true);
|
||||
write_with_wal++;
|
||||
(*write_with_wal)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged_batch;
|
||||
}
|
||||
|
||||
// When concurrent_prepare_ is disabled, this function is called from the only
|
||||
// write thread. Otherwise this must be called holding log_write_mutex_.
|
||||
Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size) {
|
||||
assert(log_size != nullptr);
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
Status status = log_writer->AddRecord(log_entry);
|
||||
if (log_used != nullptr) {
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
total_log_size_ += log_entry.size();
|
||||
// TODO(myabandeh): it might be unsafe to access alive_log_files_.back() here
|
||||
// since alive_log_files_ might be modified concurrently
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
size_t write_with_wal = 0;
|
||||
WriteBatch* merged_batch =
|
||||
MergeBatch(write_group, &tmp_batch_, &write_with_wal);
|
||||
if (merged_batch == write_group.leader->batch) {
|
||||
write_group.leader->log_used = logfile_number_;
|
||||
} else if (write_with_wal > 1) {
|
||||
for (auto writer : write_group) {
|
||||
writer->log_used = logfile_number_;
|
||||
}
|
||||
}
|
||||
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(merged_batch);
|
||||
status = log_writer->AddRecord(log_entry);
|
||||
total_log_size_ += log_entry.size();
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
uint64_t log_size = log_entry.size();
|
||||
uint64_t log_size;
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
|
||||
if (status.ok() && need_log_sync) {
|
||||
StopWatch sw(env_, stats_, WAL_FILE_SYNC_MICROS);
|
||||
@@ -582,6 +751,48 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence,
|
||||
int total_count) {
|
||||
Status status;
|
||||
|
||||
WriteBatch tmp_batch;
|
||||
size_t write_with_wal = 0;
|
||||
WriteBatch* merged_batch =
|
||||
MergeBatch(write_group, &tmp_batch, &write_with_wal);
|
||||
|
||||
// We need to lock log_write_mutex_ since logs_ and alive_log_files might be
|
||||
// pushed back concurrently
|
||||
log_write_mutex_.Lock();
|
||||
if (merged_batch == write_group.leader->batch) {
|
||||
write_group.leader->log_used = logfile_number_;
|
||||
} else if (write_with_wal > 1) {
|
||||
for (auto writer : write_group) {
|
||||
writer->log_used = logfile_number_;
|
||||
}
|
||||
}
|
||||
*last_sequence = versions_->FetchAddLastToBeWrittenSequence(total_count);
|
||||
auto sequence = *last_sequence + 1;
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
uint64_t log_size;
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
if (status.ok()) {
|
||||
const bool concurrent = true;
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size, concurrent);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::HandleWALFull(WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr);
|
||||
@@ -767,8 +978,9 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
// is that in case the write is heavy, low pri writes may never have
|
||||
// a chance to run. Now we guarantee we are still slowly making
|
||||
// progress.
|
||||
write_controller_.low_pri_rate_limiter()->Request(my_batch->GetDataSize(),
|
||||
Env::IO_HIGH, nullptr);
|
||||
write_controller_.low_pri_rate_limiter()->Request(
|
||||
my_batch->GetDataSize(), Env::IO_HIGH, nullptr /* stats */,
|
||||
RateLimiter::OpType::kWrite);
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -808,6 +1020,13 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* cfd,
|
||||
// REQUIRES: this thread is currently at the front of the writer queue
|
||||
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
mutex_.AssertHeld();
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (concurrent_prepare_) {
|
||||
// SwitchMemtable is a rare event. To simply the reasoning, we make sure
|
||||
// that there is no concurrent thread writing to WAL.
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
unique_ptr<WritableFile> lfile;
|
||||
log::Writer* new_log = nullptr;
|
||||
MemTable* new_mem = nullptr;
|
||||
@@ -821,7 +1040,13 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
// 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);
|
||||
if (concurrent_prepare_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
bool creating_new_log = !log_empty_;
|
||||
if (concurrent_prepare_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
uint64_t recycle_log_number = 0;
|
||||
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
|
||||
!log_recycle_files.empty()) {
|
||||
@@ -877,9 +1102,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), opt_env_opt));
|
||||
new_log =
|
||||
new log::Writer(std::move(file_writer), new_log_number,
|
||||
immutable_db_options_.recycle_log_file_num > 0);
|
||||
new_log = new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
immutable_db_options_.recycle_log_file_num > 0, manual_wal_flush_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,15 +1131,25 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
assert(creating_new_log);
|
||||
assert(!new_mem);
|
||||
assert(!new_log);
|
||||
if (concurrent_prepare_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (creating_new_log) {
|
||||
log_write_mutex_.Lock();
|
||||
logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
log_empty_ = true;
|
||||
log_dir_synced_ = false;
|
||||
if (!logs_.empty()) {
|
||||
// Alway flush the buffer of the last log before switching to a new one
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
cur_log_writer->WriteBuffer();
|
||||
}
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
for (auto loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
// all this is just optimization to delete logs that
|
||||
@@ -936,6 +1171,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
cfd->SetMemtable(new_mem);
|
||||
context->superversions_to_free_.push_back(InstallSuperVersionAndScheduleWork(
|
||||
cfd, new_superversion, mutable_cf_options));
|
||||
if (concurrent_prepare_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+309
-1
@@ -204,7 +204,6 @@ TEST_F(DBIOFailureTest, ManifestWriteError) {
|
||||
ASSERT_EQ("bar2", Get("foo2"));
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBIOFailureTest, PutFailsParanoid) {
|
||||
// Test the following:
|
||||
@@ -250,7 +249,316 @@ TEST_F(DBIOFailureTest, PutFailsParanoid) {
|
||||
// the next put should NOT fail
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_F(DBIOFailureTest, FlushSstRangeSyncError) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 256 * 1024 * 1024;
|
||||
options.writable_file_max_buffer_size = 128 * 1024;
|
||||
options.bytes_per_sync = 128 * 1024;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(10));
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
std::atomic<int> range_sync_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::RangeSync", [&](void* arg) {
|
||||
if (range_sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("range sync dummy error");
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
std::string rnd_str =
|
||||
RandomString(&rnd, static_cast<int>(options.bytes_per_sync / 2));
|
||||
std::string rnd_str_512kb = RandomString(&rnd, 512 * 1024);
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
// First 1MB doesn't get range synced
|
||||
ASSERT_OK(Put(1, "foo0_0", rnd_str_512kb));
|
||||
ASSERT_OK(Put(1, "foo0_1", rnd_str_512kb));
|
||||
ASSERT_OK(Put(1, "foo1_1", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo1_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo1_3", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
ASSERT_OK(Put(1, "foo3_1", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_3", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo4", "bar"));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_GE(1, range_sync_called.load());
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(DBIOFailureTest, CompactSstRangeSyncError) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 256 * 1024 * 1024;
|
||||
options.writable_file_max_buffer_size = 128 * 1024;
|
||||
options.bytes_per_sync = 128 * 1024;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.target_file_size_base = 256 * 1024 * 1024;
|
||||
options.disable_auto_compactions = true;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
Random rnd(301);
|
||||
std::string rnd_str =
|
||||
RandomString(&rnd, static_cast<int>(options.bytes_per_sync / 2));
|
||||
std::string rnd_str_512kb = RandomString(&rnd, 512 * 1024);
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
// First 1MB doesn't get range synced
|
||||
ASSERT_OK(Put(1, "foo0_0", rnd_str_512kb));
|
||||
ASSERT_OK(Put(1, "foo0_1", rnd_str_512kb));
|
||||
ASSERT_OK(Put(1, "foo1_1", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo1_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo1_3", rnd_str));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo3_1", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_3", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo4", "bar"));
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
|
||||
std::atomic<int> range_sync_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::RangeSync", [&](void* arg) {
|
||||
if (range_sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("range sync dummy error");
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1],
|
||||
{
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_GE(1, range_sync_called.load());
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(DBIOFailureTest, FlushSstCloseError) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
options.paranoid_checks = true;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
std::atomic<int> close_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Close", [&](void* arg) {
|
||||
if (close_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("close dummy error");
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo1", "bar1"));
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
ASSERT_EQ("bar2", Get(1, "foo"));
|
||||
ASSERT_EQ("bar1", Get(1, "foo1"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("bar2", Get(1, "foo"));
|
||||
ASSERT_EQ("bar1", Get(1, "foo1"));
|
||||
}
|
||||
|
||||
TEST_F(DBIOFailureTest, CompactionSstCloseError) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
options.paranoid_checks = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "foo", "bar3"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
std::atomic<int> close_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Close", [&](void* arg) {
|
||||
if (close_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("close dummy error");
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1],
|
||||
{
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Following writes should fail as compaction failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
ASSERT_EQ("bar3", Get(1, "foo"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("bar3", Get(1, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(DBIOFailureTest, FlushSstSyncError) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
options.paranoid_checks = true;
|
||||
options.use_fsync = false;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
std::atomic<int> sync_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Sync", [&](void* arg) {
|
||||
if (sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("sync dummy error");
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo1", "bar1"));
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
ASSERT_EQ("bar2", Get(1, "foo"));
|
||||
ASSERT_EQ("bar1", Get(1, "foo1"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("bar2", Get(1, "foo"));
|
||||
ASSERT_EQ("bar1", Get(1, "foo1"));
|
||||
}
|
||||
|
||||
TEST_F(DBIOFailureTest, CompactionSstSyncError) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
options.paranoid_checks = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
options.use_fsync = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "foo", "bar3"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
std::atomic<int> sync_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Sync", [&](void* arg) {
|
||||
if (sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("close dummy error");
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1],
|
||||
{
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Following writes should fail as compaction failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
ASSERT_EQ("bar3", Get(1, "foo"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("bar3", Get(1, "foo"));
|
||||
}
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+68
-30
@@ -101,12 +101,12 @@ class DBIter: public Iterator {
|
||||
uint64_t bytes_read_;
|
||||
};
|
||||
|
||||
DBIter(Env* env, const ReadOptions& read_options,
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number)
|
||||
uint64_t max_sequential_skip_in_iterations)
|
||||
: arena_mode_(arena_mode),
|
||||
env_(env),
|
||||
env_(_env),
|
||||
logger_(cf_options.info_log),
|
||||
user_comparator_(cmp),
|
||||
merge_operator_(cf_options.merge_operator),
|
||||
@@ -116,7 +116,6 @@ class DBIter: public Iterator {
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
statistics_(cf_options.statistics),
|
||||
version_number_(version_number),
|
||||
iterate_upper_bound_(read_options.iterate_upper_bound),
|
||||
prefix_same_as_start_(read_options.prefix_same_as_start),
|
||||
pin_thru_lifetime_(read_options.pin_data),
|
||||
@@ -188,10 +187,7 @@ class DBIter: public Iterator {
|
||||
}
|
||||
if (prop_name == "rocksdb.iterator.super-version-number") {
|
||||
// First try to pass the value returned from inner iterator.
|
||||
if (!iter_->GetProperty(prop_name, prop).ok()) {
|
||||
*prop = ToString(version_number_);
|
||||
}
|
||||
return Status::OK();
|
||||
return iter_->GetProperty(prop_name, prop);
|
||||
} else if (prop_name == "rocksdb.iterator.is-key-pinned") {
|
||||
if (valid_) {
|
||||
*prop = (pin_thru_lifetime_ && saved_key_.IsKeyPinned()) ? "1" : "0";
|
||||
@@ -209,6 +205,9 @@ class DBIter: public Iterator {
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
Env* env() { return env_; }
|
||||
void set_sequence(uint64_t s) { sequence_ = s; }
|
||||
void set_valid(bool v) { valid_ = v; }
|
||||
|
||||
private:
|
||||
void ReverseToForward();
|
||||
@@ -260,7 +259,7 @@ class DBIter: public Iterator {
|
||||
const Comparator* const user_comparator_;
|
||||
const MergeOperator* const merge_operator_;
|
||||
InternalIterator* iter_;
|
||||
SequenceNumber const sequence_;
|
||||
SequenceNumber sequence_;
|
||||
|
||||
Status status_;
|
||||
IterKey saved_key_;
|
||||
@@ -274,7 +273,6 @@ class DBIter: public Iterator {
|
||||
uint64_t max_skip_;
|
||||
uint64_t max_skippable_internal_keys_;
|
||||
uint64_t num_internal_keys_skipped_;
|
||||
uint64_t version_number_;
|
||||
const Slice* iterate_upper_bound_;
|
||||
IterKey prefix_start_buf_;
|
||||
Slice prefix_start_key_;
|
||||
@@ -642,6 +640,7 @@ void DBIter::ReverseToBackward() {
|
||||
while (iter_->Valid() &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) >
|
||||
0) {
|
||||
assert(ikey.sequence != kMaxSequenceNumber);
|
||||
if (ikey.sequence > sequence_) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
@@ -954,6 +953,7 @@ void DBIter::FindPrevUserKey() {
|
||||
++num_skipped;
|
||||
}
|
||||
}
|
||||
assert(ikey.sequence != kMaxSequenceNumber);
|
||||
if (ikey.sequence > sequence_) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
@@ -1155,18 +1155,15 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, read_options, cf_options, user_key_comparator, internal_iter,
|
||||
sequence, false, max_sequential_skip_in_iterations, version_number);
|
||||
uint64_t max_sequential_skip_in_iterations) {
|
||||
DBIter* db_iter = new DBIter(env, read_options, cf_options,
|
||||
user_key_comparator, internal_iter, sequence,
|
||||
false, max_sequential_skip_in_iterations);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter::~ArenaWrappedDBIter() { db_iter_->~DBIter(); }
|
||||
|
||||
void ArenaWrappedDBIter::SetDBIter(DBIter* iter) { db_iter_ = iter; }
|
||||
|
||||
RangeDelAggregator* ArenaWrappedDBIter::GetRangeDelAggregator() {
|
||||
return db_iter_->GetRangeDelAggregator();
|
||||
}
|
||||
@@ -1191,26 +1188,67 @@ inline Slice ArenaWrappedDBIter::value() const { return db_iter_->value(); }
|
||||
inline Status ArenaWrappedDBIter::status() const { return db_iter_->status(); }
|
||||
inline Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
std::string* prop) {
|
||||
if (prop_name == "rocksdb.iterator.super-version-number") {
|
||||
// First try to pass the value returned from inner iterator.
|
||||
if (!db_iter_->GetProperty(prop_name, prop).ok()) {
|
||||
*prop = ToString(sv_number_);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
return db_iter_->GetProperty(prop_name, prop);
|
||||
}
|
||||
void ArenaWrappedDBIter::RegisterCleanup(CleanupFunction function, void* arg1,
|
||||
void* arg2) {
|
||||
db_iter_->RegisterCleanup(function, arg1, arg2);
|
||||
|
||||
void ArenaWrappedDBIter::Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem)
|
||||
DBIter(env, read_options, cf_options, cf_options.user_comparator, nullptr,
|
||||
sequence, true, max_sequential_skip_in_iteration);
|
||||
sv_number_ = version_number;
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
assert(db_iter_ != nullptr);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_->mutex());
|
||||
Init(env, read_options_, *(cfd_->ioptions()), latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator());
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* user_key_comparator,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number) {
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
DBImpl* db_impl, ColumnFamilyData* cfd) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
Arena* arena = iter->GetArena();
|
||||
auto mem = arena->AllocateAligned(sizeof(DBIter));
|
||||
DBIter* db_iter = new (mem)
|
||||
DBIter(env, read_options, cf_options, user_key_comparator, nullptr,
|
||||
sequence, true, max_sequential_skip_in_iterations, version_number);
|
||||
|
||||
iter->SetDBIter(db_iter);
|
||||
iter->Init(env, read_options, cf_options, sequence,
|
||||
max_sequential_skip_in_iterations, version_number);
|
||||
if (db_impl != nullptr && cfd != nullptr) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd);
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
+25
-10
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -32,8 +33,7 @@ extern Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number);
|
||||
uint64_t max_sequential_skip_in_iterations);
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
// iterator is supposed be allocated. This class is used as an entry point of
|
||||
@@ -49,10 +49,6 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
virtual Arena* GetArena() { return &arena_; }
|
||||
virtual RangeDelAggregator* GetRangeDelAggregator();
|
||||
|
||||
// Set the DB Iterator to be wrapped
|
||||
|
||||
virtual void SetDBIter(DBIter* iter);
|
||||
|
||||
// Set the internal iterator wrapped inside the DB Iterator. Usually it is
|
||||
// a merging iterator.
|
||||
virtual void SetIterUnderDBIter(InternalIterator* iter);
|
||||
@@ -66,20 +62,39 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
virtual Slice key() const override;
|
||||
virtual Slice value() const override;
|
||||
virtual Status status() const override;
|
||||
virtual Status Refresh() override;
|
||||
|
||||
void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2);
|
||||
virtual Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number);
|
||||
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd) {
|
||||
read_options_ = read_options;
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
}
|
||||
|
||||
private:
|
||||
DBIter* db_iter_;
|
||||
Arena arena_;
|
||||
uint64_t sv_number_;
|
||||
ColumnFamilyData* cfd_ = nullptr;
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* user_key_comparator,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number);
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr);
|
||||
|
||||
} // namespace rocksdb
|
||||
+69
-69
@@ -193,7 +193,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -225,7 +225,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -251,7 +251,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -283,7 +283,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -318,7 +318,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -347,7 +347,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
7, options.max_sequential_skip_in_iterations, 0));
|
||||
7, options.max_sequential_skip_in_iterations));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -384,7 +384,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
4, options.max_sequential_skip_in_iterations, 0));
|
||||
4, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -409,7 +409,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -431,7 +431,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -466,7 +466,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
7, options.max_sequential_skip_in_iterations, 0));
|
||||
7, options.max_sequential_skip_in_iterations));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -495,7 +495,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -537,7 +537,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
2, options.max_sequential_skip_in_iterations, 0));
|
||||
2, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -568,7 +568,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -597,7 +597,7 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
0, options.max_sequential_skip_in_iterations, 0));
|
||||
0, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
@@ -608,7 +608,7 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
0, options.max_sequential_skip_in_iterations, 0));
|
||||
0, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
@@ -630,7 +630,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkipCountSkips) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -673,7 +673,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, i + 2,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -708,7 +708,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, i + 2,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -736,7 +736,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, 202,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -768,7 +768,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, i,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
|
||||
@@ -784,7 +784,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
200, options.max_sequential_skip_in_iterations, 0));
|
||||
200, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -818,7 +818,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, i + 2,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -852,7 +852,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, i + 2,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -900,7 +900,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 0;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -946,7 +946,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -990,7 +990,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1028,7 +1028,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1063,7 +1063,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1093,7 +1093,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1130,7 +1130,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1167,7 +1167,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, 2 * i + 1,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1220,7 +1220,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, BytewiseComparator(), internal_iter, 2 * i + 1,
|
||||
options.max_sequential_skip_in_iterations, 0));
|
||||
options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1258,7 +1258,7 @@ TEST_F(DBIteratorTest, DBIterator1) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 1, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 1, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1285,7 +1285,7 @@ TEST_F(DBIteratorTest, DBIterator2) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1309,7 +1309,7 @@ TEST_F(DBIteratorTest, DBIterator3) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1333,7 +1333,7 @@ TEST_F(DBIteratorTest, DBIterator4) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 4, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 4, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1365,7 +1365,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
0, options.max_sequential_skip_in_iterations, 0));
|
||||
0, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1387,7 +1387,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
1, options.max_sequential_skip_in_iterations, 0));
|
||||
1, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1409,7 +1409,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
2, options.max_sequential_skip_in_iterations, 0));
|
||||
2, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1431,7 +1431,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
3, options.max_sequential_skip_in_iterations, 0));
|
||||
3, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1453,7 +1453,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
4, options.max_sequential_skip_in_iterations, 0));
|
||||
4, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1475,7 +1475,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
5, options.max_sequential_skip_in_iterations, 0));
|
||||
5, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1497,7 +1497,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
6, options.max_sequential_skip_in_iterations, 0));
|
||||
6, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1517,7 +1517,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
10, options.max_sequential_skip_in_iterations));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -1546,7 +1546,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
0, options.max_sequential_skip_in_iterations, 0));
|
||||
0, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1568,7 +1568,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
1, options.max_sequential_skip_in_iterations, 0));
|
||||
1, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1590,7 +1590,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
2, options.max_sequential_skip_in_iterations, 0));
|
||||
2, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1612,7 +1612,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
3, options.max_sequential_skip_in_iterations, 0));
|
||||
3, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
@@ -1630,7 +1630,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
4, options.max_sequential_skip_in_iterations, 0));
|
||||
4, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1652,7 +1652,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
5, options.max_sequential_skip_in_iterations, 0));
|
||||
5, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1674,7 +1674,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
6, options.max_sequential_skip_in_iterations, 0));
|
||||
6, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1715,7 +1715,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
0, options.max_sequential_skip_in_iterations, 0));
|
||||
0, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1749,7 +1749,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
2, options.max_sequential_skip_in_iterations, 0));
|
||||
2, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1789,7 +1789,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
4, options.max_sequential_skip_in_iterations, 0));
|
||||
4, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1829,7 +1829,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
5, options.max_sequential_skip_in_iterations, 0));
|
||||
5, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1874,7 +1874,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
6, options.max_sequential_skip_in_iterations, 0));
|
||||
6, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1920,7 +1920,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
7, options.max_sequential_skip_in_iterations, 0));
|
||||
7, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1960,7 +1960,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
9, options.max_sequential_skip_in_iterations, 0));
|
||||
9, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2006,7 +2006,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
13, options.max_sequential_skip_in_iterations, 0));
|
||||
13, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2053,7 +2053,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, cf_options, BytewiseComparator(), internal_iter,
|
||||
14, options.max_sequential_skip_in_iterations, 0));
|
||||
14, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2083,7 +2083,7 @@ TEST_F(DBIteratorTest, DBIterator8) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2113,7 +2113,7 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2179,7 +2179,7 @@ TEST_F(DBIteratorTest, DBIterator10) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations));
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2218,7 +2218,7 @@ TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 10, 0 /* force seek */, 0));
|
||||
internal_iter, 10, 0 /* force seek */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2246,7 +2246,7 @@ TEST_F(DBIteratorTest, DBIterator11) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 1, options.max_sequential_skip_in_iterations, 0));
|
||||
internal_iter, 1, options.max_sequential_skip_in_iterations));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2272,7 +2272,7 @@ TEST_F(DBIteratorTest, DBIterator12) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 10, 0, 0));
|
||||
internal_iter, 10, 0));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -2309,7 +2309,7 @@ TEST_F(DBIteratorTest, DBIterator13) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 2, 3, 0));
|
||||
internal_iter, 2, 3));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), key);
|
||||
@@ -2337,7 +2337,7 @@ TEST_F(DBIteratorTest, DBIterator14) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ro, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 4, 1, 0));
|
||||
internal_iter, 4, 1));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2376,7 +2376,7 @@ class DBIterWithMergeIterTest : public testing::Test {
|
||||
db_iter_.reset(NewDBIterator(env_, ro_, ImmutableCFOptions(options_),
|
||||
BytewiseComparator(), merge_iter,
|
||||
8 /* read data earlier than seqId 8 */,
|
||||
3 /* max iterators before reseek */, 0));
|
||||
3 /* max iterators before reseek */));
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
|
||||
@@ -1909,6 +1909,65 @@ TEST_F(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, Refresh) {
|
||||
ASSERT_OK(Put("x", "y"));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Refresh();
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("c")), 0);
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
dbfull()->Flush(FlushOptions());
|
||||
|
||||
ASSERT_OK(Put("m", "n"));
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("c")), 0);
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Refresh();
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("c")), 0);
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("m")), 0);
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter.reset();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -116,6 +116,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorRace) {
|
||||
dbfull()->Flush(FlushOptions());
|
||||
Put("key4", DummyString(1024));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4U);
|
||||
dbfull()->FlushWAL(false);
|
||||
|
||||
{
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
@@ -132,6 +133,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorRace) {
|
||||
|
||||
// "key5" would be written in a new memtable and log
|
||||
Put("key5", DummyString(1024));
|
||||
dbfull()->FlushWAL(false);
|
||||
{
|
||||
// this iter would miss "key4" if not fixed
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
@@ -181,6 +183,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorCorruptedLog) {
|
||||
Put("key"+ToString(i), DummyString(10));
|
||||
}
|
||||
dbfull()->Flush(FlushOptions());
|
||||
dbfull()->FlushWAL(false);
|
||||
// Corrupt this log to create a gap
|
||||
rocksdb::VectorLogPtr wal_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
||||
@@ -194,6 +197,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorCorruptedLog) {
|
||||
|
||||
// Insert a new entry to a new log file
|
||||
Put("key1025", DummyString(10));
|
||||
dbfull()->FlushWAL(false);
|
||||
// Try to read from the beginning. Should stop before the gap and read less
|
||||
// than 1025 entries
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
|
||||
@@ -414,6 +414,7 @@ TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
TEST_F(DBOptionsTest, MaxOpenFilesChange) {
|
||||
SpecialEnv env(env_);
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.max_open_files = -1;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
@@ -27,6 +27,9 @@ class DBRangeDelTest : public DBTestBase {
|
||||
// ROCKSDB_LITE
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBRangeDelTest, NonBlockBasedTableNotSupported) {
|
||||
if (!IsMemoryMappedAccessSupported()) {
|
||||
return;
|
||||
}
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_factory.reset(new PlainTableFactory());
|
||||
opts.prefix_extractor.reset(NewNoopTransform());
|
||||
@@ -816,8 +819,55 @@ TEST_F(DBRangeDelTest, TailingIteratorRangeTombstoneUnsupported) {
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_UBSAN_RUN
|
||||
|
||||
TEST_F(DBRangeDelTest, SubcompactionHasEmptyDedicatedRangeDelFile) {
|
||||
const int kNumFiles = 2, kNumKeysPerFile = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = kNumFiles;
|
||||
options.max_subcompactions = 2;
|
||||
options.num_levels = 2;
|
||||
options.target_file_size_base = 4096;
|
||||
Reopen(options);
|
||||
|
||||
// need a L1 file for subcompaction to be triggered
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), db_->DefaultColumnFamily(), Key(0), "val"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
// put enough keys to fill up the first subcompaction, and later range-delete
|
||||
// them so that the first subcompaction outputs no key-values. In that case
|
||||
// it'll consider making an SST file dedicated to range deletions.
|
||||
for (int i = 0; i < kNumKeysPerFile; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), db_->DefaultColumnFamily(), Key(i),
|
||||
std::string(1024, 'a')));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(0),
|
||||
Key(kNumKeysPerFile)));
|
||||
|
||||
// the above range tombstone can be dropped, so that one alone won't cause a
|
||||
// dedicated file to be opened. We can make one protected by snapshot that
|
||||
// must be considered. Make its range outside the first subcompaction's range
|
||||
// to exercise the tricky part of the code.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(kNumKeysPerFile + 1),
|
||||
Key(kNumKeysPerFile + 2)));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
|
||||
db_->EnableAutoCompaction({db_->DefaultColumnFamily()});
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+11
-7
@@ -335,6 +335,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
ASSERT_OK(s);
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->TEST_SetMaxTrashDBRatio(1.1);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
// Create 4 files in L0
|
||||
@@ -400,6 +401,7 @@ TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
|
||||
env_, nullptr, trash_dir, rate_bytes_per_sec, false, &s));
|
||||
ASSERT_OK(s);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->TEST_SetMaxTrashDBRatio(1.1);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -454,9 +456,14 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
[&](void* arg) { bg_delete_file++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
std::string trash_dir = test::TmpDir(env_) + "/trash";
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, trash_dir, 0, false, &s));
|
||||
ASSERT_OK(s);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create 4 files in L0
|
||||
@@ -469,15 +476,12 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
|
||||
// Close DB and destroy it using DeleteScheduler
|
||||
Close();
|
||||
std::string trash_dir = test::TmpDir(env_) + "/trash";
|
||||
int64_t rate_bytes_per_sec = 1024 * 1024; // 1 Mb / Sec
|
||||
Status s;
|
||||
options.sst_file_manager.reset(NewSstFileManager(
|
||||
env_, nullptr, trash_dir, rate_bytes_per_sec, false, &s));
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
|
||||
sfm->SetDeleteRateBytesPerSecond(1024 * 1024);
|
||||
sfm->delete_scheduler()->TEST_SetMaxTrashDBRatio(1.1);
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
sfm->WaitForEmptyTrash();
|
||||
// We have deleted the 4 sst files in the delete_scheduler
|
||||
ASSERT_EQ(bg_delete_file, 4);
|
||||
|
||||
+285
-1
@@ -23,6 +23,7 @@
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -2234,6 +2235,10 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
virtual Status VerifyChecksum() override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -2790,7 +2795,7 @@ TEST_F(DBTest, FIFOCompactionTestWithCompaction) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
for (int i = 0; i < 60; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
// Generate and flush a file about 20KB.
|
||||
for (int j = 0; j < 20; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j + 2000), RandomString(&rnd, 980)));
|
||||
}
|
||||
@@ -2805,6 +2810,255 @@ TEST_F(DBTest, FIFOCompactionTestWithCompaction) {
|
||||
ASSERT_LE(SizeAtLevel(0),
|
||||
options.compaction_options_fifo.max_table_files_size);
|
||||
}
|
||||
|
||||
// Check that FIFO-with-TTL is not supported with max_open_files != -1.
|
||||
TEST_F(DBTest, FIFOCompactionWithTTLAndMaxOpenFilesTest) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.ttl = 600; // seconds
|
||||
|
||||
// Check that it is not supported with max_open_files != -1.
|
||||
options.max_open_files = 100;
|
||||
options = CurrentOptions(options);
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
|
||||
// Check that FIFO-with-TTL is supported only with BlockBasedTableFactory.
|
||||
TEST_F(DBTest, FIFOCompactionWithTTLAndVariousTableFormatsTest) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.ttl = 600; // seconds
|
||||
|
||||
options = CurrentOptions(options);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory());
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
Destroy(options);
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
Destroy(options);
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
Destroy(options);
|
||||
options.table_factory.reset(NewAdaptiveTableFactory());
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.arena_block_size = 4096;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
// Test to make sure that all files with expired ttl are deleted on next
|
||||
// manual compaction.
|
||||
{
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60 ; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
// Note: Couldn't use SleepForMicroseconds because it takes an int instead
|
||||
// of uint64_t. Hence used addon_time_ directly.
|
||||
// env_->SleepForMicroseconds(2 * 60 * 60 * 1000 * 1000);
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
|
||||
// Since no flushes and compactions have run, the db should still be in
|
||||
// the same state even after considerable time has passed.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
}
|
||||
|
||||
// Test to make sure that all files with expired ttl are deleted on next
|
||||
// automatic compaction.
|
||||
{
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
// Just to make sure that we are in the same state even after sleeping.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Create 1 more file to trigger TTL compaction. The old files are dropped.
|
||||
for (int i = 0; i < 1; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// Only the new 10 files remain.
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
ASSERT_LE(SizeAtLevel(0),
|
||||
options.compaction_options_fifo.max_table_files_size);
|
||||
}
|
||||
|
||||
// Test that shows the fall back to size-based FIFO compaction if TTL-based
|
||||
// deletion doesn't move the total size to be less than max_table_files_size.
|
||||
{
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 3);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
// Just to make sure that we are in the same state even after sleeping.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 3);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 140; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
// Size limit is still guaranteed.
|
||||
ASSERT_LE(SizeAtLevel(0),
|
||||
options.compaction_options_fifo.max_table_files_size);
|
||||
}
|
||||
|
||||
// Test with TTL + Intra-L0 compactions.
|
||||
{
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
// With Intra-L0 compaction, out of 10 files, 6 files will be compacted to 1
|
||||
// (due to level0_file_num_compaction_trigger = 6).
|
||||
// So total files = 1 + remaining 4 = 5.
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 5);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
// Just to make sure that we are in the same state even after sleeping.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 5);
|
||||
|
||||
// Create 10 more files. The old 5 files are dropped as their ttl expired.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 5);
|
||||
ASSERT_LE(SizeAtLevel(0),
|
||||
options.compaction_options_fifo.max_table_files_size);
|
||||
}
|
||||
|
||||
// Test with large TTL + Intra-L0 compactions.
|
||||
// Files dropped based on size, as ttl doesn't kick in.
|
||||
{
|
||||
options.write_buffer_size = 20 << 10; // 20K
|
||||
options.compaction_options_fifo.max_table_files_size = 1500 << 10; // 1.5MB
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
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 20KB.
|
||||
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
|
||||
@@ -5081,6 +5335,36 @@ TEST_F(DBTest, RowCache) {
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, PinnableSliceAndRowCache) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.row_cache = NewLRUCache(8192);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
PinnableSlice pin_slice;
|
||||
ASSERT_EQ(Get("foo", &pin_slice), Status::OK());
|
||||
ASSERT_EQ(pin_slice.ToString(), "bar");
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, DeletingOldWalAfterDrop) {
|
||||
|
||||
@@ -1028,6 +1028,7 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
const int kNumL0Files = 5;
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env; // Make sure to use any custom env that the test is configured with.
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.arena_block_size = kBlockSizeBytes;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
@@ -2234,6 +2235,75 @@ TEST_F(DBTest2, LowPriWrite) {
|
||||
Put("", "", wo);
|
||||
ASSERT_EQ(1, rate_limit_count.load());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
// compaction input has 512KB data
|
||||
const int kNumKeysPerFile = 128;
|
||||
const int kBytesPerKey = 1024;
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
for (auto use_direct_io : {false, true}) {
|
||||
if (use_direct_io && !IsDirectIOSupported()) {
|
||||
continue;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
options.new_table_reader_for_compaction_inputs = true;
|
||||
// takes roughly one second, split into 100 x 10ms intervals. Each interval
|
||||
// permits 5.12KB, which is smaller than the block size, so this test
|
||||
// exercises the code for chunking reads.
|
||||
options.rate_limiter.reset(NewGenericRateLimiter(
|
||||
static_cast<int64_t>(kNumL0Files * kNumKeysPerFile *
|
||||
kBytesPerKey) /* rate_bytes_per_sec */,
|
||||
10 * 1000 /* refill_period_us */, 10 /* fairness */,
|
||||
RateLimiter::Mode::kReadsOnly));
|
||||
options.use_direct_io_for_flush_and_compaction = use_direct_io;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 16384;
|
||||
bbto.no_block_cache = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
for (int j = 0; j <= kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(j), DummyString(kBytesPerKey)));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
|
||||
ASSERT_EQ(0, options.rate_limiter->GetTotalBytesThrough(Env::IO_HIGH));
|
||||
// should be slightly above 512KB due to non-data blocks read. Arbitrarily
|
||||
// chose 1MB as the upper bound on the total bytes read.
|
||||
size_t rate_limited_bytes =
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW);
|
||||
// Include the explict prefetch of the footer in direct I/O case.
|
||||
size_t direct_io_extra = use_direct_io ? 512 * 1024 : 0;
|
||||
ASSERT_GE(rate_limited_bytes,
|
||||
static_cast<size_t>(kNumKeysPerFile * kBytesPerKey * kNumL0Files +
|
||||
direct_io_extra));
|
||||
ASSERT_LT(
|
||||
rate_limited_bytes,
|
||||
static_cast<size_t>(2 * kNumKeysPerFile * kBytesPerKey * kNumL0Files +
|
||||
direct_io_extra));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->value().ToString(), DummyString(kBytesPerKey));
|
||||
}
|
||||
delete iter;
|
||||
// bytes read for user iterator shouldn't count against the rate limit.
|
||||
ASSERT_EQ(rate_limited_bytes,
|
||||
static_cast<size_t>(
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW)));
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+39
-6
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "rocksdb/env_encryption.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -41,10 +42,24 @@ SpecialEnv::SpecialEnv(Env* base)
|
||||
non_writable_count_ = 0;
|
||||
table_write_callback_ = nullptr;
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
ROT13BlockCipher rot13Cipher_(16);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
DBTestBase::DBTestBase(const std::string path)
|
||||
: mem_env_(!getenv("MEM_ENV") ? nullptr : new MockEnv(Env::Default())),
|
||||
env_(new SpecialEnv(mem_env_ ? mem_env_ : Env::Default())),
|
||||
#ifndef ROCKSDB_LITE
|
||||
encrypted_env_(
|
||||
!getenv("ENCRYPTED_ENV")
|
||||
? nullptr
|
||||
: NewEncryptedEnv(mem_env_ ? mem_env_ : Env::Default(),
|
||||
new CTREncryptionProvider(rot13Cipher_))),
|
||||
#else
|
||||
encrypted_env_(nullptr),
|
||||
#endif // ROCKSDB_LITE
|
||||
env_(new SpecialEnv(encrypted_env_
|
||||
? encrypted_env_
|
||||
: (mem_env_ ? mem_env_ : Env::Default()))),
|
||||
option_config_(kDefault) {
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
@@ -281,6 +296,7 @@ Options DBTestBase::GetOptions(
|
||||
"NewWritableFile:O_DIRECT");
|
||||
#endif
|
||||
|
||||
bool can_allow_mmap = IsMemoryMappedAccessSupported();
|
||||
switch (option_config) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
case kHashSkipList:
|
||||
@@ -291,14 +307,14 @@ Options DBTestBase::GetOptions(
|
||||
case kPlainTableFirstBytePrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.allow_mmap_reads = true;
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
case kPlainTableCappedPrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(8));
|
||||
options.allow_mmap_reads = true;
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
@@ -312,7 +328,7 @@ Options DBTestBase::GetOptions(
|
||||
case kPlainTableAllBytesPrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
options.allow_mmap_reads = true;
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
@@ -364,7 +380,7 @@ Options DBTestBase::GetOptions(
|
||||
options.wal_dir = alternative_wal_dir_;
|
||||
// mmap reads should be orthogonal to WalDir setting, so we piggyback to
|
||||
// this option config to test mmap reads as well
|
||||
options.allow_mmap_reads = true;
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
break;
|
||||
case kManifestFileSize:
|
||||
options.max_manifest_file_size = 50; // 50 bytes
|
||||
@@ -384,7 +400,7 @@ Options DBTestBase::GetOptions(
|
||||
options.num_levels = 8;
|
||||
break;
|
||||
case kCompressedBlockCache:
|
||||
options.allow_mmap_writes = true;
|
||||
options.allow_mmap_writes = can_allow_mmap;
|
||||
table_options.block_cache_compressed = NewLRUCache(8 * 1024 * 1024);
|
||||
break;
|
||||
case kInfiniteMaxOpenFiles:
|
||||
@@ -469,6 +485,12 @@ Options DBTestBase::GetOptions(
|
||||
options.enable_pipelined_write = true;
|
||||
break;
|
||||
}
|
||||
case kConcurrentWALWrites: {
|
||||
// This options optimize 2PC commit path
|
||||
options.concurrent_prepare = true;
|
||||
options.manual_wal_flush = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
@@ -592,6 +614,10 @@ bool DBTestBase::IsDirectIOSupported() {
|
||||
return s.ok();
|
||||
}
|
||||
|
||||
bool DBTestBase::IsMemoryMappedAccessSupported() const {
|
||||
return (!encrypted_env_);
|
||||
}
|
||||
|
||||
Status DBTestBase::Flush(int cf) {
|
||||
if (cf == 0) {
|
||||
return db_->Flush(FlushOptions());
|
||||
@@ -671,6 +697,13 @@ std::string DBTestBase::Get(int cf, const std::string& k,
|
||||
return result;
|
||||
}
|
||||
|
||||
Status DBTestBase::Get(const std::string& k, PinnableSlice* v) {
|
||||
ReadOptions options;
|
||||
options.verify_checksums = true;
|
||||
Status s = dbfull()->Get(options, dbfull()->DefaultColumnFamily(), k, v);
|
||||
return s;
|
||||
}
|
||||
|
||||
uint64_t DBTestBase::GetNumSnapshots() {
|
||||
uint64_t int_num;
|
||||
EXPECT_TRUE(dbfull()->GetIntProperty("rocksdb.num-snapshots", &int_num));
|
||||
|
||||
+30
-9
@@ -247,6 +247,13 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status RangeSync(uint64_t offset, uint64_t nbytes) override {
|
||||
Status s = base_->RangeSync(offset, nbytes);
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT_CALLBACK("SpecialEnv::SStableFile::RangeSync", &s);
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
return s;
|
||||
}
|
||||
Status Close() override {
|
||||
// SyncPoint is not supported in Released Windows Mode.
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
@@ -256,7 +263,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
TEST_SYNC_POINT_CALLBACK("DBTestWritableFile.GetPreallocationStatus",
|
||||
&preallocation_size);
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
return base_->Close();
|
||||
Status s = base_->Close();
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT_CALLBACK("SpecialEnv::SStableFile::Close", &s);
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
return s;
|
||||
}
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
Status Sync() override {
|
||||
@@ -264,7 +275,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
while (env_->delay_sstable_sync_.load(std::memory_order_acquire)) {
|
||||
env_->SleepForMicroseconds(100000);
|
||||
}
|
||||
return base_->Sync();
|
||||
Status s = base_->Sync();
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT_CALLBACK("SpecialEnv::SStableFile::Sync", &s);
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
return s;
|
||||
}
|
||||
void SetIOPriority(Env::IOPriority pri) override {
|
||||
base_->SetIOPriority(pri);
|
||||
@@ -641,13 +656,14 @@ class DBTestBase : public testing::Test {
|
||||
kRecycleLogFiles = 28,
|
||||
kConcurrentSkipList = 29,
|
||||
kPipelinedWrite = 30,
|
||||
kEnd = 31,
|
||||
kDirectIO = 32,
|
||||
kLevelSubcompactions = 33,
|
||||
kUniversalSubcompactions = 34,
|
||||
kBlockBasedTableWithIndexRestartInterval = 35,
|
||||
kBlockBasedTableWithPartitionedIndex = 36,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions = 37,
|
||||
kConcurrentWALWrites = 31,
|
||||
kEnd = 32,
|
||||
kDirectIO = 33,
|
||||
kLevelSubcompactions = 34,
|
||||
kUniversalSubcompactions = 35,
|
||||
kBlockBasedTableWithIndexRestartInterval = 36,
|
||||
kBlockBasedTableWithPartitionedIndex = 37,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions = 38,
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -655,6 +671,7 @@ class DBTestBase : public testing::Test {
|
||||
std::string alternative_wal_dir_;
|
||||
std::string alternative_db_log_dir_;
|
||||
MockEnv* mem_env_;
|
||||
Env* encrypted_env_;
|
||||
SpecialEnv* env_;
|
||||
DB* db_;
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
@@ -758,6 +775,8 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
bool IsDirectIOSupported();
|
||||
|
||||
bool IsMemoryMappedAccessSupported() const;
|
||||
|
||||
Status Flush(int cf = 0);
|
||||
|
||||
Status Put(const Slice& k, const Slice& v, WriteOptions wo = WriteOptions());
|
||||
@@ -784,6 +803,8 @@ class DBTestBase : public testing::Test {
|
||||
std::string Get(int cf, const std::string& k,
|
||||
const Snapshot* snapshot = nullptr);
|
||||
|
||||
Status Get(const std::string& k, PinnableSlice* v);
|
||||
|
||||
uint64_t GetNumSnapshots();
|
||||
|
||||
uint64_t GetTimeOldestSnapshots();
|
||||
|
||||
@@ -1370,6 +1370,103 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, FullCompactionInBottomPriThreadPool) {
|
||||
const int kNumFilesTrigger = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
for (bool allow_ingest_behind : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.allow_ingest_behind = allow_ingest_behind;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int num_bottom_pri_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkBottomCompaction",
|
||||
[&](void* arg) { ++num_bottom_pri_compactions; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
ASSERT_EQ(NumSortedRuns(), num);
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
if (allow_ingest_behind || num_levels_ > 1) {
|
||||
// allow_ingest_behind increases number of levels while sanitizing.
|
||||
ASSERT_EQ(1, num_bottom_pri_compactions);
|
||||
} else {
|
||||
// for single-level universal, everything's bottom level so nothing should
|
||||
// be executed in bottom-pri thread pool.
|
||||
ASSERT_EQ(0, num_bottom_pri_compactions);
|
||||
}
|
||||
// Verify that size amplification did occur
|
||||
ASSERT_EQ(NumSortedRuns(), 1);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, ConcurrentBottomPriLowPriCompactions) {
|
||||
if (num_levels_ == 1) {
|
||||
// for single-level universal, everything's bottom level so nothing should
|
||||
// be executed in bottom-pri thread pool.
|
||||
return;
|
||||
}
|
||||
const int kNumFilesTrigger = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{// wait for the full compaction to be picked before adding files intended
|
||||
// for the second one.
|
||||
{"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0"},
|
||||
// the full (bottom-pri) compaction waits until a partial (low-pri)
|
||||
// compaction has started to verify they can run in parallel.
|
||||
{"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
"DBImpl::BGWorkBottomCompaction"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, true /* no_wait */);
|
||||
// use no_wait above because that one waits for flush and compaction. We
|
||||
// don't want to wait for compaction because the full compaction is
|
||||
// intentionally blocked while more files are flushed.
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
if (i == 0) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBTestUniversalCompaction:ConcurrentBottomPriLowPriCompactions:0");
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// First compaction should output to bottom level. Second should output to L0
|
||||
// since older L0 files pending compaction prevent it from being placed lower.
|
||||
ASSERT_EQ(NumSortedRuns(), 2);
|
||||
ASSERT_GT(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(num_levels_ - 1), 0);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(UniversalCompactionNumLevels, DBTestUniversalCompaction,
|
||||
::testing::Combine(::testing::Values(1, 3, 5),
|
||||
::testing::Bool()));
|
||||
|
||||
+7
-2
@@ -120,9 +120,11 @@ TEST_F(DBWALTest, SyncWALNotWaitWrite) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
rocksdb::port::Thread thread([&]() { ASSERT_OK(Put("foo2", "bar2")); });
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
// Moving this to SyncWAL before the actual fsync
|
||||
// TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
// Moving this to SyncWAL after actual fsync
|
||||
// TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
thread.join();
|
||||
|
||||
@@ -658,6 +660,7 @@ TEST_F(DBWALTest, PartOfWritesWithWALDisabled) {
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_OK(Put(0, "key", "v5", wal_on)); // seq id 5
|
||||
ASSERT_EQ("v5", Get(0, "key"));
|
||||
dbfull()->FlushWAL(false);
|
||||
// Simulate a crash.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
Close();
|
||||
@@ -727,6 +730,7 @@ class RecoveryTestHelper {
|
||||
batch.Put(key, value);
|
||||
WriteBatchInternal::SetSequence(&batch, seq);
|
||||
current_log_writer->AddRecord(WriteBatchInternal::Contents(&batch));
|
||||
versions->SetLastToBeWrittenSequence(seq);
|
||||
versions->SetLastSequence(seq);
|
||||
}
|
||||
}
|
||||
@@ -1111,6 +1115,7 @@ TEST_F(DBWALTest, RecoverWithoutFlushMultipleCF) {
|
||||
ASSERT_EQ(3, countWalFiles());
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(2, "key7", kLargeValue));
|
||||
dbfull()->FlushWAL(false);
|
||||
ASSERT_EQ(4, countWalFiles());
|
||||
|
||||
// Reopen twice and validate.
|
||||
|
||||
@@ -69,6 +69,7 @@ TEST_P(DBWriteTest, ReturnSeuqneceNumberMultiThreaded) {
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
|
||||
testing::Values(DBTestBase::kDefault,
|
||||
DBTestBase::kConcurrentWALWrites,
|
||||
DBTestBase::kPipelinedWrite));
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+12
-1
@@ -78,10 +78,18 @@ struct ParsedInternalKey {
|
||||
SequenceNumber sequence;
|
||||
ValueType type;
|
||||
|
||||
ParsedInternalKey() { } // Intentionally left uninitialized (for speed)
|
||||
ParsedInternalKey()
|
||||
: sequence(kMaxSequenceNumber) // Make code analyzer happy
|
||||
{} // Intentionally left uninitialized (for speed)
|
||||
ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
|
||||
: user_key(u), sequence(seq), type(t) { }
|
||||
std::string DebugString(bool hex = false) const;
|
||||
|
||||
void clear() {
|
||||
user_key.clear();
|
||||
sequence = 0;
|
||||
type = kTypeDeletion;
|
||||
}
|
||||
};
|
||||
|
||||
// Return the length of the encoding of "key".
|
||||
@@ -149,6 +157,9 @@ class InternalKeyComparator : public Comparator {
|
||||
|
||||
int Compare(const InternalKey& a, const InternalKey& b) const;
|
||||
int Compare(const ParsedInternalKey& a, const ParsedInternalKey& b) const;
|
||||
virtual const Comparator* GetRootComparator() const override {
|
||||
return user_comparator_->GetRootComparator();
|
||||
}
|
||||
};
|
||||
|
||||
// Modules in this directory should keep internal keys wrapped inside
|
||||
|
||||
@@ -37,6 +37,24 @@ void EventHelpers::NotifyTableFileCreationStarted(
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
void EventHelpers::NotifyOnBackgroundError(
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
BackgroundErrorReason reason, Status* bg_error,
|
||||
InstrumentedMutex* db_mutex) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (listeners.size() == 0U) {
|
||||
return;
|
||||
}
|
||||
db_mutex->AssertHeld();
|
||||
// release lock while notifying events
|
||||
db_mutex->Unlock();
|
||||
for (auto& listener : listeners) {
|
||||
listener->OnBackgroundError(reason, bg_error);
|
||||
}
|
||||
db_mutex->Lock();
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
EventLogger* event_logger,
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
|
||||
@@ -25,6 +25,10 @@ class EventHelpers {
|
||||
const std::string& db_name, const std::string& cf_name,
|
||||
const std::string& file_path, int job_id, TableFileCreationReason reason);
|
||||
#endif // !ROCKSDB_LITE
|
||||
static void NotifyOnBackgroundError(
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
BackgroundErrorReason reason, Status* bg_error,
|
||||
InstrumentedMutex* db_mutex);
|
||||
static void LogAndNotifyTableFileCreationFinished(
|
||||
EventLogger* event_logger,
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
|
||||
+5
-7
@@ -14,20 +14,18 @@ namespace experimental {
|
||||
|
||||
Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto dbimpl = dynamic_cast<DBImpl*>(db);
|
||||
if (dbimpl == nullptr) {
|
||||
return Status::InvalidArgument("Didn't recognize DB object");
|
||||
if (db == nullptr) {
|
||||
return Status::InvalidArgument("DB is empty");
|
||||
}
|
||||
|
||||
return dbimpl->SuggestCompactRange(column_family, begin, end);
|
||||
return db->SuggestCompactRange(column_family, begin, end);
|
||||
}
|
||||
|
||||
Status PromoteL0(DB* db, ColumnFamilyHandle* column_family, int target_level) {
|
||||
auto dbimpl = dynamic_cast<DBImpl*>(db);
|
||||
if (dbimpl == nullptr) {
|
||||
if (db == nullptr) {
|
||||
return Status::InvalidArgument("Didn't recognize DB object");
|
||||
}
|
||||
return dbimpl->PromoteL0(column_family, target_level);
|
||||
return db->PromoteL0(column_family, target_level);
|
||||
}
|
||||
|
||||
#else // ROCKSDB_LITE
|
||||
|
||||
@@ -144,6 +144,8 @@ Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// REQUIRES: we have become the only writer by entering both write_thread_ and
|
||||
// nonmem_write_thread_
|
||||
Status ExternalSstFileIngestionJob::Run() {
|
||||
Status status;
|
||||
#ifndef NDEBUG
|
||||
@@ -162,6 +164,8 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
// if the dont overlap with any ranges since we have snapshots
|
||||
force_global_seqno = true;
|
||||
}
|
||||
// It is safe to use this instead of LastToBeWrittenSequence since we are
|
||||
// the only active writer, and hence they are equal
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
edit_.SetColumnFamily(cfd_->GetID());
|
||||
@@ -195,6 +199,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
}
|
||||
|
||||
if (consumed_seqno) {
|
||||
versions_->SetLastToBeWrittenSequence(last_seqno + 1);
|
||||
versions_->SetLastSequence(last_seqno + 1);
|
||||
}
|
||||
|
||||
@@ -279,7 +284,8 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
sst_file_reader.reset(new RandomAccessFileReader(std::move(sst_file)));
|
||||
sst_file_reader.reset(new RandomAccessFileReader(std::move(sst_file),
|
||||
external_file));
|
||||
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(*cfd_->ioptions(), env_options_,
|
||||
|
||||
@@ -410,6 +410,7 @@ TEST_P(FaultInjectionTest, WriteOptionSyncTest) {
|
||||
write_options.sync = true;
|
||||
ASSERT_OK(
|
||||
db_->Put(write_options, Key(2, &key_space), Value(2, &value_space)));
|
||||
db_->FlushWAL(false);
|
||||
|
||||
env_->SetFilesystemActive(false);
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
@@ -494,7 +495,7 @@ TEST_P(FaultInjectionTest, ManualLogSyncTest) {
|
||||
ASSERT_OK(db_->Flush(flush_options));
|
||||
ASSERT_OK(
|
||||
db_->Put(write_options, Key(2, &key_space), Value(2, &value_space)));
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
ASSERT_OK(db_->FlushWAL(true));
|
||||
|
||||
env_->SetFilesystemActive(false);
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
|
||||
+6
-1
@@ -296,6 +296,11 @@ Status FlushJob::WriteLevel0Table() {
|
||||
&output_compression_);
|
||||
EnvOptions optimized_env_options =
|
||||
db_options_.env->OptimizeForCompactionTableWrite(env_options_, db_options_);
|
||||
|
||||
int64_t _current_time = 0;
|
||||
db_options_.env->GetCurrentTime(&_current_time); // ignore error
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
optimized_env_options, cfd_->table_cache(), iter.get(),
|
||||
@@ -306,7 +311,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cfd_->ioptions()->compression_opts,
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
TableFileCreationReason::kFlush, event_logger_, job_context_->job_id,
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */);
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */, current_time);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class FlushJob {
|
||||
~FlushJob();
|
||||
|
||||
// Require db_mutex held.
|
||||
// Once PickMemTable() is called, either Run() or Cancel() has to be call.
|
||||
// Once PickMemTable() is called, either Run() or Cancel() has to be called.
|
||||
void PickMemTable();
|
||||
Status Run(FileMetaData* file_meta = nullptr);
|
||||
void Cancel();
|
||||
|
||||
@@ -542,7 +542,7 @@ bool InternalStats::HandleDBStats(std::string* value, Slice suffix) {
|
||||
|
||||
bool InternalStats::HandleSsTables(std::string* value, Slice suffix) {
|
||||
auto* current = cfd_->current();
|
||||
*value = current->DebugString();
|
||||
*value = current->DebugString(true, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -270,10 +270,15 @@ class InternalStats {
|
||||
++cf_stats_count_[type];
|
||||
}
|
||||
|
||||
void AddDBStats(InternalDBStatsType type, uint64_t value) {
|
||||
void AddDBStats(InternalDBStatsType type, uint64_t value,
|
||||
bool concurrent = false) {
|
||||
auto& v = db_stats_[type];
|
||||
v.store(v.load(std::memory_order_relaxed) + value,
|
||||
std::memory_order_relaxed);
|
||||
if (concurrent) {
|
||||
v.fetch_add(value, std::memory_order_relaxed);
|
||||
} else {
|
||||
v.store(v.load(std::memory_order_relaxed) + value,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetDBStats(InternalDBStatsType type) {
|
||||
@@ -548,7 +553,8 @@ class InternalStats {
|
||||
|
||||
void AddCFStats(InternalCFStatsType type, uint64_t value) {}
|
||||
|
||||
void AddDBStats(InternalDBStatsType type, uint64_t value) {}
|
||||
void AddDBStats(InternalDBStatsType type, uint64_t value,
|
||||
bool concurrent = false) {}
|
||||
|
||||
HistogramImpl* GetFileReadHist(int level) { return nullptr; }
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ TEST_F(EventListenerTest, OnSingleDBCompactionTest) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = kEntrySize * kEntriesPerBuffer;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
@@ -231,6 +232,7 @@ class TestFlushListener : public EventListener {
|
||||
|
||||
TEST_F(EventListenerTest, OnSingleDBFlushTest) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.write_buffer_size = k110KB;
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
options.enable_thread_tracking = true;
|
||||
@@ -267,6 +269,7 @@ TEST_F(EventListenerTest, OnSingleDBFlushTest) {
|
||||
|
||||
TEST_F(EventListenerTest, MultiCF) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.write_buffer_size = k110KB;
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
options.enable_thread_tracking = true;
|
||||
@@ -302,6 +305,7 @@ TEST_F(EventListenerTest, MultiCF) {
|
||||
|
||||
TEST_F(EventListenerTest, MultiDBMultiListeners) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
options.enable_thread_tracking = true;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
@@ -384,6 +388,7 @@ TEST_F(EventListenerTest, MultiDBMultiListeners) {
|
||||
|
||||
TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
options.enable_thread_tracking = true;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
@@ -431,6 +436,7 @@ class TestCompactionReasonListener : public EventListener {
|
||||
|
||||
TEST_F(EventListenerTest, CompactionReasonLevel) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(DBTestBase::kNumKeysByGenerateNewRandomFile));
|
||||
@@ -496,6 +502,7 @@ TEST_F(EventListenerTest, CompactionReasonLevel) {
|
||||
|
||||
TEST_F(EventListenerTest, CompactionReasonUniversal) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(DBTestBase::kNumKeysByGenerateNewRandomFile));
|
||||
@@ -557,6 +564,7 @@ TEST_F(EventListenerTest, CompactionReasonUniversal) {
|
||||
|
||||
TEST_F(EventListenerTest, CompactionReasonFIFO) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(DBTestBase::kNumKeysByGenerateNewRandomFile));
|
||||
@@ -779,6 +787,7 @@ TEST_F(EventListenerTest, ColumnFamilyHandleDeletionStartedListenerTest) {
|
||||
auto listener =
|
||||
std::make_shared<ColumnFamilyHandleDeletionStartedListener>(cfs);
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.listeners.push_back(listener);
|
||||
CreateAndReopenWithCF(cfs, options);
|
||||
@@ -790,6 +799,92 @@ TEST_F(EventListenerTest, ColumnFamilyHandleDeletionStartedListenerTest) {
|
||||
ASSERT_EQ(listener->getCounter(), 3);
|
||||
}
|
||||
|
||||
class BackgroundErrorListener : public EventListener {
|
||||
private:
|
||||
SpecialEnv* env_;
|
||||
int counter_;
|
||||
|
||||
public:
|
||||
BackgroundErrorListener(SpecialEnv* env) : env_(env), counter_(0) {}
|
||||
|
||||
void OnBackgroundError(BackgroundErrorReason reason, Status* bg_error) override {
|
||||
if (counter_ == 0) {
|
||||
// suppress the first error and disable write-dropping such that a retry
|
||||
// can succeed.
|
||||
*bg_error = Status::OK();
|
||||
env_->drop_writes_.store(false, std::memory_order_release);
|
||||
env_->no_slowdown_ = false;
|
||||
}
|
||||
++counter_;
|
||||
}
|
||||
|
||||
int counter() { return counter_; }
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, BackgroundErrorListenerFailedFlushTest) {
|
||||
auto listener = std::make_shared<BackgroundErrorListener>(env_);
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.listeners.push_back(listener);
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(1));
|
||||
options.paranoid_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// the usual TEST_WaitForFlushMemTable() doesn't work for failed flushes, so
|
||||
// forge a custom one for the failed flush case.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkFlush:done",
|
||||
"EventListenerTest:BackgroundErrorListenerFailedFlushTest:1"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->drop_writes_.store(true, std::memory_order_release);
|
||||
env_->no_slowdown_ = true;
|
||||
|
||||
ASSERT_OK(Put("key0", "val"));
|
||||
ASSERT_OK(Put("key1", "val"));
|
||||
TEST_SYNC_POINT("EventListenerTest:BackgroundErrorListenerFailedFlushTest:1");
|
||||
ASSERT_EQ(1, listener->counter());
|
||||
ASSERT_OK(Put("key2", "val"));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(EventListenerTest, BackgroundErrorListenerFailedCompactionTest) {
|
||||
auto listener = std::make_shared<BackgroundErrorListener>(env_);
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.push_back(listener);
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
options.paranoid_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// third iteration triggers the second memtable's flush
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
ASSERT_OK(Put("key0", "val"));
|
||||
if (i > 0) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
ASSERT_OK(Put("key1", "val"));
|
||||
}
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
|
||||
env_->drop_writes_.store(true, std::memory_order_release);
|
||||
env_->no_slowdown_ = true;
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(1, listener->counter());
|
||||
|
||||
// trigger flush so compaction is triggered again; this time it succeeds
|
||||
ASSERT_OK(Put("key0", "val"));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+10
-6
@@ -18,20 +18,22 @@
|
||||
namespace rocksdb {
|
||||
namespace log {
|
||||
|
||||
Writer::Writer(unique_ptr<WritableFileWriter>&& dest,
|
||||
uint64_t log_number, bool recycle_log_files)
|
||||
Writer::Writer(unique_ptr<WritableFileWriter>&& dest, uint64_t log_number,
|
||||
bool recycle_log_files, bool manual_flush)
|
||||
: dest_(std::move(dest)),
|
||||
block_offset_(0),
|
||||
log_number_(log_number),
|
||||
recycle_log_files_(recycle_log_files) {
|
||||
recycle_log_files_(recycle_log_files),
|
||||
manual_flush_(manual_flush) {
|
||||
for (int i = 0; i <= kMaxRecordType; i++) {
|
||||
char t = static_cast<char>(i);
|
||||
type_crc_[i] = crc32c::Value(&t, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Writer::~Writer() {
|
||||
}
|
||||
Writer::~Writer() { WriteBuffer(); }
|
||||
|
||||
Status Writer::WriteBuffer() { return dest_->Flush(); }
|
||||
|
||||
Status Writer::AddRecord(const Slice& slice) {
|
||||
const char* ptr = slice.data();
|
||||
@@ -127,7 +129,9 @@ Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(ptr, n));
|
||||
if (s.ok()) {
|
||||
s = dest_->Flush();
|
||||
if (!manual_flush_) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
block_offset_ += header_size + n;
|
||||
|
||||
+8
-2
@@ -72,8 +72,8 @@ class Writer {
|
||||
// Create a writer that will append data to "*dest".
|
||||
// "*dest" must be initially empty.
|
||||
// "*dest" must remain live while this Writer is in use.
|
||||
explicit Writer(unique_ptr<WritableFileWriter>&& dest,
|
||||
uint64_t log_number, bool recycle_log_files);
|
||||
explicit Writer(unique_ptr<WritableFileWriter>&& dest, uint64_t log_number,
|
||||
bool recycle_log_files, bool manual_flush = false);
|
||||
~Writer();
|
||||
|
||||
Status AddRecord(const Slice& slice);
|
||||
@@ -83,6 +83,8 @@ class Writer {
|
||||
|
||||
uint64_t get_log_number() const { return log_number_; }
|
||||
|
||||
Status WriteBuffer();
|
||||
|
||||
private:
|
||||
unique_ptr<WritableFileWriter> dest_;
|
||||
size_t block_offset_; // Current offset in block
|
||||
@@ -96,6 +98,10 @@ class Writer {
|
||||
|
||||
Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
|
||||
// If true, it does not flush after each write. Instead it relies on the upper
|
||||
// layer to manually does the flush by calling ::WriteBuffer()
|
||||
bool manual_flush_;
|
||||
|
||||
// No copying allowed
|
||||
Writer(const Writer&);
|
||||
void operator=(const Writer&);
|
||||
|
||||
+3
-4
@@ -201,12 +201,11 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
ikey.sequence <= latest_snapshot_
|
||||
? CompactionFilter::Decision::kKeep
|
||||
: FilterMerge(orig_ikey.user_key, value_slice);
|
||||
if (range_del_agg != nullptr &&
|
||||
|
||||
if (filter != CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
||||
range_del_agg != nullptr &&
|
||||
range_del_agg->ShouldDelete(
|
||||
iter->key(),
|
||||
RangeDelAggregator::RangePositioningMode::kForwardTraversal) &&
|
||||
filter != CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
RangeDelAggregator::RangePositioningMode::kForwardTraversal)) {
|
||||
filter = CompactionFilter::Decision::kRemove;
|
||||
}
|
||||
if (filter == CompactionFilter::Decision::kKeep ||
|
||||
|
||||
+26
-6
@@ -176,6 +176,7 @@ class Repairer {
|
||||
status = db_impl->NewDB();
|
||||
delete db_impl;
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
// Recover using the fresh manifest created by NewDB()
|
||||
status =
|
||||
@@ -246,9 +247,21 @@ class Repairer {
|
||||
Status FindFiles() {
|
||||
std::vector<std::string> filenames;
|
||||
bool found_file = false;
|
||||
std::vector<std::string> to_search_paths;
|
||||
|
||||
for (size_t path_id = 0; path_id < db_options_.db_paths.size(); path_id++) {
|
||||
to_search_paths.push_back(db_options_.db_paths[path_id].path);
|
||||
}
|
||||
|
||||
// search wal_dir if user uses a customize wal_dir
|
||||
if (!db_options_.wal_dir.empty() &&
|
||||
db_options_.wal_dir != dbname_) {
|
||||
to_search_paths.push_back(db_options_.wal_dir);
|
||||
}
|
||||
|
||||
for (size_t path_id = 0; path_id < to_search_paths.size(); path_id++) {
|
||||
Status status =
|
||||
env_->GetChildren(db_options_.db_paths[path_id].path, &filenames);
|
||||
env_->GetChildren(to_search_paths[path_id], &filenames);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -261,14 +274,12 @@ class Repairer {
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type)) {
|
||||
if (type == kDescriptorFile) {
|
||||
assert(path_id == 0);
|
||||
manifests_.push_back(filenames[i]);
|
||||
} else {
|
||||
if (number + 1 > next_file_number_) {
|
||||
next_file_number_ = number + 1;
|
||||
}
|
||||
if (type == kLogFile) {
|
||||
assert(path_id == 0);
|
||||
logs_.push_back(number);
|
||||
} else if (type == kTableFile) {
|
||||
table_fds_.emplace_back(number, static_cast<uint32_t>(path_id),
|
||||
@@ -288,7 +299,8 @@ class Repairer {
|
||||
|
||||
void ConvertLogFilesToTables() {
|
||||
for (size_t i = 0; i < logs_.size(); i++) {
|
||||
std::string logname = LogFileName(dbname_, logs_[i]);
|
||||
// we should use LogFileName(wal_dir, logs_[i]) here. user might uses wal_dir option.
|
||||
std::string logname = LogFileName(db_options_.wal_dir, logs_[i]);
|
||||
Status status = ConvertLogToTable(logs_[i]);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
@@ -312,7 +324,7 @@ class Repairer {
|
||||
};
|
||||
|
||||
// Open the log file
|
||||
std::string logname = LogFileName(dbname_, log);
|
||||
std::string logname = LogFileName(db_options_.wal_dir, log);
|
||||
unique_ptr<SequentialFile> lfile;
|
||||
Status status = env_->NewSequentialFile(
|
||||
logname, &lfile, env_->OptimizeForLogRead(env_options_));
|
||||
@@ -380,6 +392,11 @@ class Repairer {
|
||||
ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
|
||||
EnvOptions optimized_env_options =
|
||||
env_->OptimizeForCompactionTableWrite(env_options_, immutable_db_options_);
|
||||
|
||||
int64_t _current_time = 0;
|
||||
status = env_->GetCurrentTime(&_current_time); // ignore error
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
status = BuildTable(
|
||||
dbname_, env_, *cfd->ioptions(), *cfd->GetLatestMutableCFOptions(),
|
||||
optimized_env_options, table_cache_, iter.get(),
|
||||
@@ -387,7 +404,9 @@ class Repairer {
|
||||
&meta, cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
{}, kMaxSequenceNumber, kNoCompression, CompressionOptions(), false,
|
||||
nullptr /* internal_stats */, TableFileCreationReason::kRecovery);
|
||||
nullptr /* internal_stats */, TableFileCreationReason::kRecovery,
|
||||
nullptr /* event_logger */, 0 /* job_id */, Env::IO_HIGH,
|
||||
nullptr /* table_properties */, -1 /* level */, current_time);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s",
|
||||
log, counter, meta.fd.GetNumber(),
|
||||
@@ -522,6 +541,7 @@ class Repairer {
|
||||
max_sequence = tables_[i].max_sequence;
|
||||
}
|
||||
}
|
||||
vset_.SetLastToBeWrittenSequence(max_sequence);
|
||||
vset_.SetLastSequence(max_sequence);
|
||||
|
||||
for (const auto& cf_id_and_tables : cf_id_to_tables) {
|
||||
|
||||
@@ -174,6 +174,40 @@ TEST_F(RepairTest, UnflushedSst) {
|
||||
ASSERT_EQ(Get("key"), "val");
|
||||
}
|
||||
|
||||
TEST_F(RepairTest, SeparateWalDir) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
Put("key", "val");
|
||||
Put("foo", "bar");
|
||||
VectorLogPtr wal_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
||||
ASSERT_EQ(wal_files.size(), 1);
|
||||
uint64_t total_ssts_size;
|
||||
GetAllSSTFiles(&total_ssts_size);
|
||||
ASSERT_EQ(total_ssts_size, 0);
|
||||
std::string manifest_path =
|
||||
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
||||
|
||||
Close();
|
||||
ASSERT_OK(env_->FileExists(manifest_path));
|
||||
ASSERT_OK(env_->DeleteFile(manifest_path));
|
||||
ASSERT_OK(RepairDB(dbname_, options));
|
||||
|
||||
// make sure that all WALs are converted to SSTables.
|
||||
options.wal_dir = "";
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
||||
ASSERT_EQ(wal_files.size(), 0);
|
||||
GetAllSSTFiles(&total_ssts_size);
|
||||
ASSERT_GT(total_ssts_size, 0);
|
||||
ASSERT_EQ(Get("key"), "val");
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
|
||||
} while(ChangeWalOptions());
|
||||
}
|
||||
|
||||
TEST_F(RepairTest, RepairMultipleColumnFamilies) {
|
||||
// Verify repair logic associates SST files with their original column
|
||||
// families.
|
||||
|
||||
+28
-9
@@ -89,7 +89,8 @@ Status TableCache::GetTableReader(
|
||||
const InternalKeyComparator& internal_comparator, const FileDescriptor& fd,
|
||||
bool sequential_mode, size_t readahead, bool record_read_stats,
|
||||
HistogramImpl* file_read_hist, unique_ptr<TableReader>* table_reader,
|
||||
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache) {
|
||||
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache,
|
||||
bool for_compaction) {
|
||||
std::string fname =
|
||||
TableFileName(ioptions_.db_paths, fd.GetNumber(), fd.GetPathId());
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
@@ -105,9 +106,10 @@ Status TableCache::GetTableReader(
|
||||
}
|
||||
StopWatch sw(ioptions_.env, ioptions_.statistics, TABLE_OPEN_IO_MICROS);
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), ioptions_.env,
|
||||
ioptions_.statistics, record_read_stats,
|
||||
file_read_hist));
|
||||
new RandomAccessFileReader(
|
||||
std::move(file), fname, ioptions_.env,
|
||||
record_read_stats ? ioptions_.statistics : nullptr, SST_READ_MICROS,
|
||||
file_read_hist, ioptions_.rate_limiter, for_compaction));
|
||||
s = ioptions_.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions_, env_options, internal_comparator,
|
||||
skip_filters, level),
|
||||
@@ -203,7 +205,8 @@ InternalIterator* TableCache::NewIterator(
|
||||
s = GetTableReader(
|
||||
env_options, icomparator, fd, true /* sequential_mode */, readahead,
|
||||
!for_compaction /* record stats */, nullptr, &table_reader_unique_ptr,
|
||||
false /* skip_filters */, level);
|
||||
false /* skip_filters */, level,
|
||||
true /* prefetch_index_and_filter_in_cache */, for_compaction);
|
||||
if (s.ok()) {
|
||||
table_reader = table_reader_unique_ptr.release();
|
||||
}
|
||||
@@ -222,8 +225,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
}
|
||||
InternalIterator* result = nullptr;
|
||||
if (s.ok()) {
|
||||
result =
|
||||
table_reader->NewIterator(options, arena, &icomparator, skip_filters);
|
||||
result = table_reader->NewIterator(options, arena, skip_filters);
|
||||
if (create_new_table_reader) {
|
||||
assert(handle == nullptr);
|
||||
result->RegisterCleanup(&DeleteTableReader, table_reader, nullptr);
|
||||
@@ -308,6 +310,7 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
#ifndef ROCKSDB_LITE
|
||||
IterKey row_cache_key;
|
||||
std::string row_cache_entry_buffer;
|
||||
|
||||
// Check row cache if enabled. Since row cache does not currently store
|
||||
// sequence numbers, we cannot use it if we need to fetch the sequence.
|
||||
if (ioptions_.row_cache && !get_context->NeedToReadSequence()) {
|
||||
@@ -331,10 +334,26 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
|
||||
if (auto row_handle =
|
||||
ioptions_.row_cache->Lookup(row_cache_key.GetUserKey())) {
|
||||
// Cleanable routine to release the cache entry
|
||||
Cleanable value_pinner;
|
||||
auto release_cache_entry_func = [](void* cache_to_clean,
|
||||
void* cache_handle) {
|
||||
((Cache*)cache_to_clean)->Release((Cache::Handle*)cache_handle);
|
||||
};
|
||||
auto found_row_cache_entry = static_cast<const std::string*>(
|
||||
ioptions_.row_cache->Value(row_handle));
|
||||
replayGetContextLog(*found_row_cache_entry, user_key, get_context);
|
||||
ioptions_.row_cache->Release(row_handle);
|
||||
// If it comes here value is located on the cache.
|
||||
// found_row_cache_entry points to the value on cache,
|
||||
// and value_pinner has cleanup procedure for the cached entry.
|
||||
// After replayGetContextLog() returns, get_context.pinnable_slice_
|
||||
// will point to cache entry buffer (or a copy based on that) and
|
||||
// cleanup routine under value_pinner will be delegated to
|
||||
// get_context.pinnable_slice_. Cache entry is released when
|
||||
// get_context.pinnable_slice_ is reset.
|
||||
value_pinner.RegisterCleanup(release_cache_entry_func,
|
||||
ioptions_.row_cache.get(), row_handle);
|
||||
replayGetContextLog(*found_row_cache_entry, user_key, get_context,
|
||||
&value_pinner);
|
||||
RecordTick(ioptions_.statistics, ROW_CACHE_HIT);
|
||||
done = true;
|
||||
} else {
|
||||
|
||||
+2
-1
@@ -134,7 +134,8 @@ class TableCache {
|
||||
HistogramImpl* file_read_hist,
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true);
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
bool for_compaction = false);
|
||||
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
const EnvOptions& env_options_;
|
||||
|
||||
+31
-9
@@ -61,18 +61,30 @@ struct FileDescriptor {
|
||||
uint64_t GetFileSize() const { return file_size; }
|
||||
};
|
||||
|
||||
struct FileSampledStats {
|
||||
FileSampledStats() : num_reads_sampled(0) {}
|
||||
FileSampledStats(const FileSampledStats& other) { *this = other; }
|
||||
FileSampledStats& operator=(const FileSampledStats& other) {
|
||||
num_reads_sampled = other.num_reads_sampled.load();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// number of user reads to this file.
|
||||
mutable std::atomic<uint64_t> num_reads_sampled;
|
||||
};
|
||||
|
||||
struct FileMetaData {
|
||||
int refs;
|
||||
FileDescriptor fd;
|
||||
InternalKey smallest; // Smallest internal key served by table
|
||||
InternalKey largest; // Largest internal key served by table
|
||||
bool being_compacted; // Is this file undergoing compaction?
|
||||
SequenceNumber smallest_seqno; // The smallest seqno in this file
|
||||
SequenceNumber largest_seqno; // The largest seqno in this file
|
||||
|
||||
// Needs to be disposed when refs becomes 0.
|
||||
Cache::Handle* table_reader_handle;
|
||||
|
||||
FileSampledStats stats;
|
||||
|
||||
// Stats for compensating deletion entries during compaction
|
||||
|
||||
// File size compensated by deletion entry.
|
||||
@@ -85,6 +97,10 @@ struct FileMetaData {
|
||||
uint64_t num_deletions; // the number of deletion entries.
|
||||
uint64_t raw_key_size; // total uncompressed key size.
|
||||
uint64_t raw_value_size; // total uncompressed value size.
|
||||
|
||||
int refs; // Reference count
|
||||
|
||||
bool being_compacted; // Is this file undergoing compaction?
|
||||
bool init_stats_from_file; // true if the data-entry stats of this file
|
||||
// has initialized from file.
|
||||
|
||||
@@ -92,9 +108,7 @@ struct FileMetaData {
|
||||
// file.
|
||||
|
||||
FileMetaData()
|
||||
: refs(0),
|
||||
being_compacted(false),
|
||||
smallest_seqno(kMaxSequenceNumber),
|
||||
: smallest_seqno(kMaxSequenceNumber),
|
||||
largest_seqno(0),
|
||||
table_reader_handle(nullptr),
|
||||
compensated_file_size(0),
|
||||
@@ -102,6 +116,8 @@ struct FileMetaData {
|
||||
num_deletions(0),
|
||||
raw_key_size(0),
|
||||
raw_value_size(0),
|
||||
refs(0),
|
||||
being_compacted(false),
|
||||
init_stats_from_file(false),
|
||||
marked_for_compaction(false) {}
|
||||
|
||||
@@ -117,10 +133,12 @@ struct FileMetaData {
|
||||
}
|
||||
};
|
||||
|
||||
// A compressed copy of file meta data that just contain
|
||||
// smallest and largest key's slice
|
||||
// A compressed copy of file meta data that just contain minimum data needed
|
||||
// to server read operations, while still keeping the pointer to full metadata
|
||||
// of the file in case it is needed.
|
||||
struct FdWithKeyRange {
|
||||
FileDescriptor fd;
|
||||
FileMetaData* file_metadata; // Point to all metadata
|
||||
Slice smallest_key; // slice that contain smallest key
|
||||
Slice largest_key; // slice that contain largest key
|
||||
|
||||
@@ -130,8 +148,12 @@ struct FdWithKeyRange {
|
||||
largest_key() {
|
||||
}
|
||||
|
||||
FdWithKeyRange(FileDescriptor _fd, Slice _smallest_key, Slice _largest_key)
|
||||
: fd(_fd), smallest_key(_smallest_key), largest_key(_largest_key) {}
|
||||
FdWithKeyRange(FileDescriptor _fd, Slice _smallest_key, Slice _largest_key,
|
||||
FileMetaData* _file_metadata)
|
||||
: fd(_fd),
|
||||
file_metadata(_file_metadata),
|
||||
smallest_key(_smallest_key),
|
||||
largest_key(_largest_key) {}
|
||||
};
|
||||
|
||||
// Data structure to store an array of FdWithKeyRange in one level
|
||||
|
||||
+98
-26
@@ -32,6 +32,7 @@
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_builder.h"
|
||||
#include "monitoring/file_read_sample.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
@@ -120,7 +121,7 @@ class FilePicker {
|
||||
}
|
||||
}
|
||||
|
||||
int GetCurrentLevel() { return returned_file_level_; }
|
||||
int GetCurrentLevel() const { return curr_level_; }
|
||||
|
||||
FdWithKeyRange* GetNextFile() {
|
||||
while (!search_ended_) { // Loops over different levels.
|
||||
@@ -327,10 +328,6 @@ Version::~Version() {
|
||||
assert(f->refs > 0);
|
||||
f->refs--;
|
||||
if (f->refs <= 0) {
|
||||
if (f->table_reader_handle) {
|
||||
cfd_->table_cache()->EraseHandle(f->fd, f->table_reader_handle);
|
||||
f->table_reader_handle = nullptr;
|
||||
}
|
||||
vset_->obsolete_files_.push_back(f);
|
||||
}
|
||||
}
|
||||
@@ -368,6 +365,7 @@ void DoGenerateLevelFilesBrief(LevelFilesBrief* file_level,
|
||||
|
||||
FdWithKeyRange& f = file_level->files[i];
|
||||
f.fd = files[i]->fd;
|
||||
f.file_metadata = files[i];
|
||||
f.smallest_key = Slice(mem, smallest_size);
|
||||
f.largest_key = Slice(mem + smallest_size, largest_size);
|
||||
}
|
||||
@@ -435,12 +433,12 @@ namespace {
|
||||
class LevelFileNumIterator : public InternalIterator {
|
||||
public:
|
||||
LevelFileNumIterator(const InternalKeyComparator& icmp,
|
||||
const LevelFilesBrief* flevel)
|
||||
const LevelFilesBrief* flevel, bool should_sample)
|
||||
: icmp_(icmp),
|
||||
flevel_(flevel),
|
||||
index_(static_cast<uint32_t>(flevel->num_files)),
|
||||
current_value_(0, 0, 0) { // Marks as invalid
|
||||
}
|
||||
current_value_(0, 0, 0), // Marks as invalid
|
||||
should_sample_(should_sample) {}
|
||||
virtual bool Valid() const override { return index_ < flevel_->num_files; }
|
||||
virtual void Seek(const Slice& target) override {
|
||||
index_ = FindFile(icmp_, *flevel_, target);
|
||||
@@ -475,6 +473,9 @@ class LevelFileNumIterator : public InternalIterator {
|
||||
assert(Valid());
|
||||
|
||||
auto file_meta = flevel_->files[index_];
|
||||
if (should_sample_) {
|
||||
sample_file_read_inc(file_meta.file_metadata);
|
||||
}
|
||||
current_value_ = file_meta.fd;
|
||||
return Slice(reinterpret_cast<const char*>(¤t_value_),
|
||||
sizeof(FileDescriptor));
|
||||
@@ -486,6 +487,7 @@ class LevelFileNumIterator : public InternalIterator {
|
||||
const LevelFilesBrief* flevel_;
|
||||
uint32_t index_;
|
||||
mutable FileDescriptor current_value_;
|
||||
bool should_sample_;
|
||||
};
|
||||
|
||||
class LevelFileIteratorState : public TwoLevelIteratorState {
|
||||
@@ -590,15 +592,15 @@ Status Version::GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
// 2. Table is not present in table cache, we'll read the table properties
|
||||
// directly from the properties block in the file.
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
std::string file_name;
|
||||
if (fname != nullptr) {
|
||||
s = ioptions->env->NewRandomAccessFile(
|
||||
*fname, &file, vset_->env_options_);
|
||||
file_name = *fname;
|
||||
} else {
|
||||
s = ioptions->env->NewRandomAccessFile(
|
||||
TableFileName(vset_->db_options_->db_paths, file_meta->fd.GetNumber(),
|
||||
file_meta->fd.GetPathId()),
|
||||
&file, vset_->env_options_);
|
||||
file_name =
|
||||
TableFileName(vset_->db_options_->db_paths, file_meta->fd.GetNumber(),
|
||||
file_meta->fd.GetPathId());
|
||||
}
|
||||
s = ioptions->env->NewRandomAccessFile(file_name, &file, vset_->env_options_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -607,7 +609,7 @@ Status Version::GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
// By setting the magic number to kInvalidTableMagicNumber, we can by
|
||||
// pass the magic number check in the footer.
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file)));
|
||||
new RandomAccessFileReader(std::move(file), file_name));
|
||||
s = ReadTableProperties(
|
||||
file_reader.get(), file_meta->fd.GetFileSize(),
|
||||
Footer::kInvalidTableMagicNumber /* table's magic number */, *ioptions, &raw_table_properties);
|
||||
@@ -743,13 +745,11 @@ void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
|
||||
file_path = ioptions->db_paths.back().path;
|
||||
}
|
||||
files.emplace_back(
|
||||
MakeTableFileName("", file->fd.GetNumber()),
|
||||
file_path,
|
||||
file->fd.GetFileSize(),
|
||||
file->smallest_seqno,
|
||||
file->largest_seqno,
|
||||
MakeTableFileName("", file->fd.GetNumber()), file_path,
|
||||
file->fd.GetFileSize(), file->smallest_seqno, file->largest_seqno,
|
||||
file->smallest.user_key().ToString(),
|
||||
file->largest.user_key().ToString(),
|
||||
file->stats.num_reads_sampled.load(std::memory_order_relaxed),
|
||||
file->being_compacted);
|
||||
level_size += file->fd.GetFileSize();
|
||||
}
|
||||
@@ -833,6 +833,8 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
return;
|
||||
}
|
||||
|
||||
bool should_sample = should_sample_file_read();
|
||||
|
||||
auto* arena = merge_iter_builder->GetArena();
|
||||
if (level == 0) {
|
||||
// Merge all level zero files together since they may overlap
|
||||
@@ -843,6 +845,15 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
range_del_agg, nullptr, cfd_->internal_stats()->GetFileReadHist(0),
|
||||
false, arena, false /* skip_filters */, 0 /* level */));
|
||||
}
|
||||
if (should_sample) {
|
||||
// Count ones for every L0 files. This is done per iterator creation
|
||||
// rather than Seek(), while files in other levels are recored per seek.
|
||||
// If users execute one range query per iterator, there may be some
|
||||
// discrepancy here.
|
||||
for (FileMetaData* meta : storage_info_.LevelFiles(0)) {
|
||||
sample_file_read_inc(meta);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For levels > 0, we can use a concatenating iterator that sequentially
|
||||
// walks through the non-overlapping files in the level, opening them
|
||||
@@ -857,7 +868,8 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
IsFilterSkipped(level), level, range_del_agg);
|
||||
mem = arena->AllocateAligned(sizeof(LevelFileNumIterator));
|
||||
auto* first_level_iter = new (mem) LevelFileNumIterator(
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level));
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
should_sample_file_read());
|
||||
merge_iter_builder->AddIterator(
|
||||
NewTwoLevelIterator(state, first_level_iter, arena, false));
|
||||
}
|
||||
@@ -982,6 +994,9 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
user_comparator(), internal_comparator());
|
||||
FdWithKeyRange* f = fp.GetNextFile();
|
||||
while (f != nullptr) {
|
||||
if (get_context.sample()) {
|
||||
sample_file_read_inc(f->file_metadata);
|
||||
}
|
||||
*status = table_cache_->Get(
|
||||
read_options, *internal_comparator(), f->fd, ikey, &get_context,
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
@@ -1212,6 +1227,14 @@ int VersionStorageInfo::MaxInputLevel() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VersionStorageInfo::MaxOutputLevel(bool allow_ingest_behind) const {
|
||||
if (allow_ingest_behind) {
|
||||
assert(num_levels() > 1);
|
||||
return num_levels() - 2;
|
||||
}
|
||||
return num_levels() - 1;
|
||||
}
|
||||
|
||||
void VersionStorageInfo::EstimateCompactionBytesNeeded(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
// Only implemented for level-based compaction
|
||||
@@ -1297,6 +1320,32 @@ void VersionStorageInfo::EstimateCompactionBytesNeeded(
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
uint32_t GetExpiredTtlFilesCount(const ImmutableCFOptions& ioptions,
|
||||
const std::vector<FileMetaData*>& files) {
|
||||
uint32_t ttl_expired_files_count = 0;
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions.env->GetCurrentTime(&_current_time);
|
||||
if (status.ok()) {
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
for (auto f : files) {
|
||||
if (!f->being_compacted && f->fd.table_reader != nullptr &&
|
||||
f->fd.table_reader->GetTableProperties() != nullptr) {
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time > 0 &&
|
||||
creation_time <
|
||||
(current_time - ioptions.compaction_options_fifo.ttl)) {
|
||||
ttl_expired_files_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ttl_expired_files_count;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
void VersionStorageInfo::ComputeCompactionScore(
|
||||
const ImmutableCFOptions& immutable_cf_options,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
@@ -1343,6 +1392,11 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger,
|
||||
score);
|
||||
}
|
||||
if (immutable_cf_options.compaction_options_fifo.ttl > 0) {
|
||||
score = std::max(static_cast<double>(GetExpiredTtlFilesCount(
|
||||
immutable_cf_options, files_[level])),
|
||||
score);
|
||||
}
|
||||
|
||||
} else {
|
||||
score = static_cast<double>(num_sorted_runs) /
|
||||
@@ -2199,13 +2253,16 @@ void Version::AddLiveFiles(std::vector<FileDescriptor>* live) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string Version::DebugString(bool hex) const {
|
||||
std::string Version::DebugString(bool hex, bool print_stats) const {
|
||||
std::string r;
|
||||
for (int level = 0; level < storage_info_.num_levels_; level++) {
|
||||
// E.g.,
|
||||
// --- level 1 ---
|
||||
// 17:123['a' .. 'd']
|
||||
// 20:43['e' .. 'g']
|
||||
//
|
||||
// if print_stats=true:
|
||||
// 17:123['a' .. 'd'](4096)
|
||||
r.append("--- level ");
|
||||
AppendNumberTo(&r, level);
|
||||
r.append(" --- version# ");
|
||||
@@ -2221,7 +2278,14 @@ std::string Version::DebugString(bool hex) const {
|
||||
r.append(files[i]->smallest.DebugString(hex));
|
||||
r.append(" .. ");
|
||||
r.append(files[i]->largest.DebugString(hex));
|
||||
r.append("]\n");
|
||||
r.append("]");
|
||||
if (print_stats) {
|
||||
r.append("(");
|
||||
r.append(ToString(
|
||||
files[i]->stats.num_reads_sampled.load(std::memory_order_relaxed)));
|
||||
r.append(")");
|
||||
}
|
||||
r.append("\n");
|
||||
}
|
||||
}
|
||||
return r;
|
||||
@@ -2255,6 +2319,7 @@ VersionSet::VersionSet(const std::string& dbname,
|
||||
manifest_file_number_(0), // Filled by Recover()
|
||||
pending_manifest_file_number_(0),
|
||||
last_sequence_(0),
|
||||
last_to_be_written_sequence_(0),
|
||||
prev_log_number_(0),
|
||||
current_version_number_(0),
|
||||
manifest_file_size_(0),
|
||||
@@ -2270,10 +2335,14 @@ void CloseTables(void* ptr, size_t) {
|
||||
VersionSet::~VersionSet() {
|
||||
// we need to delete column_family_set_ because its destructor depends on
|
||||
// VersionSet
|
||||
column_family_set_->get_table_cache()->ApplyToAllCacheEntries(&CloseTables,
|
||||
false);
|
||||
Cache* table_cache = column_family_set_->get_table_cache();
|
||||
table_cache->ApplyToAllCacheEntries(&CloseTables, false /* thread_safe */);
|
||||
column_family_set_.reset();
|
||||
for (auto file : obsolete_files_) {
|
||||
if (file->table_reader_handle) {
|
||||
table_cache->Release(file->table_reader_handle);
|
||||
TableCache::Evict(table_cache, file->fd.GetNumber());
|
||||
}
|
||||
delete file;
|
||||
}
|
||||
obsolete_files_.clear();
|
||||
@@ -2891,6 +2960,7 @@ Status VersionSet::Recover(
|
||||
|
||||
manifest_file_size_ = current_manifest_file_size;
|
||||
next_file_number_.store(next_file + 1);
|
||||
last_to_be_written_sequence_ = last_sequence;
|
||||
last_sequence_ = last_sequence;
|
||||
prev_log_number_ = previous_log_number;
|
||||
|
||||
@@ -3260,6 +3330,7 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
|
||||
}
|
||||
|
||||
next_file_number_.store(next_file + 1);
|
||||
last_to_be_written_sequence_ = last_sequence;
|
||||
last_sequence_ = last_sequence;
|
||||
prev_log_number_ = previous_log_number;
|
||||
|
||||
@@ -3544,7 +3615,8 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
false /* skip_filters */, (int)which /* level */,
|
||||
range_del_agg),
|
||||
new LevelFileNumIterator(cfd->internal_comparator(),
|
||||
c->input_levels(which)));
|
||||
c->input_levels(which),
|
||||
false /* don't sample compaction */));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-1
@@ -147,6 +147,7 @@ class VersionStorageInfo {
|
||||
}
|
||||
|
||||
int MaxInputLevel() const;
|
||||
int MaxOutputLevel(bool allow_ingest_behind) const;
|
||||
|
||||
// Return level number that has idx'th highest score
|
||||
int CompactionScoreLevel(int idx) const { return compaction_level_[idx]; }
|
||||
@@ -502,7 +503,7 @@ class Version {
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live);
|
||||
|
||||
// Return a human readable string that describes this version's contents.
|
||||
std::string DebugString(bool hex = false) const;
|
||||
std::string DebugString(bool hex = false, bool print_stats = false) const;
|
||||
|
||||
// Returns the version nuber of this version
|
||||
uint64_t GetVersionNumber() const { return version_number_; }
|
||||
@@ -697,12 +698,31 @@ class VersionSet {
|
||||
return last_sequence_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
// Note: memory_order_acquire must be sufficient.
|
||||
uint64_t LastToBeWrittenSequence() const {
|
||||
return last_to_be_written_sequence_.load(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// Set the last sequence number to s.
|
||||
void SetLastSequence(uint64_t s) {
|
||||
assert(s >= last_sequence_);
|
||||
// Last visible seqeunce must always be less than last written seq
|
||||
assert(!db_options_->concurrent_prepare ||
|
||||
s <= last_to_be_written_sequence_);
|
||||
last_sequence_.store(s, std::memory_order_release);
|
||||
}
|
||||
|
||||
// Note: memory_order_release must be sufficient
|
||||
void SetLastToBeWrittenSequence(uint64_t s) {
|
||||
assert(s >= last_to_be_written_sequence_);
|
||||
last_to_be_written_sequence_.store(s, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// Note: memory_order_release must be sufficient
|
||||
uint64_t FetchAddLastToBeWrittenSequence(uint64_t s) {
|
||||
return last_to_be_written_sequence_.fetch_add(s, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// Mark the specified file number as used.
|
||||
// REQUIRED: this is only called during single-threaded recovery
|
||||
void MarkFileNumberUsedDuringRecovery(uint64_t number);
|
||||
@@ -802,7 +822,10 @@ class VersionSet {
|
||||
uint64_t manifest_file_number_;
|
||||
uint64_t options_file_number_;
|
||||
uint64_t pending_manifest_file_number_;
|
||||
// The last seq visible to reads
|
||||
std::atomic<uint64_t> last_sequence_;
|
||||
// The last seq with which a writer has written/will write.
|
||||
std::atomic<uint64_t> last_to_be_written_sequence_;
|
||||
uint64_t prev_log_number_; // 0 or backing store for memtable being compacted
|
||||
|
||||
// Opened lazily
|
||||
|
||||
+3
-2
@@ -26,6 +26,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
@@ -273,8 +274,8 @@ namespace {
|
||||
struct CompareLogByPointer {
|
||||
bool operator()(const std::unique_ptr<LogFile>& a,
|
||||
const std::unique_ptr<LogFile>& b) {
|
||||
LogFileImpl* a_impl = dynamic_cast<LogFileImpl*>(a.get());
|
||||
LogFileImpl* b_impl = dynamic_cast<LogFileImpl*>(b.get());
|
||||
LogFileImpl* a_impl = static_cast_with_check<LogFileImpl, LogFile>(a.get());
|
||||
LogFileImpl* b_impl = static_cast_with_check<LogFileImpl, LogFile>(b.get());
|
||||
return *a_impl < *b_impl;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,6 +67,7 @@ class WalManagerTest : public testing::Test {
|
||||
batch.Put(key, value);
|
||||
WriteBatchInternal::SetSequence(&batch, seq);
|
||||
current_log_writer_->AddRecord(WriteBatchInternal::Contents(&batch));
|
||||
versions_->SetLastToBeWrittenSequence(seq);
|
||||
versions_->SetLastSequence(seq);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ static std::string PrintContents(WriteBatch* b) {
|
||||
}
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ParsedInternalKey ikey;
|
||||
memset((void*)&ikey, 0, sizeof(ikey));
|
||||
ikey.clear();
|
||||
EXPECT_TRUE(ParseInternalKey(iter->key(), &ikey));
|
||||
switch (ikey.type) {
|
||||
case kTypeValue:
|
||||
@@ -451,20 +451,20 @@ TEST_F(WriteBatchTest, DISABLED_ManyUpdates) {
|
||||
}
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
EXPECT_TRUE(false);
|
||||
ADD_FAILURE();
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status SingleDeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
EXPECT_TRUE(false);
|
||||
ADD_FAILURE();
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
EXPECT_TRUE(false);
|
||||
ADD_FAILURE();
|
||||
return Status::OK();
|
||||
}
|
||||
virtual void LogData(const Slice& blob) override { EXPECT_TRUE(false); }
|
||||
virtual void LogData(const Slice& blob) override { ADD_FAILURE(); }
|
||||
virtual bool Continue() override { return num_seen < kNumUpdates; }
|
||||
} handler;
|
||||
|
||||
@@ -502,20 +502,20 @@ TEST_F(WriteBatchTest, DISABLED_LargeKeyValue) {
|
||||
}
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
EXPECT_TRUE(false);
|
||||
ADD_FAILURE();
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status SingleDeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
EXPECT_TRUE(false);
|
||||
ADD_FAILURE();
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
EXPECT_TRUE(false);
|
||||
ADD_FAILURE();
|
||||
return Status::OK();
|
||||
}
|
||||
virtual void LogData(const Slice& blob) override { EXPECT_TRUE(false); }
|
||||
virtual void LogData(const Slice& blob) override { ADD_FAILURE(); }
|
||||
virtual bool Continue() override { return num_seen < 2; }
|
||||
} handler;
|
||||
|
||||
|
||||
+161
-139
@@ -16,7 +16,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "port/port.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
@@ -107,6 +106,10 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
std::vector<std::pair<string, string>> kvs_;
|
||||
};
|
||||
|
||||
// In each scenario we'll launch multiple threads to write.
|
||||
// The size of each array equals to number of threads, and
|
||||
// each boolean in it denote whether callback of corresponding
|
||||
// thread should succeed or fail.
|
||||
std::vector<std::vector<WriteOP>> write_scenarios = {
|
||||
{true},
|
||||
{false},
|
||||
@@ -123,176 +126,195 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
{false, false, true, false, true},
|
||||
};
|
||||
|
||||
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& two_queues : {true, false}) {
|
||||
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;
|
||||
options.concurrent_prepare = two_queues;
|
||||
|
||||
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);
|
||||
// Writers that have called JoinBatchGroup.
|
||||
std::atomic<uint64_t> threads_joining(0);
|
||||
// Writers that have linked to the queue
|
||||
std::atomic<uint64_t> threads_linked(0);
|
||||
// Writers that pass WriteThread::JoinBatchGroup:Wait sync-point.
|
||||
std::atomic<uint64_t> threads_verified(0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg) {
|
||||
uint64_t cur_threads_waiting = 0;
|
||||
bool is_leader = false;
|
||||
bool is_last = false;
|
||||
std::atomic<uint64_t> seq(db_impl->GetLatestSequenceNumber());
|
||||
ASSERT_EQ(db_impl->GetLatestSequenceNumber(), 0);
|
||||
|
||||
// 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));
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Start", [&](void*) {
|
||||
uint64_t cur_threads_joining = threads_joining.fetch_add(1);
|
||||
// Wait for the last joined writer to link to the queue.
|
||||
// In this way the writers link to the queue one by one.
|
||||
// This allows us to confidently detect the first writer
|
||||
// who increases threads_linked as the leader.
|
||||
while (threads_linked.load() < cur_threads_joining) {
|
||||
}
|
||||
});
|
||||
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
// Verification once writers call JoinBatchGroup.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg) {
|
||||
uint64_t cur_threads_linked = threads_linked.fetch_add(1);
|
||||
bool is_leader = false;
|
||||
bool is_last = false;
|
||||
|
||||
if (is_leader) {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_GROUP_LEADER);
|
||||
} else {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_INIT);
|
||||
}
|
||||
// who am i
|
||||
is_leader = (cur_threads_linked == 0);
|
||||
is_last = (cur_threads_linked == write_group.size() - 1);
|
||||
|
||||
// (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_);
|
||||
}
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
|
||||
// wait for friends
|
||||
while (threads_waiting.load() < write_group.size()) {
|
||||
}
|
||||
});
|
||||
if (is_leader) {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_GROUP_LEADER);
|
||||
} else {
|
||||
ASSERT_TRUE(writer->state ==
|
||||
WriteThread::State::STATE_INIT);
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:DoneWaiting", [&](void* arg) {
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
// (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_);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
threads_verified.fetch_add(1);
|
||||
// Wait here until all verification in this sync-point
|
||||
// callback finish for all writers.
|
||||
while (threads_verified.load() < write_group.size()) {
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:DoneWaiting", [&](void* arg) {
|
||||
// check my state
|
||||
auto* writer = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
|
||||
// leaders gotta lead
|
||||
while (i > 0 && threads_waiting.load() < 1) {
|
||||
}
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
||||
// loser has to lose
|
||||
while (i == write_group.size() - 1 &&
|
||||
threads_waiting.load() < write_group.size() - 1) {
|
||||
}
|
||||
std::atomic<uint32_t> thread_num(0);
|
||||
std::atomic<char> dummy_key(0);
|
||||
|
||||
auto& write_op = write_group.at(i);
|
||||
write_op.Clear();
|
||||
write_op.callback_.allow_batching_ = allow_batching;
|
||||
// Each write thread create a random write batch and write to DB
|
||||
// with a write callback.
|
||||
std::function<void()> write_with_callback_func = [&]() {
|
||||
uint32_t i = thread_num.fetch_add(1);
|
||||
Random rnd(i);
|
||||
|
||||
// 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);
|
||||
// leaders gotta lead
|
||||
while (i > 0 && threads_verified.load() < 1) {
|
||||
}
|
||||
}
|
||||
|
||||
WriteOptions woptions;
|
||||
woptions.disableWAL = !enable_WAL;
|
||||
woptions.sync = enable_WAL;
|
||||
Status s = db_impl->WriteWithCallback(
|
||||
woptions, &write_op.write_batch_, &write_op.callback_);
|
||||
// loser has to lose
|
||||
while (i == write_group.size() - 1 &&
|
||||
threads_verified.load() < write_group.size() - 1) {
|
||||
}
|
||||
|
||||
if (write_op.callback_.should_fail_) {
|
||||
ASSERT_TRUE(s.IsBusy());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
};
|
||||
auto& write_op = write_group.at(i);
|
||||
write_op.Clear();
|
||||
write_op.callback_.allow_batching_ = allow_batching;
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
// insert some keys
|
||||
for (uint32_t j = 0; j < rnd.Next() % 50; j++) {
|
||||
// grab unique key
|
||||
char my_key = dummy_key.fetch_add(1);
|
||||
|
||||
// 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();
|
||||
}
|
||||
string skey(5, my_key);
|
||||
string sval(10, my_key);
|
||||
write_op.Put(skey, sval);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
if (!write_op.callback_.should_fail_) {
|
||||
seq.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
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(db->Get(read_options, kvp.first, &value));
|
||||
ASSERT_EQ(value, kvp.second);
|
||||
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());
|
||||
} 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);
|
||||
}
|
||||
|
||||
ASSERT_EQ(seq.load(), db_impl->GetLatestSequenceNumber());
|
||||
|
||||
delete db;
|
||||
DestroyDB(dbname, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WriteCallbackTest, WriteCallBackTest) {
|
||||
|
||||
+38
-17
@@ -57,6 +57,10 @@ uint8_t WriteThread::AwaitState(Writer* w, uint8_t goal_mask,
|
||||
AdaptationContext* ctx) {
|
||||
uint8_t state;
|
||||
|
||||
// 1. Busy loop using "pause" for 1 micro sec
|
||||
// 2. Else SOMETIMES busy loop using "yield" for 100 micro sec (default)
|
||||
// 3. Else blocking wait
|
||||
|
||||
// On a modern Xeon each loop takes about 7 nanoseconds (most of which
|
||||
// is the effect of the pause instruction), so 200 iterations is a bit
|
||||
// more than a microsecond. This is long enough that waits longer than
|
||||
@@ -114,13 +118,21 @@ uint8_t WriteThread::AwaitState(Writer* w, uint8_t goal_mask,
|
||||
|
||||
const size_t kMaxSlowYieldsWhileSpinning = 3;
|
||||
|
||||
// Whether the yield approach has any credit in this context. The credit is
|
||||
// added by yield being succesfull before timing out, and decreased otherwise.
|
||||
auto& yield_credit = ctx->value;
|
||||
// Update the yield_credit based on sample runs or right after a hard failure
|
||||
bool update_ctx = false;
|
||||
// Should we reinforce the yield credit
|
||||
bool would_spin_again = false;
|
||||
// The samling base for updating the yeild credit. The sampling rate would be
|
||||
// 1/sampling_base.
|
||||
const int sampling_base = 256;
|
||||
|
||||
if (max_yield_usec_ > 0) {
|
||||
update_ctx = Random::GetTLSInstance()->OneIn(256);
|
||||
update_ctx = Random::GetTLSInstance()->OneIn(sampling_base);
|
||||
|
||||
if (update_ctx || ctx->value.load(std::memory_order_relaxed) >= 0) {
|
||||
if (update_ctx || yield_credit.load(std::memory_order_relaxed) >= 0) {
|
||||
// we're updating the adaptation statistics, or spinning has >
|
||||
// 50% chance of being shorter than max_yield_usec_ and causing no
|
||||
// involuntary context switches
|
||||
@@ -149,7 +161,7 @@ uint8_t WriteThread::AwaitState(Writer* w, uint8_t goal_mask,
|
||||
// accurate enough to measure the yield duration
|
||||
++slow_yield_count;
|
||||
if (slow_yield_count >= kMaxSlowYieldsWhileSpinning) {
|
||||
// Not just one ivcsw, but several. Immediately update ctx
|
||||
// Not just one ivcsw, but several. Immediately update yield_credit
|
||||
// and fall back to blocking
|
||||
update_ctx = true;
|
||||
break;
|
||||
@@ -165,11 +177,19 @@ uint8_t WriteThread::AwaitState(Writer* w, uint8_t goal_mask,
|
||||
}
|
||||
|
||||
if (update_ctx) {
|
||||
auto v = ctx->value.load(std::memory_order_relaxed);
|
||||
// Since our update is sample based, it is ok if a thread overwrites the
|
||||
// updates by other threads. Thus the update does not have to be atomic.
|
||||
auto v = yield_credit.load(std::memory_order_relaxed);
|
||||
// fixed point exponential decay with decay constant 1/1024, with +1
|
||||
// and -1 scaled to avoid overflow for int32_t
|
||||
v = v + (v / 1024) + (would_spin_again ? 1 : -1) * 16384;
|
||||
ctx->value.store(v, std::memory_order_relaxed);
|
||||
//
|
||||
// On each update the positive credit is decayed by a facor of 1/1024 (i.e.,
|
||||
// 0.1%). If the sampled yield was successful, the credit is also increased
|
||||
// by X. Setting X=2^17 ensures that the credit never exceeds
|
||||
// 2^17*2^10=2^27, which is lower than 2^31 the upperbound of int32_t. Same
|
||||
// logic applies to negative credits.
|
||||
v = v - (v / 1024) + (would_spin_again ? 1 : -1) * 131072;
|
||||
yield_credit.store(v, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
assert((state & goal_mask) != 0);
|
||||
@@ -267,10 +287,11 @@ void WriteThread::CompleteFollower(Writer* w, WriteGroup& write_group) {
|
||||
SetState(w, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext jbg_ctx("JoinBatchGroup");
|
||||
void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
static AdaptationContext ctx("JoinBatchGroup");
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:Start", w);
|
||||
assert(w->batch != nullptr);
|
||||
|
||||
bool linked_as_leader = LinkOne(w, &newest_writer_);
|
||||
if (linked_as_leader) {
|
||||
SetState(w, STATE_GROUP_LEADER);
|
||||
@@ -294,7 +315,7 @@ void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
*/
|
||||
AwaitState(w, STATE_GROUP_LEADER | STATE_MEMTABLE_WRITER_LEADER |
|
||||
STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED,
|
||||
&ctx);
|
||||
&jbg_ctx);
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:DoneWaiting", w);
|
||||
}
|
||||
}
|
||||
@@ -473,9 +494,9 @@ void WriteThread::LaunchParallelMemTableWriters(WriteGroup* write_group) {
|
||||
}
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext cpmtw_ctx("CompleteParallelMemTableWriter");
|
||||
// This method is called by both the leader and parallel followers
|
||||
bool WriteThread::CompleteParallelMemTableWriter(Writer* w) {
|
||||
static AdaptationContext ctx("CompleteParallelMemTableWriter");
|
||||
|
||||
auto* write_group = w->write_group;
|
||||
if (!w->status.ok()) {
|
||||
@@ -485,7 +506,7 @@ bool WriteThread::CompleteParallelMemTableWriter(Writer* w) {
|
||||
|
||||
if (write_group->running-- > 1) {
|
||||
// we're not the last one
|
||||
AwaitState(w, STATE_COMPLETED, &ctx);
|
||||
AwaitState(w, STATE_COMPLETED, &cpmtw_ctx);
|
||||
return false;
|
||||
}
|
||||
// else we're the last parallel worker and should perform exit duties.
|
||||
@@ -504,9 +525,9 @@ void WriteThread::ExitAsBatchGroupFollower(Writer* w) {
|
||||
SetState(write_group->leader, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext eabgl_ctx("ExitAsBatchGroupLeader");
|
||||
void WriteThread::ExitAsBatchGroupLeader(WriteGroup& write_group,
|
||||
Status status) {
|
||||
static AdaptationContext ctx("ExitAsBatchGroupLeader");
|
||||
Writer* leader = write_group.leader;
|
||||
Writer* last_writer = write_group.last_writer;
|
||||
assert(leader->link_older == nullptr);
|
||||
@@ -544,7 +565,7 @@ void WriteThread::ExitAsBatchGroupLeader(WriteGroup& write_group,
|
||||
}
|
||||
AwaitState(leader, STATE_MEMTABLE_WRITER_LEADER |
|
||||
STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED,
|
||||
&ctx);
|
||||
&eabgl_ctx);
|
||||
} else {
|
||||
Writer* head = newest_writer_.load(std::memory_order_acquire);
|
||||
if (head != last_writer ||
|
||||
@@ -591,15 +612,15 @@ void WriteThread::ExitAsBatchGroupLeader(WriteGroup& write_group,
|
||||
}
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext eu_ctx("EnterUnbatched");
|
||||
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_);
|
||||
if (!linked_as_leader) {
|
||||
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);
|
||||
AwaitState(w, STATE_GROUP_LEADER, &eu_ctx);
|
||||
}
|
||||
if (enable_pipelined_write_) {
|
||||
WaitForMemTableWriters();
|
||||
@@ -619,15 +640,15 @@ void WriteThread::ExitUnbatched(Writer* w) {
|
||||
}
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext wfmw_ctx("WaitForMemTableWriters");
|
||||
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);
|
||||
AwaitState(&w, STATE_MEMTABLE_WRITER_LEADER, &wfmw_ctx);
|
||||
}
|
||||
newest_memtable_writer_.store(nullptr);
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ class WriteThread {
|
||||
|
||||
// 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
|
||||
// parallel 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,
|
||||
|
||||
@@ -56,3 +56,7 @@ ajkr:
|
||||
sagar0:
|
||||
full_name: Sagar Vemuri
|
||||
fbid: 2419111
|
||||
|
||||
lightmark:
|
||||
full_name: Aaron Gao
|
||||
fbid: 1351549072
|
||||
+1
-1
@@ -13,7 +13,7 @@ RocksDB is an embeddable persistent key-value store for fast storage. RocksDB ca
|
||||
|
||||
RocksDB builds on [LevelDB](https://code.google.com/p/leveldb/) to be scalable to run on servers with many CPU cores, to efficiently use fast storage, to support IO-bound, in-memory and write-once workloads, and to be flexible to allow for innovation.
|
||||
|
||||
For the latest details, watch [Mark Callaghan’s and Igor Canadi’s talk at CMU on 10/2015](https://scs.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=f4e0eb37-ae18-468f-9248-cb73edad3e56). [Dhruba Borthakur’s introductory talk](https://github.com/facebook/rocksdb/blob/gh-pages/intro.pdf?raw=true) from the Data @ Scale 2013 conference provides some perspective about how RocksDB has evolved.
|
||||
For the latest details, watch [Mark Callaghan’s and Igor Canadi’s talk at CMU on 10/2015](https://scs.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=f4e0eb37-ae18-468f-9248-cb73edad3e56). [Dhruba Borthakur’s introductory talk](https://github.com/facebook/rocksdb/blob/gh-pages-old/intro.pdf?raw=true) from the Data @ Scale 2013 conference provides some perspective about how RocksDB has evolved.
|
||||
|
||||
## How does performance compare?
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ The RocksDB library provides a persistent key value store. Keys and values are a
|
||||
|
||||
The library is maintained by the Facebook Database Engineering Team, and is based on [LevelDB](https://github.com/google/leveldb), by Sanjay Ghemawat and Jeff Dean at Google.
|
||||
|
||||
This overview gives some simple examples of how RocksDB is used. For the story of why RocksDB was created in the first place, see [Dhruba Borthakur’s introductory talk](https://github.com/facebook/rocksdb/blob/gh-pages/intro.pdf?raw=true) from the Data @ Scale 2013 conference.
|
||||
This overview gives some simple examples of how RocksDB is used. For the story of why RocksDB was created in the first place, see [Dhruba Borthakur’s introductory talk](https://github.com/facebook/rocksdb/blob/gh-pages-old/intro.pdf?raw=true) from the Data @ Scale 2013 conference.
|
||||
|
||||
## Opening A Database
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Level-based Compaction Changes
|
||||
layout: post
|
||||
author: ajkr
|
||||
category: blog
|
||||
---
|
||||
|
||||
### Introduction
|
||||
|
||||
RocksDB provides an option to limit the number of L0 files, which bounds read-amplification. Since L0 files (unlike files at lower levels) can span the entire key-range, a key might be in any file, thus reads need to check them one-by-one. Users often wish to configure a low limit to improve their read latency.
|
||||
|
||||
Although, the mechanism with which we enforce L0's file count limit may be unappealing. When the limit is reached, RocksDB intentionally delays user writes. This slows down accumulation of files in L0, and frees up resources for compacting files down to lower levels. But adding delays will significantly increase user-visible write latency jitter.
|
||||
|
||||
Also, due to how L0 files can span the entire key-range, compaction parallelization is limited. Files at L0 or L1 may be locked due to involvement in pending L0->L1 or L1->L2 compactions. We can only schedule a parallel L0->L1 compaction if it does not require any of the locked files, which is typically not the case.
|
||||
|
||||
To handle these constraints better, we added a new type of compaction, L0->L0. It quickly reduces file count in L0 and can be scheduled even when L1 files are locked, unlike L0->L1. We also changed the L0->L1 picking algorithm to increase opportunities for parallelism.
|
||||
|
||||
### Old L0->L1 Picking Logic
|
||||
|
||||
Previously, our logic for picking which L0 file to compact was the same as every other level: pick the largest file in the level. One special property of L0->L1 compaction is that files can overlap in the input level, so those overlapping files must be pulled in as well. For example, a compaction may look like this:
|
||||
|
||||

|
||||
|
||||
This compaction pulls in every L0 and L1 file. This happens regardless of which L0 file is initially chosen as each file overlaps with every other file.
|
||||
|
||||
Users may insert their data less uniformly in the key-range. For example, a database may look like this during L0->L1 compaction:
|
||||
|
||||

|
||||
|
||||
Let's say the third file from the top is the largest, and let's say the top two files are created after the compaction started. When the compaction is picked, the fourth L0 file and six rightmost L1 files are pulled in due to overlap. Notice this leaves the database in a state where we might not be able to schedule parallel compactions. For example, if the sixth file from the top is the next largest, we can't compact it because it overlaps with the top two files, which overlap with the locked L0 files.
|
||||
|
||||
We can now see the high-level problems with this approach more clearly. First, locked files in L0 or L1 prevent us from parallelizing compactions. When locked files block L0->L1 compaction, there is nothing we can do to eliminate L0 files. Second, L0->L1 compactions are relatively slow. As we saw, when keys are uniformly distributed, L0->L1 compacts two entire levels. While this is happening, new files are being flushed to L0, advancing towards the file count limit.
|
||||
|
||||
### New L0->L0 Algorithm
|
||||
|
||||
We introduced compaction within L0 to improve both parallelization and speed of reducing L0 file count. An L0->L0 compaction may look like this:
|
||||
|
||||

|
||||
|
||||
Say the L1->L2 compaction started first. Now L0->L1 is prevented by the locked L1 file. In this case, we compact files within L0. This allows us to start the work for eliminating L0 files earlier. It also lets us do less work since we don't pull in any L1 files, whereas L0->L1 compaction would've pulled in all of them. This lets us quickly reduce L0 file count to keep read-amp low while sustaining large bursts of writes (i.e., fast accumulation of L0 files).
|
||||
|
||||
The tradeoff is this increases total compaction work, as we're now compacting files without contributing towards our eventual goal of moving them towards lower levels. Our benchmarks, though, consistently show less compaction stalls and improved write throughput. One justification is that L0 file data is highly likely in page cache and/or block cache due to it being recently written and frequently accessed. So, this type of compaction is relatively cheap compared to compactions at lower levels.
|
||||
|
||||
This feature is available since RocksDB 5.4.
|
||||
|
||||
### New L0->L1 Picking Logic
|
||||
|
||||
Recall how the old L0->L1 picking algorithm chose the largest L0 file for compaction. This didn't fit well with L0->L0 compaction, which operates on a span of files. That span begins at the newest L0 file, and expands towards older files as long as they're not being compacted. Since the largest file may be anywhere, the old L0->L1 picking logic could arbitrarily prevent us from getting a long span of files. See the second illustration in this post for a scenario where this would happen.
|
||||
|
||||
So, we changed the L0->L1 picking algorithm to start from the oldest file and expand towards newer files as long as they're not being compacted. For example:
|
||||
|
||||

|
||||
|
||||
Now, there can never be L0 files unreachable for L0->L0 due to L0->L1 selecting files in the middle. When longer spans of files are available for L0->L0, we perform less compaction work per deleted L0 file, thus improving efficiency.
|
||||
|
||||
This feature will be available in RocksDB 5.7.
|
||||
|
||||
### Performance Changes
|
||||
|
||||
Mark Callaghan did the most extensive benchmarking of this feature's impact on MyRocks. See his results [here](http://smalldatum.blogspot.com/2017/05/innodb-myrocks-and-tokudb-on-insert.html). Note the primary change between his March 17 and April 14 builds is the latter performs L0->L0 compaction.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: RocksDB 5.5.1 Released!
|
||||
layout: post
|
||||
author: lightmark
|
||||
category: blog
|
||||
---
|
||||
|
||||
### New Features
|
||||
* FIFO compaction to support Intra L0 compaction too with CompactionOptionsFIFO.allow_compaction=true.
|
||||
* 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 the bug that flush doesn't respond to fsync result
|
||||
Loaded 100 of 325 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user