mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-08 03:52:33 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0113c61ce9 | ||
|
|
405017bb8c | ||
|
|
cfdea78fde | ||
|
|
fef38d9477 | ||
|
|
4d461733e4 | ||
|
|
1e7cca20fb | ||
|
|
ab21dc6323 | ||
|
|
fa37825fce | ||
|
|
4f6da54e8d | ||
|
|
98f11d01bd | ||
|
|
7b378c0de2 | ||
|
|
e90493a865 | ||
|
|
e36e75d7c8 | ||
|
|
895a4ad185 | ||
|
|
377d4d2c31 | ||
|
|
5271d0750a | ||
|
|
29713489b6 | ||
|
|
abcad80559 | ||
|
|
b6a8f16b5e | ||
|
|
eddc3ebd90 | ||
|
|
73fcd6b8ff | ||
|
|
7981e040a7 | ||
|
|
5279417e73 | ||
|
|
8c7b501d0f | ||
|
|
3c1781d6af | ||
|
|
9d5eb4063c | ||
|
|
0e35aa9930 | ||
|
|
023e9bf214 | ||
|
|
096e5f5d02 | ||
|
|
c9a0419413 | ||
|
|
9e2d5ab6bf | ||
|
|
6d37fdb365 | ||
|
|
7188bd34f3 | ||
|
|
d5612b43de | ||
|
|
dc064f302e | ||
|
|
d116a1725d | ||
|
|
90f744941d | ||
|
|
c7cf981a85 | ||
|
|
fa4de6e30f | ||
|
|
9d646a6311 | ||
|
|
889a0553c8 | ||
|
|
9c0c8f5ff6 | ||
|
|
aeed4f0749 | ||
|
|
19ec44fd39 | ||
|
|
8ae2bf5331 | ||
|
|
33ad9060d3 | ||
|
|
bf07e90cf2 | ||
|
|
d122025891 | ||
|
|
d916a1105a | ||
|
|
f3d91a0b57 | ||
|
|
999d955e4f | ||
|
|
1b1d264342 | ||
|
|
4ea56b1bd0 | ||
|
|
6d75319d95 | ||
|
|
76d77205da | ||
|
|
d511f35ea7 | ||
|
|
058026a885 | ||
|
|
b271f956c2 | ||
|
|
caf0f53a74 | ||
|
|
63f265c383 | ||
|
|
72e69497fc | ||
|
|
1b813a9b2e | ||
|
|
ab22cf349e | ||
|
|
de7f423a82 | ||
|
|
9e8ef45066 | ||
|
|
b15379dcea | ||
|
|
7a9a164276 | ||
|
|
eb8885a08a | ||
|
|
853a5cd3b2 | ||
|
|
bbf30330b4 | ||
|
|
d8d66c937e | ||
|
|
badfd70a3e | ||
|
|
c970358574 | ||
|
|
4cb7068c1e | ||
|
|
6175b4b294 | ||
|
|
140f256da2 | ||
|
|
ceb5fea1e3 | ||
|
|
a15354d04e | ||
|
|
1f802773bc | ||
|
|
22368965a0 | ||
|
|
fefdac1004 | ||
|
|
f9373e2d5c | ||
|
|
9dbf39399e | ||
|
|
892a156267 | ||
|
|
f8f6983f89 | ||
|
|
12b6cdeed3 | ||
|
|
ee7617167f | ||
|
|
a1a546a634 | ||
|
|
8abafb1feb | ||
|
|
54de56844d | ||
|
|
a11df583ec | ||
|
|
f5e46354d2 | ||
|
|
c33b32671e | ||
|
|
e0906eb785 | ||
|
|
134a52e144 | ||
|
|
bdc6abd0b4 | ||
|
|
fd45495cf5 | ||
|
|
2a81633da2 | ||
|
|
18f538038a | ||
|
|
8805ec2f49 | ||
|
|
ea212e5316 | ||
|
|
374c37da5b | ||
|
|
c6d2a7f821 | ||
|
|
4b0a43574a | ||
|
|
f95a5b2464 | ||
|
|
6811fb0658 | ||
|
|
a5e851e113 | ||
|
|
8425c8bd4d | ||
|
|
ab35505e21 | ||
|
|
2736752b33 | ||
|
|
b5613227a9 | ||
|
|
78ab11cd71 | ||
|
|
f3801528c1 | ||
|
|
1857576e03 | ||
|
|
80afa84903 | ||
|
|
4bb1e239b5 | ||
|
|
79f009f22e | ||
|
|
37e0fdc824 | ||
|
|
d538ebdff0 |
No files matched your search
@@ -45,6 +45,8 @@ etags
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
|
||||
java/out
|
||||
java/target
|
||||
|
||||
+8
-1
@@ -549,6 +549,8 @@ set(SOURCES
|
||||
table/cuckoo_table_builder.cc
|
||||
table/cuckoo_table_factory.cc
|
||||
table/cuckoo_table_reader.cc
|
||||
table/data_block_hash_index.cc
|
||||
table/data_block_footer.cc
|
||||
table/flush_block_policy.cc
|
||||
table/format.cc
|
||||
table/full_filter_block.cc
|
||||
@@ -572,6 +574,7 @@ set(SOURCES
|
||||
tools/ldb_cmd.cc
|
||||
tools/ldb_tool.cc
|
||||
tools/sst_dump_tool.cc
|
||||
tools/trace_analyzer_tool.cc
|
||||
util/arena.cc
|
||||
util/auto_roll_logger.cc
|
||||
util/bloom.cc
|
||||
@@ -596,19 +599,20 @@ set(SOURCES
|
||||
util/slice.cc
|
||||
util/sst_file_manager_impl.cc
|
||||
util/status.cc
|
||||
util/status_message.cc
|
||||
util/string_util.cc
|
||||
util/sync_point.cc
|
||||
util/sync_point_impl.cc
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/trace_replay.cc
|
||||
util/transaction_test_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_compaction_filter.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/blob_db/blob_db_impl.cc
|
||||
utilities/blob_db/blob_db_impl_filesnapshot.cc
|
||||
utilities/blob_db/blob_dump_tool.cc
|
||||
utilities/blob_db/blob_file.cc
|
||||
utilities/blob_db/blob_log_reader.cc
|
||||
@@ -650,6 +654,7 @@ set(SOURCES
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/spatialdb/spatial_db.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
utilities/transactions/optimistic_transaction.cc
|
||||
utilities/transactions/pessimistic_transaction.cc
|
||||
@@ -913,12 +918,14 @@ if(WITH_TESTS)
|
||||
table/cleanable_test.cc
|
||||
table/cuckoo_table_builder_test.cc
|
||||
table/cuckoo_table_reader_test.cc
|
||||
table/data_block_hash_index_test.cc
|
||||
table/full_filter_block_test.cc
|
||||
table/merger_test.cc
|
||||
table/table_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
tools/trace_analyzer_test.cc
|
||||
util/arena_test.cc
|
||||
util/auto_roll_logger_test.cc
|
||||
util/autovector_test.cc
|
||||
|
||||
+37
-31
@@ -1,43 +1,48 @@
|
||||
# Rocksdb Change Log
|
||||
### 5.15.10 (9/13/2018)
|
||||
|
||||
# 5.16.6 (11/24/2018)
|
||||
### Bug Fixes
|
||||
* Fix the bug that WriteBatchWithIndex's SeekForPrev() doesn't see the entries with the same key.
|
||||
|
||||
## 5.16.5 (10/16/2018)
|
||||
### Bug Fixes
|
||||
* Fix slow flush/compaction when DB contains many snapshots. The problem became noticeable to us in DBs with 100,000+ snapshots, though it will affect others at different thresholds.
|
||||
* Properly set the stop key for a truncated manual CompactRange
|
||||
|
||||
## 5.16.4 (10/10/2018)
|
||||
### Bug Fixes
|
||||
* Fix corner case where a write group leader blocked due to write stall blocks other writers in queue with WriteOptions::no_slowdown set.
|
||||
|
||||
## 5.16.3 (10/1/2018)
|
||||
### Bug Fixes
|
||||
* Fix crash caused when `CompactFiles` run with `CompactionOptions::compression == CompressionType::kDisableCompressionOption`. Now that setting causes the compression type to be chosen according to the column family-wide compression options.
|
||||
|
||||
## 5.16.2 (9/21/2018)
|
||||
### Bug Fixes
|
||||
* Fix bug in partition filters with format_version=4.
|
||||
|
||||
## 5.16.1 (9/17/2018)
|
||||
### Bug Fixes
|
||||
* Remove trace_analyzer_tool from rocksdb_lib target in TARGETS file.
|
||||
* Fix RocksDB Java build and tests.
|
||||
* Remove sync point in Block destructor.
|
||||
|
||||
### 5.15.9 (9/4/2018)
|
||||
### Bug Fixes
|
||||
* Fix compilation errors on OS X clang due to '-Wsuggest-override'.
|
||||
|
||||
## 5.15.8 (8/31/2018)
|
||||
### Bug Fixes
|
||||
* Further avoid creating empty SSTs and subsequently deleting them during compaction.
|
||||
|
||||
## 5.15.7 (8/24/2018)
|
||||
### Bug Fixes
|
||||
* Avoid creating empty SSTs and subsequently deleting them in certain cases during compaction.
|
||||
|
||||
## 5.15.6 (8/21/2018)
|
||||
## 5.16.0 (8/21/2018)
|
||||
### Public API Change
|
||||
* `OnTableFileCreated` will now be called for empty files generated during compaction. In that case, `TableFileCreationInfo::file_path` will be "(nil)" and `TableFileCreationInfo::file_size` will be zero.
|
||||
* Add `FlushOptions::allow_write_stall`, which controls whether Flush calls start working immediately, even if it causes user writes to stall, or will wait until flush can be performed without causing write stall (similar to `CompactRangeOptions::allow_write_stall`). Note that the default value is false, meaning we add delay to Flush calls until stalling can be avoided when possible. This is behavior change compared to previous RocksDB versions, where Flush calls didn't check if they might cause stall or not.
|
||||
|
||||
* The merge operands are passed to `MergeOperator::ShouldMerge` in the reversed order relative to how they were merged (passed to FullMerge or FullMergeV2) for performance reasons
|
||||
* GetAllKeyVersions() to take an extra argument of `max_num_ikeys`.
|
||||
|
||||
## 5.15.5 (8/16/2018)
|
||||
### Bug Fixes
|
||||
* Fix VerifyChecksum() API not preserving options
|
||||
### New Features
|
||||
* Changes the format of index blocks by delta encoding the index values, which are the block handles. This saves the encoding of BlockHandle::offset of the non-head index entries in each restart interval. The feature is backward compatible but not forward compatible. It is disabled by default unless format_version 4 or above is used.
|
||||
* Add a new tool: trace_analyzer. Trace_analyzer analyzes the trace file generated by using trace_replay API. It can convert the binary format trace file to a human readable txt file, output the statistics of the analyzed query types such as access statistics and size statistics, combining the dumped whole key space file to analyze, support query correlation analyzing, and etc. Current supported query types are: Get, Put, Delete, SingleDelete, DeleteRange, Merge, Iterator (Seek, SeekForPrev only).
|
||||
* Add hash index support to data blocks, which helps reducing the cpu utilization of point-lookup operations. This feature is backward compatible with the data block created without the hash index. It is disabled by default unless BlockBasedTableOptions::data_block_index_type is set to data_block_index_type = kDataBlockBinaryAndHash.
|
||||
|
||||
## 5.15.4 (8/11/2018)
|
||||
### Bug Fixes
|
||||
* Fix a bug caused by not generating OnTableFileCreated() notification for a 0-byte SST.
|
||||
|
||||
## 5.15.3 (8/10/2018)
|
||||
### Bug Fixes
|
||||
* Fix a bug in misreporting the estimated partition index size in properties block.
|
||||
|
||||
## 5.15.2 (8/9/2018)
|
||||
### Bug Fixes
|
||||
* Return correct usable_size for BlockContents.
|
||||
|
||||
## 5.15.1 (8/1/2018)
|
||||
### Bug Fixes
|
||||
* Prevent dereferencing invalid STL iterators when there are range tombstones in ingested files.
|
||||
* Avoid creating empty SSTs and subsequently deleting them in certain cases during compaction.
|
||||
|
||||
## 5.15.0 (7/17/2018)
|
||||
### Public API Change
|
||||
@@ -48,12 +53,13 @@
|
||||
* The "rocksdb.num.entries" table property no longer counts range deletion tombstones as entries.
|
||||
|
||||
### New Features
|
||||
* Changes the format of index blocks by storing the key in their raw form rather than converting them to InternalKey. This saves 8 bytes per index key. The feature is backward compatbile but not forward compatible. It is disabled by default unless format_version 3 or above is used.
|
||||
* Changes the format of index blocks by storing the key in their raw form rather than converting them to InternalKey. This saves 8 bytes per index key. The feature is backward compatible but not forward compatible. It is disabled by default unless format_version 3 or above is used.
|
||||
* Avoid memcpy when reading mmap files with OpenReadOnly and max_open_files==-1.
|
||||
* Support dynamically changing `ColumnFamilyOptions::ttl` via `SetOptions()`.
|
||||
* Add a new table property, "rocksdb.num.range-deletions", which counts the number of range deletion tombstones in the table.
|
||||
* Improve the performance of iterators doing long range scans by using readahead, when using direct IO.
|
||||
* pin_top_level_index_and_filter (default true) in BlockBasedTableOptions can be used in combination with cache_index_and_filter_blocks to prefetch and pin the top-level index of partitioned index and filter blocks in cache. It has no impact when cache_index_and_filter_blocks is false.
|
||||
* Write properties meta-block at the end of block-based table to save read-ahead IO.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix deadlock with enable_pipelined_write=true and max_successive_merges > 0
|
||||
|
||||
@@ -382,6 +382,8 @@ VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
|
||||
BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
ANALYZETOOLOBJECTS = $(ANALYZER_LIB_SOURCES:.cc=.o)
|
||||
|
||||
EXPOBJECTS = $(EXP_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
@@ -437,6 +439,7 @@ TESTS = \
|
||||
table_properties_collector_test \
|
||||
arena_test \
|
||||
block_test \
|
||||
data_block_hash_index_test \
|
||||
cache_test \
|
||||
corruption_test \
|
||||
slice_transform_test \
|
||||
@@ -529,6 +532,7 @@ TESTS = \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
db_universal_compaction_test \
|
||||
trace_analyzer_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -572,6 +576,7 @@ TOOLS = \
|
||||
rocksdb_dump \
|
||||
rocksdb_undump \
|
||||
blob_dump \
|
||||
trace_analyzer \
|
||||
|
||||
TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
@@ -665,7 +670,7 @@ $(SHARED4): $(shared_all_libobjects)
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
release tags tags0 valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava install install-static install-shared uninstall \
|
||||
analyze tools tools_lib
|
||||
|
||||
@@ -1018,6 +1023,13 @@ tags:
|
||||
cscope -b `$(FIND) . -name '*.cc'` `$(FIND) . -name '*.h'` `$(FIND) . -name '*.c'`
|
||||
ctags -e -R -o etags *
|
||||
|
||||
tags0:
|
||||
ctags -R .
|
||||
cscope -b `$(FIND) . -name '*.cc' -and ! -name '*_test.cc'` \
|
||||
`$(FIND) . -name '*.c' -and ! -name '*_test.c'` \
|
||||
`$(FIND) . -name '*.h' -and ! -name '*_test.h'`
|
||||
ctags -e -R -o etags *
|
||||
|
||||
format:
|
||||
build_tools/format-diff.sh
|
||||
|
||||
@@ -1031,7 +1043,7 @@ $(LIBRARY): $(LIBOBJECTS)
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $(LIBOBJECTS)
|
||||
|
||||
$(TOOLS_LIBRARY): $(BENCH_LIB_SOURCES:.cc=.o) $(TOOL_LIB_SOURCES:.cc=.o) $(LIB_SOURCES:.cc=.o) $(TESTUTIL)
|
||||
$(TOOLS_LIBRARY): $(BENCH_LIB_SOURCES:.cc=.o) $(TOOL_LIB_SOURCES:.cc=.o) $(LIB_SOURCES:.cc=.o) $(TESTUTIL) $(ANALYZER_LIB_SOURCES:.cc=.o)
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
@@ -1042,6 +1054,9 @@ librocksdb_env_basic_test.a: env/env_basic_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
trace_analyzer: tools/trace_analyzer.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_bench: cache/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1350,6 +1365,9 @@ table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_test: table/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
data_block_hash_index_test: table/data_block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
inlineskiplist_test: memtable/inlineskiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1446,6 +1464,9 @@ options_util_test: utilities/options/options_util_test.o $(LIBOBJECTS) $(TESTHAR
|
||||
db_bench_tool_test: tools/db_bench_tool_test.o $(BENCHTOOLOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
trace_analyzer_test: tools/trace_analyzer_test.o $(LIBOBJECTS) $(ANALYZETOOLOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1760,20 +1781,26 @@ rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastatic
|
||||
DOCKER_LINUX_X64_CONTAINER=`docker ps -aqf name=rocksdb_linux_x64-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X64_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x64-be evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x64-be
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastatic rocksdbjavastaticdockerx86 rocksdbjavastaticdockerx86_64
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_X86_CONTAINER=`docker ps -aqf name=rocksdb_linux_x86-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X86_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x86-be evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x86-be
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_X64_CONTAINER=`docker ps -aqf name=rocksdb_linux_x64-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X64_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x64-be evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x64-be
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
@@ -1898,7 +1925,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(EXP_LIB_SOURCES)
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(EXP_LIB_SOURCES) $(ANALYZER_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.cc.d)
|
||||
|
||||
# Add proper dependency support so changing a .h file forces a .cc file to
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
@@ -171,6 +173,8 @@ cpp_library(
|
||||
"table/cuckoo_table_builder.cc",
|
||||
"table/cuckoo_table_factory.cc",
|
||||
"table/cuckoo_table_reader.cc",
|
||||
"table/data_block_footer.cc",
|
||||
"table/data_block_hash_index.cc",
|
||||
"table/flush_block_policy.cc",
|
||||
"table/format.cc",
|
||||
"table/full_filter_block.cc",
|
||||
@@ -218,18 +222,19 @@ cpp_library(
|
||||
"util/slice.cc",
|
||||
"util/sst_file_manager_impl.cc",
|
||||
"util/status.cc",
|
||||
"util/status_message.cc",
|
||||
"util/string_util.cc",
|
||||
"util/sync_point.cc",
|
||||
"util/sync_point_impl.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/trace_replay.cc",
|
||||
"util/transaction_test_util.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/backupable/backupable_db.cc",
|
||||
"utilities/blob_db/blob_compaction_filter.cc",
|
||||
"utilities/blob_db/blob_db.cc",
|
||||
"utilities/blob_db/blob_db_impl.cc",
|
||||
"utilities/blob_db/blob_db_impl_filesnapshot.cc",
|
||||
"utilities/blob_db/blob_dump_tool.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/blob_db/blob_log_format.cc",
|
||||
@@ -269,6 +274,7 @@ cpp_library(
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/spatialdb/spatial_db.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
@@ -299,6 +305,7 @@ cpp_library(
|
||||
srcs = [
|
||||
"db/db_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"util/fault_injection_test_env.cc",
|
||||
"util/testharness.cc",
|
||||
"util/testutil.cc",
|
||||
@@ -497,6 +504,11 @@ ROCKS_TESTS = [
|
||||
"table/cuckoo_table_reader_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"data_block_hash_index_test",
|
||||
"table/data_block_hash_index_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"date_tiered_test",
|
||||
"utilities/date_tiered/date_tiered_test.cc",
|
||||
@@ -982,6 +994,11 @@ ROCKS_TESTS = [
|
||||
"util/timer_queue_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"trace_analyzer_test",
|
||||
"tools/trace_analyzer_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"transaction_test",
|
||||
"utilities/transactions/transaction_test.cc",
|
||||
@@ -1080,4 +1097,3 @@ if not is_opt_mode:
|
||||
deps = [":" + test_bin],
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin]
|
||||
)
|
||||
|
||||
@@ -2,7 +2,10 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
rocksdb_target_header = """REPO_PATH = package_name() + "/"
|
||||
rocksdb_target_header = """
|
||||
load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
|
||||
|
||||
+18
-19
@@ -1,19 +1,18 @@
|
||||
# shellcheck disable=SC2148
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/8219ec1bcedf8ad9da05e121e193364de2cc4f61/5.x/centos6-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/64d8d58e3d84f8bde7a029763d4f5baf39d0d5b9/stable/centos6-native/6aaf4de
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/ba9be983c81de7299b59fe71950c664a84dcb5f8/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/f20197cf3d4bd50339c9777aaa0b2ccadad9e2cb/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/6427ce8c7496e4ab06c2da81543b94c0de8be3d0/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/8f1e8b867d26efef93eac2fabbdb2e1d512665d7/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/70471c0571559fe0af7db6d7e8860b93a7eadfe1/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/453c89d6f0e68cdf1c151c769197fabedad9cac8/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/00a40fa5f8bd2cd0622f2e868552793aef37ccf4/1.3.0/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/47eef08f9acb77de982fbda6047c26d330739538/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/4414ddc78df8008b35cc4adac23590ad29148584/master/gcc-5-glibc-2.23/d506c82
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9d7ae2693d05d62f9a579cb21e6b717cf257a75d/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/2b2dd58e3a52ccf2c1d827def59e5f740de0ad15/1.2/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/379addf7ab2468a2b4293b47456cfcd1c9cb318d/4.3/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/3f68f5fe65a85b7c2d3e66852268fbd1efdb3151/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b9fab0aec99d9c36408e810b2677e91c12807afd/2.28/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/423431d61786b20bcc3bde8972901130cb29e6b3/3.11.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/3b0bb3bd9a0f690a069c479fcc0f7424fc7456d2/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/112ec378fec7002ad3e09afde022e656049f7191/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/04999bdb3ce81a11073535dcb00b5e13dc1cbaf5/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/92b0c8e5c8eecc71eb042594ce1ab3413799b385/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/3d8698d5973ba94f41620a80a67e4457fdf01e90/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/af6628a46758f1a15484a1760cd7294164bc5ba1/1.3.5/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b1a0e56c1e3e6929813a4331ade3a58ff083afbb/master/gcc-5-glibc-2.23/aa64d6b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/55031de95a2b46c82948743419a603b3d6aefe28/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
@@ -64,8 +64,12 @@ class MatchErrorParser(ErrorParserBase):
|
||||
|
||||
class CompilerErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# format: '<filename>:<line #>:<column #>: error: <error msg>'
|
||||
super(CompilerErrorParser, self).__init__(r'\S+:\d+:\d+: error:')
|
||||
# format (compile error):
|
||||
# '<filename>:<line #>:<column #>: error: <error msg>'
|
||||
# format (link error):
|
||||
# '<filename>:<line #>: error: <error msg>'
|
||||
# The below regex catches both
|
||||
super(CompilerErrorParser, self).__init__(r'\S+:\d+: error:')
|
||||
|
||||
|
||||
class ScanBuildErrorParser(MatchErrorParser):
|
||||
|
||||
@@ -43,12 +43,16 @@ if test -z $PIC_BUILD; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
|
||||
@@ -89,6 +89,8 @@ CLANG="USE_CLANG=1"
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
TSAN_CRASH='CRASH_TEST_EXT_ARGS="--compression_type=zstd --log2_keys_per_lock=22"'
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
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"
|
||||
SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-8u60-64/; export PATH=\$JAVA_HOME/bin:\$PATH"
|
||||
@@ -381,14 +383,14 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -453,7 +455,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -495,7 +497,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -561,7 +563,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'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',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
|
||||
@@ -104,8 +104,8 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/5.x/centos6-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos6-native/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/5.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
@@ -125,7 +125,7 @@ get_lib_base libunwind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base tbb LATEST gcc-5-glibc-2.23
|
||||
|
||||
get_lib_base kernel-headers 4.0.9-36_fbk5_2933_gd092e3f gcc-5-glibc-2.23
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lua 5.2.3 gcc-5-glibc-2.23
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ class VersionEdit;
|
||||
class TableBuilder;
|
||||
class WritableFileWriter;
|
||||
class InternalStats;
|
||||
class InternalIterator;
|
||||
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown. It must outlive the
|
||||
|
||||
@@ -2433,6 +2433,18 @@ void rocksdb_options_set_max_write_buffer_number_to_maintain(
|
||||
opt->rep.max_write_buffer_number_to_maintain = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_subcompactions(rocksdb_options_t* opt, uint32_t n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_background_jobs(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_background_jobs = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_background_compactions(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_background_compactions = n;
|
||||
}
|
||||
|
||||
@@ -308,6 +308,47 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
// Check that passing `CompressionType::kDisableCompressionOption` to
|
||||
// `CompactFiles` causes it to use the column family compression options.
|
||||
for (auto compaction_style :
|
||||
{CompactionStyle::kCompactionStyleLevel,
|
||||
CompactionStyle::kCompactionStyleUniversal,
|
||||
CompactionStyle::kCompactionStyleNone}) {
|
||||
DestroyDB(db_name_, Options());
|
||||
Options options;
|
||||
options.compaction_style = compaction_style;
|
||||
// L0: Snappy, L1: ZSTD, L2: Snappy
|
||||
options.compression_per_level = {CompressionType::kSnappyCompression,
|
||||
CompressionType::kZlibCompression,
|
||||
CompressionType::kSnappyCompression};
|
||||
options.create_if_missing = true;
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
DB* db = nullptr;
|
||||
ASSERT_OK(DB::Open(options, db_name_, &db));
|
||||
|
||||
db->Put(WriteOptions(), "key", "val");
|
||||
db->Flush(FlushOptions());
|
||||
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
ASSERT_EQ(1, l0_files.size());
|
||||
|
||||
// L0->L1 compaction, so output should be ZSTD-compressed
|
||||
CompactionOptions compaction_opts;
|
||||
compaction_opts.compression = CompressionType::kDisableCompressionOption;
|
||||
ASSERT_OK(db->CompactFiles(compaction_opts, l0_files, 1));
|
||||
|
||||
rocksdb::TablePropertiesCollection all_tables_props;
|
||||
ASSERT_OK(db->GetPropertiesOfAllTables(&all_tables_props));
|
||||
for (const auto& name_and_table_props : all_tables_props) {
|
||||
ASSERT_EQ(CompressionTypeToString(CompressionType::kZlibCompression),
|
||||
name_and_table_props.second->compression_name);
|
||||
}
|
||||
delete db;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -77,6 +77,12 @@ CompactionIterator::CompactionIterator(
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// findEarliestVisibleSnapshot assumes this ordering.
|
||||
for (size_t i = 1; i < snapshots_->size(); ++i) {
|
||||
assert(snapshots_->at(i - 1) <= snapshots_->at(i));
|
||||
}
|
||||
#endif
|
||||
if (compaction_filter_ != nullptr) {
|
||||
if (compaction_filter_->IgnoreSnapshots()) {
|
||||
ignore_snapshots_ = true;
|
||||
@@ -603,18 +609,23 @@ void CompactionIterator::PrepareOutput() {
|
||||
inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, SequenceNumber* prev_snapshot) {
|
||||
assert(snapshots_->size());
|
||||
SequenceNumber prev = kMaxSequenceNumber;
|
||||
for (const auto cur : *snapshots_) {
|
||||
assert(prev == kMaxSequenceNumber || prev <= cur);
|
||||
if (cur >= in && (snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(in, cur))) {
|
||||
*prev_snapshot = prev == kMaxSequenceNumber ? 0 : prev;
|
||||
auto snapshots_iter = std::lower_bound(
|
||||
snapshots_->begin(), snapshots_->end(), in);
|
||||
if (snapshots_iter == snapshots_->begin()) {
|
||||
*prev_snapshot = 0;
|
||||
} else {
|
||||
*prev_snapshot = *std::prev(snapshots_iter);
|
||||
assert(*prev_snapshot < in);
|
||||
}
|
||||
for (; snapshots_iter != snapshots_->end(); ++snapshots_iter) {
|
||||
auto cur = *snapshots_iter;
|
||||
assert(in <= cur);
|
||||
if (snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(in, cur)) {
|
||||
return cur;
|
||||
}
|
||||
prev = cur;
|
||||
assert(prev < kMaxSequenceNumber);
|
||||
*prev_snapshot = cur;
|
||||
}
|
||||
*prev_snapshot = prev;
|
||||
return kMaxSequenceNumber;
|
||||
}
|
||||
|
||||
|
||||
@@ -600,6 +600,8 @@ Status CompactionJob::Run() {
|
||||
compaction_stats_.micros = env_->NowMicros() - start_micros;
|
||||
MeasureTime(stats_, COMPACTION_TIME, compaction_stats_.micros);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run:BeforeVerify");
|
||||
|
||||
// Check if any thread encountered an error during execution
|
||||
Status status;
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
|
||||
+34
-12
@@ -219,7 +219,8 @@ void CompactionPicker::GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
|
||||
bool CompactionPicker::ExpandInputsToCleanCut(const std::string& /*cf_name*/,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs) {
|
||||
CompactionInputFiles* inputs,
|
||||
InternalKey** next_smallest) {
|
||||
// This isn't good compaction
|
||||
assert(!inputs->empty());
|
||||
|
||||
@@ -242,7 +243,8 @@ bool CompactionPicker::ExpandInputsToCleanCut(const std::string& /*cf_name*/,
|
||||
GetRange(*inputs, &smallest, &largest);
|
||||
inputs->clear();
|
||||
vstorage->GetOverlappingInputs(level, &smallest, &largest, &inputs->files,
|
||||
hint_index, &hint_index);
|
||||
hint_index, &hint_index, true,
|
||||
next_smallest);
|
||||
} while (inputs->size() > old_size);
|
||||
|
||||
// we started off with inputs non-empty and the previous loop only grew
|
||||
@@ -315,13 +317,29 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
// shouldn't have been released since.
|
||||
assert(!FilesRangeOverlapWithCompaction(input_files, output_level));
|
||||
|
||||
auto c =
|
||||
new Compaction(vstorage, ioptions_, mutable_cf_options, input_files,
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
compact_options.compression, ioptions_.compression_opts,
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
CompressionType compression_type;
|
||||
if (compact_options.compression == kDisableCompressionOption) {
|
||||
int base_level;
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
base_level = vstorage->base_level();
|
||||
} else {
|
||||
base_level = 1;
|
||||
}
|
||||
compression_type =
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, base_level);
|
||||
} else {
|
||||
// TODO(ajkr): `CompactionOptions` offers configurable `CompressionType`
|
||||
// without configurable `CompressionOptions`, which is inconsistent.
|
||||
compression_type = compact_options.compression;
|
||||
}
|
||||
auto c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, input_files, output_level,
|
||||
compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -633,7 +651,6 @@ Compaction* CompactionPicker::CompactRange(
|
||||
uint64_t s = inputs[i]->compensated_file_size;
|
||||
total += s;
|
||||
if (total >= limit) {
|
||||
**compaction_end = inputs[i + 1]->smallest;
|
||||
covering_the_whole_range = false;
|
||||
inputs.files.resize(i + 1);
|
||||
break;
|
||||
@@ -642,7 +659,10 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
assert(output_path_id < static_cast<uint32_t>(ioptions_.cf_paths.size()));
|
||||
|
||||
if (ExpandInputsToCleanCut(cf_name, vstorage, &inputs) == false) {
|
||||
InternalKey key_storage;
|
||||
InternalKey* next_smallest = &key_storage;
|
||||
if (ExpandInputsToCleanCut(cf_name, vstorage, &inputs, &next_smallest) ==
|
||||
false) {
|
||||
// manual compaction is now multi-threaded, so it can
|
||||
// happen that ExpandWhileOverlapping fails
|
||||
// we handle it higher in RunManualCompaction
|
||||
@@ -650,8 +670,10 @@ Compaction* CompactionPicker::CompactRange(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (covering_the_whole_range) {
|
||||
if (covering_the_whole_range || !next_smallest) {
|
||||
*compaction_end = nullptr;
|
||||
} else {
|
||||
**compaction_end = *next_smallest;
|
||||
}
|
||||
|
||||
CompactionInputFiles output_level_inputs;
|
||||
|
||||
@@ -151,7 +151,8 @@ class CompactionPicker {
|
||||
// Will return false if it is impossible to apply this compaction.
|
||||
bool ExpandInputsToCleanCut(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs);
|
||||
CompactionInputFiles* inputs,
|
||||
InternalKey** next_smallest = nullptr);
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool IsRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
|
||||
@@ -90,8 +90,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
f->smallest = InternalKey(smallest, smallest_seq, kTypeValue);
|
||||
f->largest = InternalKey(largest, largest_seq, kTypeValue);
|
||||
f->smallest_seqno = smallest_seq;
|
||||
f->largest_seqno = largest_seq;
|
||||
f->fd.smallest_seqno = smallest_seq;
|
||||
f->fd.largest_seqno = largest_seq;
|
||||
f->compensated_file_size = file_size;
|
||||
f->refs = 0;
|
||||
vstorage_->AddFile(level, f);
|
||||
|
||||
@@ -97,17 +97,17 @@ void GetSmallestLargestSeqno(const std::vector<FileMetaData*>& files,
|
||||
SequenceNumber* largest_seqno) {
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : files) {
|
||||
assert(f->smallest_seqno <= f->largest_seqno);
|
||||
assert(f->fd.smallest_seqno <= f->fd.largest_seqno);
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
*smallest_seqno = f->smallest_seqno;
|
||||
*largest_seqno = f->largest_seqno;
|
||||
*smallest_seqno = f->fd.smallest_seqno;
|
||||
*largest_seqno = f->fd.largest_seqno;
|
||||
} else {
|
||||
if (f->smallest_seqno < *smallest_seqno) {
|
||||
*smallest_seqno = f->smallest_seqno;
|
||||
if (f->fd.smallest_seqno < *smallest_seqno) {
|
||||
*smallest_seqno = f->fd.smallest_seqno;
|
||||
}
|
||||
if (f->largest_seqno > *largest_seqno) {
|
||||
*largest_seqno = f->largest_seqno;
|
||||
if (f->fd.largest_seqno > *largest_seqno) {
|
||||
*largest_seqno = f->fd.largest_seqno;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,11 +365,11 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
size_t level_index = 0U;
|
||||
if (c->start_level() == 0) {
|
||||
for (auto f : *c->inputs(0)) {
|
||||
assert(f->smallest_seqno <= f->largest_seqno);
|
||||
assert(f->fd.smallest_seqno <= f->fd.largest_seqno);
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
}
|
||||
prev_smallest_seqno = f->smallest_seqno;
|
||||
prev_smallest_seqno = f->fd.smallest_seqno;
|
||||
}
|
||||
level_index = 1U;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ class DBBloomFilterTest : public DBTestBase {
|
||||
|
||||
class DBBloomFilterTestWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public testing::WithParamInterface<std::tuple<bool, bool, uint32_t>> {
|
||||
// public testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
bool use_block_based_filter_;
|
||||
bool partition_filters_;
|
||||
uint32_t format_version_;
|
||||
|
||||
public:
|
||||
DBBloomFilterTestWithParam() : DBTestBase("/db_bloom_filter_tests") {}
|
||||
@@ -36,9 +37,12 @@ class DBBloomFilterTestWithParam
|
||||
void SetUp() override {
|
||||
use_block_based_filter_ = std::get<0>(GetParam());
|
||||
partition_filters_ = std::get<1>(GetParam());
|
||||
format_version_ = std::get<2>(GetParam());
|
||||
}
|
||||
};
|
||||
|
||||
class DBBloomFilterTestDefFormatVersion : public DBBloomFilterTestWithParam {};
|
||||
|
||||
class SliceTransformLimitedDomainGeneric : public SliceTransform {
|
||||
const char* Name() const override {
|
||||
return "SliceTransformLimitedDomainGeneric";
|
||||
@@ -62,7 +66,7 @@ class SliceTransformLimitedDomainGeneric : public SliceTransform {
|
||||
// KeyMayExist can lead to a few false positives, but not false negatives.
|
||||
// To make test deterministic, use a much larger number of bits per key-20 than
|
||||
// bits in the key, so that false positives are eliminated
|
||||
TEST_P(DBBloomFilterTestWithParam, KeyMayExist) {
|
||||
TEST_P(DBBloomFilterTestDefFormatVersion, KeyMayExist) {
|
||||
do {
|
||||
ReadOptions ropts;
|
||||
std::string value;
|
||||
@@ -401,6 +405,11 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
table_options.format_version = format_version_;
|
||||
if (format_version_ >= 4) {
|
||||
// value delta encoding challenged more with index interval > 1
|
||||
table_options.index_block_restart_interval = 8;
|
||||
}
|
||||
table_options.metadata_block_size = 32;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -456,10 +465,26 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBloomFilterTestWithParam, DBBloomFilterTestWithParam,
|
||||
::testing::Values(std::make_tuple(true, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(false, false)));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestDefFormatVersion,
|
||||
::testing::Values(std::make_tuple(true, false, test::kDefaultFormatVersion),
|
||||
std::make_tuple(false, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(false, false,
|
||||
test::kDefaultFormatVersion)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestWithParam,
|
||||
::testing::Values(std::make_tuple(true, false, test::kDefaultFormatVersion),
|
||||
std::make_tuple(false, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(false, false,
|
||||
test::kDefaultFormatVersion)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, DBBloomFilterTestWithParam,
|
||||
::testing::Values(std::make_tuple(true, false, test::kLatestFormatVersion),
|
||||
std::make_tuple(false, true, test::kLatestFormatVersion),
|
||||
std::make_tuple(false, false,
|
||||
test::kLatestFormatVersion)));
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
while (ChangeFilterOptions()) {
|
||||
|
||||
+60
-10
@@ -3501,12 +3501,13 @@ TEST_F(DBCompactionTest, CompactRangeDelayedByL0FileCount) {
|
||||
// ensure the auto compaction doesn't finish until manual compaction has
|
||||
// had a chance to be delayed.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait", "CompactionJob::Run():End"}});
|
||||
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
|
||||
"CompactionJob::Run():End"}});
|
||||
} else {
|
||||
// ensure the auto-compaction doesn't finish until manual compaction has
|
||||
// continued without delay.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
{{"DBImpl::FlushMemTable:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3554,12 +3555,13 @@ TEST_F(DBCompactionTest, CompactRangeDelayedByImmMemTableCount) {
|
||||
// ensure the flush doesn't finish until manual compaction has had a
|
||||
// chance to be delayed.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait", "FlushJob::WriteLevel0Table"}});
|
||||
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
|
||||
"FlushJob::WriteLevel0Table"}});
|
||||
} else {
|
||||
// ensure the flush doesn't finish until manual compaction has continued
|
||||
// without delay.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWaitDone",
|
||||
{{"DBImpl::FlushMemTable:StallWaitDone",
|
||||
"FlushJob::WriteLevel0Table"}});
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -3569,6 +3571,7 @@ TEST_F(DBCompactionTest, CompactRangeDelayedByImmMemTableCount) {
|
||||
ASSERT_OK(Put(Key(0), RandomString(&rnd, 1024)));
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
flush_opts.allow_write_stall = true;
|
||||
dbfull()->Flush(flush_opts);
|
||||
}
|
||||
|
||||
@@ -3604,7 +3607,7 @@ TEST_F(DBCompactionTest, CompactRangeShutdownWhileDelayed) {
|
||||
// The auto-compaction waits until the manual compaction finishes to ensure
|
||||
// the signal comes from closing CF/DB, not from compaction making progress.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait",
|
||||
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
|
||||
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PreShutdown"},
|
||||
{"DBCompactionTest::CompactRangeShutdownWhileDelayed:PostManual",
|
||||
"CompactionJob::Run():End"}});
|
||||
@@ -3655,18 +3658,21 @@ TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
// began. So it unblocks CompactRange and precludes its flush. Throughout the
|
||||
// test, stall conditions are upheld via high L0 file count.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait",
|
||||
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
|
||||
"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush"},
|
||||
{"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush",
|
||||
"DBImpl::CompactRange:StallWaitDone"},
|
||||
{"DBImpl::CompactRange:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
"DBImpl::FlushMemTable:StallWaitDone"},
|
||||
{"DBImpl::FlushMemTable:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
//used for the delayable flushes
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
for (int i = 0; i < kNumL0FilesLimit - 1; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
dbfull()->Flush(flush_opts);
|
||||
}
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
CompactRangeOptions cro;
|
||||
@@ -3676,7 +3682,7 @@ TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush");
|
||||
Put(ToString(0), RandomString(&rnd, 1024));
|
||||
Flush();
|
||||
dbfull()->Flush(flush_opts);
|
||||
Put(ToString(0), RandomString(&rnd, 1024));
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush");
|
||||
manual_compaction_thread.join();
|
||||
@@ -3953,6 +3959,50 @@ INSTANTIATE_TEST_CASE_P(
|
||||
CompactionPri::kOldestSmallestSeqFirst,
|
||||
CompactionPri::kMinOverlappingRatio));
|
||||
|
||||
class NoopMergeOperator : public MergeOperator {
|
||||
public:
|
||||
NoopMergeOperator() {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
std::string val("bar");
|
||||
merge_out->new_value = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "Noop"; }
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, PartialManualCompaction) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.num_levels = 3;
|
||||
opts.level0_file_num_compaction_trigger = 10;
|
||||
opts.compression = kNoCompression;
|
||||
opts.merge_operator.reset(new NoopMergeOperator());
|
||||
opts.target_file_size_base = 10240;
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 8; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
Merge("foo", RandomString(&rnd, 1024));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
std::string prop;
|
||||
EXPECT_TRUE(dbfull()->GetProperty(DB::Properties::kLiveSstFilesSize, &prop));
|
||||
uint64_t max_compaction_bytes = atoi(prop.c_str()) / 2;
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"max_compaction_bytes", std::to_string(max_compaction_bytes)}}));
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
Reopen(options);
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
no_wait.allow_write_stall=true;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"VersionSet::LogAndApply:WriteManifest",
|
||||
@@ -55,6 +56,9 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifndef TRAVIS
|
||||
// Disable this test temporarily on Travis as it fails intermittently.
|
||||
// Github issue: #4151
|
||||
TEST_F(DBFlushTest, SyncFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
@@ -92,6 +96,7 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
ASSERT_EQ(refs_before, cfd->current()->TEST_refs());
|
||||
Destroy(options);
|
||||
}
|
||||
#endif // TRAVIS
|
||||
|
||||
TEST_F(DBFlushTest, FlushInLowPriThreadPool) {
|
||||
// Verify setting an empty high-pri (flush) thread pool causes flushes to be
|
||||
|
||||
+85
-17
@@ -1047,7 +1047,7 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
} else {
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
return NewErrorInternalIterator(s, arena);
|
||||
return NewErrorInternalIterator<Slice>(s, arena);
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* DBImpl::DefaultColumnFamily() const {
|
||||
@@ -1071,6 +1071,15 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
if (tracer_) {
|
||||
// TODO: This mutex should be removed later, to improve performance when
|
||||
// tracing is enabled.
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
tracer_->Get(column_family, key);
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire SuperVersion
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
|
||||
@@ -1609,8 +1618,8 @@ if (read_options.tailing) {
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback);
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations, read_callback,
|
||||
this, cfd);
|
||||
#endif
|
||||
} else {
|
||||
// Note: no need to consider the special case of
|
||||
@@ -1677,9 +1686,8 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback,
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd, allow_blob,
|
||||
allow_refresh);
|
||||
sv->version_number, read_callback, this, cfd, allow_blob,
|
||||
((read_options.snapshot != nullptr) ? false : allow_refresh));
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
@@ -1716,7 +1724,7 @@ Status DBImpl::NewIterators(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback));
|
||||
read_callback, this, cfd));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
@@ -2227,9 +2235,9 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
FlushReason::kDeleteFiles);
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions(), FlushReason::kDeleteFiles);
|
||||
}
|
||||
FindObsoleteFiles(&job_context, false);
|
||||
} // lock released here
|
||||
@@ -2312,9 +2320,9 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
FlushReason::kDeleteFiles);
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions(), FlushReason::kDeleteFiles);
|
||||
}
|
||||
for (auto* deleted_file : deleted_files) {
|
||||
deleted_file->being_compacted = false;
|
||||
@@ -2879,6 +2887,10 @@ Status DBImpl::IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) {
|
||||
if (external_files.empty()) {
|
||||
return Status::InvalidArgument("external_files is empty");
|
||||
}
|
||||
|
||||
Status status;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
@@ -2896,6 +2908,9 @@ Status DBImpl::IngestExternalFile(
|
||||
immutable_db_options_, env_options_,
|
||||
&snapshots_, ingestion_options);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
uint64_t next_file_number = 0;
|
||||
std::list<uint64_t>::iterator pending_output_elem;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -2906,10 +2921,29 @@ Status DBImpl::IngestExternalFile(
|
||||
|
||||
// Make sure that bg cleanup wont delete the files that we are ingesting
|
||||
pending_output_elem = CaptureCurrentFileNumberInPendingOutputs();
|
||||
|
||||
// If crash happen after a hard link established, Recover function may
|
||||
// reuse the file number that has already assigned to the internal file,
|
||||
// and this will overwrite the external file. To protect the external
|
||||
// file, we have to make sure the file number will never being reused.
|
||||
next_file_number = versions_->FetchAddFileNumber(external_files.size());
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
|
||||
}
|
||||
}
|
||||
dummy_sv_ctx.Clean();
|
||||
if (!status.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
return status;
|
||||
}
|
||||
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
status = ingestion_job.Prepare(external_files, super_version);
|
||||
status =
|
||||
ingestion_job.Prepare(external_files, next_file_number, super_version);
|
||||
CleanupSuperVersion(super_version);
|
||||
if (!status.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -3060,7 +3094,6 @@ Status DBImpl::VerifyChecksum() {
|
||||
|
||||
void DBImpl::NotifyOnExternalFileIngested(
|
||||
ColumnFamilyData* cfd, const ExternalSstFileIngestionJob& ingestion_job) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (immutable_db_options_.listeners.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -3076,8 +3109,6 @@ void DBImpl::NotifyOnExternalFileIngested(
|
||||
listener->OnExternalFileIngested(this, info);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void DBImpl::WaitForIngestFile() {
|
||||
@@ -3087,5 +3118,42 @@ void DBImpl::WaitForIngestFile() {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::StartTrace(const TraceOptions& /* options */,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
tracer_.reset(new Tracer(env_, std::move(trace_writer)));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::EndTrace() {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
Status s = tracer_->Close();
|
||||
tracer_.reset();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::TraceIteratorSeek(const uint32_t& cf_id, const Slice& key) {
|
||||
Status s;
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
s = tracer_->IteratorSeek(cf_id, key);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::TraceIteratorSeekForPrev(const uint32_t& cf_id, const Slice& key) {
|
||||
Status s;
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
s = tracer_->IteratorSeekForPrev(cf_id, key);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
+22
-2
@@ -46,6 +46,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/trace_reader_writer.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
@@ -54,6 +55,7 @@
|
||||
#include "util/hash.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/trace_replay.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -333,6 +335,15 @@ class DBImpl : public DB {
|
||||
|
||||
virtual Status VerifyChecksum() override;
|
||||
|
||||
using DB::StartTrace;
|
||||
virtual Status StartTrace(
|
||||
const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
|
||||
using DB::EndTrace;
|
||||
virtual Status EndTrace() override;
|
||||
Status TraceIteratorSeek(const uint32_t& cf_id, const Slice& key);
|
||||
Status TraceIteratorSeekForPrev(const uint32_t& cf_id, const Slice& key);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Similar to GetSnapshot(), but also lets the db know that this snapshot
|
||||
@@ -385,7 +396,7 @@ class DBImpl : public DB {
|
||||
Status TEST_SwitchMemtable(ColumnFamilyData* cfd = nullptr);
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status TEST_FlushMemTable(bool wait = true,
|
||||
Status TEST_FlushMemTable(bool wait = true, bool allow_write_stall = false,
|
||||
ColumnFamilyHandle* cfh = nullptr);
|
||||
|
||||
// Wait for memtable compaction
|
||||
@@ -697,6 +708,8 @@ class DBImpl : public DB {
|
||||
Statistics* stats_;
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
recovered_transactions_;
|
||||
std::unique_ptr<Tracer> tracer_;
|
||||
InstrumentedMutex trace_mutex_;
|
||||
|
||||
// Except in DB::Open(), WriteOptionsFile can only be called when:
|
||||
// Persist options to options file.
|
||||
@@ -790,18 +803,21 @@ class DBImpl : public DB {
|
||||
friend class WritePreparedTxnDB;
|
||||
friend class WriteBatchWithIndex;
|
||||
friend class WriteUnpreparedTxnDB;
|
||||
friend class WriteUnpreparedTxn;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
friend class ForwardIterator;
|
||||
#endif
|
||||
friend struct SuperVersion;
|
||||
friend class CompactedDBImpl;
|
||||
friend class DBTest_ConcurrentFlushWAL_Test;
|
||||
friend class DBTest_MixedSlowdownOptionsStop_Test;
|
||||
#ifndef NDEBUG
|
||||
friend class DBTest2_ReadCallbackTest_Test;
|
||||
friend class WriteCallbackTest_WriteWithCallbackTest_Test;
|
||||
friend class XFTransactionWriteHandler;
|
||||
friend class DBBlobIndexTest;
|
||||
friend class WriteUnpreparedTransactionTest_RecoveryRollbackUnprepared_Test;
|
||||
friend class WriteUnpreparedTransactionTest_RecoveryTest_Test;
|
||||
#endif
|
||||
struct CompactionState;
|
||||
|
||||
@@ -903,6 +919,10 @@ class DBImpl : public DB {
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
FlushReason flush_reason, bool writes_stopped = false);
|
||||
|
||||
// Wait until flushing this column family won't stall writes
|
||||
Status WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
bool* flush_needed);
|
||||
|
||||
// Wait for memtable flushed.
|
||||
// If flush_memtable_id is non-null, wait until the memtable with the ID
|
||||
// gets flush. Otherwise, wait until the column family don't have any
|
||||
|
||||
@@ -160,8 +160,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context->superversion_context,
|
||||
mutable_cf_options);
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context->superversion_contexts[0], mutable_cf_options);
|
||||
if (made_progress) {
|
||||
*made_progress = 1;
|
||||
}
|
||||
@@ -230,8 +230,8 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.job_id = job_id;
|
||||
info.triggered_writes_slowdown = triggered_writes_slowdown;
|
||||
info.triggered_writes_stop = triggered_writes_stop;
|
||||
info.smallest_seqno = file_meta->smallest_seqno;
|
||||
info.largest_seqno = file_meta->largest_seqno;
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.table_properties = prop;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
@@ -281,8 +281,8 @@ void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
info.job_id = job_id;
|
||||
info.triggered_writes_slowdown = triggered_writes_slowdown;
|
||||
info.triggered_writes_stop = triggered_writes_stop;
|
||||
info.smallest_seqno = file_meta->smallest_seqno;
|
||||
info.largest_seqno = file_meta->largest_seqno;
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.table_properties = prop;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
@@ -324,60 +324,12 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
|
||||
if (!options.allow_write_stall && flush_needed) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t orig_active_memtable_id = cfd->mem()->GetID();
|
||||
WriteStallCondition write_stall_condition = WriteStallCondition::kNormal;
|
||||
do {
|
||||
if (write_stall_condition != WriteStallCondition::kNormal) {
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:StallWait");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] CompactRange waiting on stall conditions to clear",
|
||||
cfd->GetName().c_str());
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
if (cfd->IsDropped() || shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
|
||||
uint64_t earliest_memtable_id =
|
||||
std::min(cfd->mem()->GetID(), cfd->imm()->GetEarliestMemTableID());
|
||||
if (earliest_memtable_id > orig_active_memtable_id) {
|
||||
// We waited so long that the memtable we were originally waiting on was
|
||||
// flushed.
|
||||
flush_needed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
// Skip stalling check if we're below auto-flush and auto-compaction
|
||||
// triggers. If it stalled in these conditions, that'd mean the stall
|
||||
// triggers are so low that stalling is needed for any background work. In
|
||||
// that case we shouldn't wait since background work won't be scheduled.
|
||||
if (cfd->imm()->NumNotFlushed() <
|
||||
cfd->ioptions()->min_write_buffer_number_to_merge &&
|
||||
vstorage->l0_delay_trigger_count() <
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
break;
|
||||
}
|
||||
|
||||
// check whether one extra immutable memtable or an extra L0 file would
|
||||
// cause write stalling mode to be entered. It could still enter stall
|
||||
// mode due to pending compaction bytes, but that's less common
|
||||
write_stall_condition =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + 1,
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options)
|
||||
.first;
|
||||
} while (write_stall_condition != WriteStallCondition::kNormal);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:StallWaitDone");
|
||||
Status s;
|
||||
if (flush_needed) {
|
||||
s = FlushMemTable(cfd, FlushOptions(), FlushReason::kManualCompaction);
|
||||
FlushOptions fo;
|
||||
fo.allow_write_stall = options.allow_write_stall;
|
||||
s = FlushMemTable(cfd, fo, FlushReason::kManualCompaction,
|
||||
false /* writes_stopped*/);
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
@@ -691,7 +643,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
Status status = compaction_job.Install(*c->mutable_cf_options());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kManualCompaction);
|
||||
}
|
||||
c->ReleaseCompactionFiles(s);
|
||||
@@ -885,7 +837,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
edit.DeleteFile(level, f->fd.GetNumber());
|
||||
edit.AddFile(to_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->smallest_seqno, f->largest_seqno,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
@@ -1077,6 +1029,14 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
FlushReason flush_reason, bool writes_stopped) {
|
||||
Status s;
|
||||
uint64_t flush_memtable_id = 0;
|
||||
if (!flush_options.allow_write_stall) {
|
||||
bool flush_needed = true;
|
||||
s = WaitUntilFlushWouldNotStallWrites(cfd, &flush_needed);
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:StallWaitDone");
|
||||
if (!s.ok() || !flush_needed) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
{
|
||||
WriteContext context;
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
@@ -1115,6 +1075,69 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
return s;
|
||||
}
|
||||
|
||||
// Calling FlushMemTable(), whether from DB::Flush() or from Backup Engine, can
|
||||
// cause write stall, for example if one memtable is being flushed already.
|
||||
// This method tries to avoid write stall (similar to CompactRange() behavior)
|
||||
// it emulates how the SuperVersion / LSM would change if flush happens, checks
|
||||
// it against various constrains and delays flush if it'd cause write stall.
|
||||
// Called should check status and flush_needed to see if flush already happened.
|
||||
Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
bool* flush_needed) {
|
||||
{
|
||||
*flush_needed = true;
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t orig_active_memtable_id = cfd->mem()->GetID();
|
||||
WriteStallCondition write_stall_condition = WriteStallCondition::kNormal;
|
||||
do {
|
||||
if (write_stall_condition != WriteStallCondition::kNormal) {
|
||||
TEST_SYNC_POINT("DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] WaitUntilFlushWouldNotStallWrites"
|
||||
" waiting on stall conditions to clear",
|
||||
cfd->GetName().c_str());
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
if (cfd->IsDropped() || shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
|
||||
uint64_t earliest_memtable_id =
|
||||
std::min(cfd->mem()->GetID(), cfd->imm()->GetEarliestMemTableID());
|
||||
if (earliest_memtable_id > orig_active_memtable_id) {
|
||||
// We waited so long that the memtable we were originally waiting on was
|
||||
// flushed.
|
||||
*flush_needed = false;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
// Skip stalling check if we're below auto-flush and auto-compaction
|
||||
// triggers. If it stalled in these conditions, that'd mean the stall
|
||||
// triggers are so low that stalling is needed for any background work. In
|
||||
// that case we shouldn't wait since background work won't be scheduled.
|
||||
if (cfd->imm()->NumNotFlushed() <
|
||||
cfd->ioptions()->min_write_buffer_number_to_merge &&
|
||||
vstorage->l0_delay_trigger_count() <
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
break;
|
||||
}
|
||||
|
||||
// check whether one extra immutable memtable or an extra L0 file would
|
||||
// cause write stalling mode to be entered. It could still enter stall
|
||||
// mode due to pending compaction bytes, but that's less common
|
||||
write_stall_condition =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + 1,
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options)
|
||||
.first;
|
||||
} while (write_stall_condition != WriteStallCondition::kNormal);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::WaitForFlushMemTable(ColumnFamilyData* cfd,
|
||||
const uint64_t* flush_memtable_id) {
|
||||
Status s;
|
||||
@@ -1775,7 +1798,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
@@ -1804,8 +1827,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->edit()->DeleteFile(c->level(l), f->fd.GetNumber());
|
||||
c->edit()->AddFile(c->output_level(), f->fd.GetNumber(),
|
||||
f->fd.GetPathId(), f->fd.GetFileSize(), f->smallest,
|
||||
f->largest, f->smallest_seqno, f->largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
f->largest, f->fd.smallest_seqno,
|
||||
f->fd.largest_seqno, f->marked_for_compaction);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -1822,7 +1845,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
@@ -1899,7 +1922,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
status = compaction_job.Install(*c->mutable_cf_options());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
}
|
||||
*made_progress = true;
|
||||
@@ -2091,7 +2114,8 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
old_sv->mutable_cf_options.max_write_buffer_number;
|
||||
}
|
||||
|
||||
if (sv_context->new_superversion == nullptr) {
|
||||
// this branch is unlikely to step in
|
||||
if (UNLIKELY(sv_context->new_superversion == nullptr)) {
|
||||
sv_context->NewSuperVersion();
|
||||
}
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_, mutable_cf_options);
|
||||
|
||||
+3
-1
@@ -100,9 +100,11 @@ Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
return SwitchMemtable(cfd, &write_context);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_FlushMemTable(bool wait, ColumnFamilyHandle* cfh) {
|
||||
Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
|
||||
ColumnFamilyHandle* cfh) {
|
||||
FlushOptions fo;
|
||||
fo.wait = wait;
|
||||
fo.allow_write_stall = allow_write_stall;
|
||||
ColumnFamilyData* cfd;
|
||||
if (cfh == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
|
||||
@@ -131,16 +131,16 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
edit.DeleteFile(0, f->fd.GetNumber());
|
||||
edit.AddFile(target_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->smallest_seqno, f->largest_seqno,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
+3
-3
@@ -361,7 +361,7 @@ Status DBImpl::Recover(
|
||||
s = env_->NewRandomAccessFile(IdentityFileName(dbname_), &idfile,
|
||||
customized_env);
|
||||
if (!s.ok()) {
|
||||
const char* error_msg = s.ToString().c_str();
|
||||
std::string error_str = s.ToString();
|
||||
// Check if unsupported Direct I/O is the root cause
|
||||
customized_env.use_direct_reads = false;
|
||||
s = env_->NewRandomAccessFile(IdentityFileName(dbname_), &idfile,
|
||||
@@ -371,7 +371,7 @@ Status DBImpl::Recover(
|
||||
"Direct I/O is not supported by the specified DB.");
|
||||
} else {
|
||||
return Status::InvalidArgument(
|
||||
"Found options incompatible with filesystem", error_msg);
|
||||
"Found options incompatible with filesystem", error_str.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -969,7 +969,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
if (s.ok() && meta.fd.GetFileSize() > 0) {
|
||||
edit->AddFile(level, meta.fd.GetNumber(), meta.fd.GetPathId(),
|
||||
meta.fd.GetFileSize(), meta.smallest, meta.largest,
|
||||
meta.smallest_seqno, meta.largest_seqno,
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.marked_for_compaction);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
SequenceNumber snapshot = versions_->LastSequence();
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
tracer_->Get(column_family, key);
|
||||
}
|
||||
}
|
||||
SuperVersion* super_version = cfd->GetSuperVersion();
|
||||
MergeContext merge_context;
|
||||
RangeDelAggregator range_del_agg(cfd->internal_comparator(), snapshot);
|
||||
|
||||
+23
-6
@@ -76,6 +76,12 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
tracer_->Write(my_batch);
|
||||
}
|
||||
}
|
||||
if (write_options.sync && write_options.disableWAL) {
|
||||
return Status::InvalidArgument("Sync writes has to enable WAL.");
|
||||
}
|
||||
@@ -311,7 +317,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
write_group, current_sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*recovery_log_number*/, this, parallel, seq_per_batch_);
|
||||
0 /*recovery_log_number*/, this, parallel, seq_per_batch_,
|
||||
batch_per_txn_);
|
||||
} else {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
// Note: the logic for advancing seq here must be consistent with the
|
||||
@@ -346,7 +353,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/,
|
||||
this, true /*concurrent_memtable_writes*/, seq_per_batch_,
|
||||
w.batch_cnt);
|
||||
w.batch_cnt, batch_per_txn_);
|
||||
}
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
@@ -508,7 +515,8 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
memtable_write_group.status = WriteBatchInternal::InsertInto(
|
||||
memtable_write_group, w.sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*log_number*/, this, seq_per_batch_);
|
||||
0 /*log_number*/, this, false /*concurrent_memtable_writes*/,
|
||||
seq_per_batch_, batch_per_txn_);
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
write_thread_.ExitAsMemTableWriter(&w, memtable_write_group);
|
||||
}
|
||||
@@ -565,7 +573,6 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
}
|
||||
// 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);
|
||||
@@ -1139,10 +1146,14 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
uint64_t delay = write_controller_.GetDelay(env_, num_bytes);
|
||||
if (delay > 0) {
|
||||
if (write_options.no_slowdown) {
|
||||
return Status::Incomplete();
|
||||
return Status::Incomplete("Write stall");
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
|
||||
|
||||
// Notify write_thread_ about the stall so it can setup a barrier and
|
||||
// fail any pending writers with no_slowdown
|
||||
write_thread_.BeginWriteStall();
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:BeginWriteStallDone");
|
||||
mutex_.Unlock();
|
||||
// We will delay the write until we have slept for delay ms or
|
||||
// we don't need a delay anymore
|
||||
@@ -1159,15 +1170,21 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
env_->SleepForMicroseconds(kDelayInterval);
|
||||
}
|
||||
mutex_.Lock();
|
||||
write_thread_.EndWriteStall();
|
||||
}
|
||||
|
||||
while (!error_handler_.IsDBStopped() && write_controller_.IsStopped()) {
|
||||
if (write_options.no_slowdown) {
|
||||
return Status::Incomplete();
|
||||
return Status::Incomplete("Write stall");
|
||||
}
|
||||
delayed = true;
|
||||
|
||||
// Notify write_thread_ about the stall so it can setup a barrier and
|
||||
// fail any pending writers with no_slowdown
|
||||
write_thread_.BeginWriteStall();
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Wait");
|
||||
bg_cv_.Wait();
|
||||
write_thread_.EndWriteStall();
|
||||
}
|
||||
}
|
||||
assert(!delayed || !write_options.no_slowdown);
|
||||
|
||||
+34
-13
@@ -27,6 +27,7 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/trace_replay.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -114,7 +115,8 @@ class DBIter final: public Iterator {
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, bool allow_blob)
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob)
|
||||
: arena_mode_(arena_mode),
|
||||
env_(_env),
|
||||
logger_(cf_options.info_log),
|
||||
@@ -135,6 +137,8 @@ class DBIter final: public Iterator {
|
||||
range_del_agg_(cf_options.internal_comparator, s,
|
||||
true /* collapse_deletions */),
|
||||
read_callback_(read_callback),
|
||||
db_impl_(db_impl),
|
||||
cfd_(cfd),
|
||||
allow_blob_(allow_blob),
|
||||
is_blob_(false),
|
||||
start_seqnum_(read_options.iter_start_seqnum) {
|
||||
@@ -344,6 +348,8 @@ class DBIter final: public Iterator {
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
ReadCallback* read_callback_;
|
||||
DBImpl* db_impl_;
|
||||
ColumnFamilyData* cfd_;
|
||||
bool allow_blob_;
|
||||
bool is_blob_;
|
||||
// for diff snapshots we want the lower bound on the seqnum;
|
||||
@@ -1267,6 +1273,12 @@ void DBIter::Seek(const Slice& target) {
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(target, seq);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
db_impl_->TraceIteratorSeek(cfd_->GetID(), target);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
if (iterate_lower_bound_ != nullptr &&
|
||||
user_comparator_->Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) < 0) {
|
||||
@@ -1331,6 +1343,12 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
range_del_agg_.InvalidateRangeDelMapPositions();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
db_impl_->TraceIteratorSeekForPrev(cfd_->GetID(), target);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
if (prefix_extractor_ && prefix_same_as_start_) {
|
||||
@@ -1453,11 +1471,12 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, bool allow_blob) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
user_key_comparator, internal_iter, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, allow_blob);
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, read_options, cf_options, mutable_cf_options, user_key_comparator,
|
||||
internal_iter, sequence, false, max_sequential_skip_in_iterations,
|
||||
read_callback, db_impl, cfd, allow_blob);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
@@ -1504,13 +1523,14 @@ void ArenaWrappedDBIter::Init(Env* env, const ReadOptions& read_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number,
|
||||
ReadCallback* read_callback, bool allow_blob,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem)
|
||||
DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
cf_options.user_comparator, nullptr, sequence, true,
|
||||
max_sequential_skip_in_iteration, read_callback, allow_blob);
|
||||
db_iter_ = new (mem) DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
cf_options.user_comparator, nullptr, sequence,
|
||||
true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, allow_blob);
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
}
|
||||
@@ -1534,7 +1554,8 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_->mutex());
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
latest_seq, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, allow_blob_, allow_refresh_);
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, allow_blob_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator());
|
||||
@@ -1556,7 +1577,7 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, cf_options, mutable_cf_options, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
allow_blob, allow_refresh);
|
||||
db_impl, cfd, allow_blob, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd, read_callback,
|
||||
allow_blob);
|
||||
|
||||
+10
-11
@@ -23,20 +23,18 @@ namespace rocksdb {
|
||||
|
||||
class Arena;
|
||||
class DBIter;
|
||||
class InternalIterator;
|
||||
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified "sequence" number
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob = false);
|
||||
extern Iterator* NewDBIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false);
|
||||
|
||||
// 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
|
||||
@@ -75,7 +73,8 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, bool allow_blob, bool allow_refresh);
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh);
|
||||
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, ReadCallback* read_callback,
|
||||
|
||||
@@ -2093,6 +2093,34 @@ TEST_P(DBIteratorTest, Refresh) {
|
||||
iter.reset();
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, RefreshWithSnapshot) {
|
||||
ASSERT_OK(Put("x", "y"));
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ReadOptions options;
|
||||
options.snapshot = snapshot;
|
||||
Iterator* iter = NewIterator(options);
|
||||
|
||||
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());
|
||||
|
||||
Status s;
|
||||
s = iter->Refresh();
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, CreationFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
|
||||
|
||||
+20
-19
@@ -180,17 +180,16 @@ void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
ResetTableProperties(tp);
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64
|
||||
" # range deletions %" SCNu64
|
||||
" raw key size %" SCNu64
|
||||
" # range deletions %" SCNu64 " raw key size %" SCNu64
|
||||
" raw average key size %lf "
|
||||
" raw value size %" SCNu64
|
||||
" raw average value size %lf "
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
") %" SCNu64 " filter block size %" SCNu64,
|
||||
", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_range_deletions,
|
||||
&tp->raw_key_size, &dummy_double, &tp->raw_value_size, &dummy_double,
|
||||
&tp->data_size, &tp->index_key_is_user_key, &tp->index_size,
|
||||
&tp->filter_size);
|
||||
&tp->data_size, &tp->index_key_is_user_key,
|
||||
&tp->index_value_is_delta_encoded, &tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
@@ -224,14 +223,11 @@ void VerifyTableProperties(const TableProperties& base_tp,
|
||||
ASSERT_EQ(base_tp.num_range_deletions, new_tp.num_range_deletions);
|
||||
}
|
||||
|
||||
void GetExpectedTableProperties(TableProperties* expected_tp,
|
||||
const int kKeySize, const int kValueSize,
|
||||
const int kKeysPerTable,
|
||||
const int kRangeDeletionsPerTable,
|
||||
const int kTableCount,
|
||||
const int kBloomBitsPerKey,
|
||||
const size_t kBlockSize,
|
||||
const bool index_key_is_user_key) {
|
||||
void GetExpectedTableProperties(
|
||||
TableProperties* expected_tp, const int kKeySize, const int kValueSize,
|
||||
const int kKeysPerTable, const int kRangeDeletionsPerTable,
|
||||
const int kTableCount, const int kBloomBitsPerKey, const size_t kBlockSize,
|
||||
const bool index_key_is_user_key, const bool value_delta_encoding) {
|
||||
const int kKeyCount = kTableCount * kKeysPerTable;
|
||||
const int kRangeDeletionCount = kTableCount * kRangeDeletionsPerTable;
|
||||
const int kAvgSuccessorSize = kKeySize / 5;
|
||||
@@ -248,7 +244,9 @@ void GetExpectedTableProperties(TableProperties* expected_tp,
|
||||
kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
|
||||
expected_tp->index_size =
|
||||
expected_tp->num_data_blocks *
|
||||
(kAvgSuccessorSize + (index_key_is_user_key ? 0 : 8));
|
||||
(kAvgSuccessorSize + (index_key_is_user_key ? 0 : 8) -
|
||||
// discount 1 byte as value size is not encoded in value delta encoding
|
||||
(value_delta_encoding ? 1 : 0));
|
||||
expected_tp->filter_size =
|
||||
kTableCount * (kKeysPerTable * kBloomBitsPerKey / 8);
|
||||
}
|
||||
@@ -342,12 +340,14 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
TableProperties output_tp;
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
bool index_key_is_user_key = output_tp.index_key_is_user_key > 0;
|
||||
bool value_is_delta_encoded = output_tp.index_value_is_delta_encoded > 0;
|
||||
|
||||
TableProperties expected_tp;
|
||||
GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
|
||||
kKeysPerTable, kRangeDeletionsPerTable,
|
||||
kTableCount, kBloomBitsPerKey,
|
||||
table_options.block_size, index_key_is_user_key);
|
||||
table_options.block_size, index_key_is_user_key,
|
||||
value_is_delta_encoded);
|
||||
|
||||
VerifyTableProperties(expected_tp, output_tp);
|
||||
}
|
||||
@@ -533,6 +533,7 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &tp_string);
|
||||
ParseTablePropertiesString(tp_string, &tp);
|
||||
bool index_key_is_user_key = tp.index_key_is_user_key > 0;
|
||||
bool value_is_delta_encoded = tp.index_value_is_delta_encoded > 0;
|
||||
ASSERT_EQ(sum_tp.data_size, tp.data_size);
|
||||
ASSERT_EQ(sum_tp.index_size, tp.index_size);
|
||||
ASSERT_EQ(sum_tp.filter_size, tp.filter_size);
|
||||
@@ -542,10 +543,10 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
ASSERT_EQ(sum_tp.num_entries, tp.num_entries);
|
||||
ASSERT_EQ(sum_tp.num_range_deletions, tp.num_range_deletions);
|
||||
if (table > 3) {
|
||||
GetExpectedTableProperties(
|
||||
&expected_tp, kKeySize, kValueSize, kKeysPerTable,
|
||||
kRangeDeletionsPerTable, table, kBloomBitsPerKey,
|
||||
table_options.block_size, index_key_is_user_key);
|
||||
GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
|
||||
kKeysPerTable, kRangeDeletionsPerTable, table,
|
||||
kBloomBitsPerKey, table_options.block_size,
|
||||
index_key_is_user_key, value_is_delta_encoded);
|
||||
// Gives larger bias here as index block size, filter block size,
|
||||
// and data block size become much harder to estimate in this test.
|
||||
VerifyTableProperties(expected_tp, tp, 0.5, 0.4, 0.4, 0.25);
|
||||
|
||||
@@ -191,7 +191,7 @@ TEST_F(DBRangeDelTest, SentinelsOmittedFromOutputFile) {
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_GT(files[0][0].smallest_seqno, 0);
|
||||
ASSERT_GT(files[0][0].fd.smallest_seqno, 0);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
+9
-24
@@ -436,7 +436,7 @@ TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
// deleted from first db_path were deleted using DeleteScheduler and
|
||||
// files in the second path were not.
|
||||
TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
|
||||
int bg_delete_file = 0;
|
||||
std::atomic<int> bg_delete_file(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::DeleteTrashFile:DeleteFile",
|
||||
[&](void* /*arg*/) { bg_delete_file++; });
|
||||
@@ -703,26 +703,19 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
// When bg_error_ is set we will verify that the DB size is greater
|
||||
// than the limit.
|
||||
|
||||
std::vector<int> max_space_limits_mbs = {1, 2, 4, 8, 10};
|
||||
decltype(max_space_limits_mbs)::value_type limit_mb_cb;
|
||||
bool bg_error_set = false;
|
||||
uint64_t total_sst_files_size = 0;
|
||||
std::vector<int> max_space_limits_mbs = {1, 10};
|
||||
std::atomic<bool> bg_error_set(false);
|
||||
|
||||
std::atomic<int> estimate_multiplier(1);
|
||||
int reached_max_space_on_flush = 0;
|
||||
int reached_max_space_on_compaction = 0;
|
||||
std::atomic<int> reached_max_space_on_flush(0);
|
||||
std::atomic<int> reached_max_space_on_compaction(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached",
|
||||
[&](void* arg) {
|
||||
Status* bg_error = static_cast<Status*>(arg);
|
||||
bg_error_set = true;
|
||||
GetAllSSTFiles(&total_sst_files_size);
|
||||
reached_max_space_on_flush++;
|
||||
// low limit for size calculated using sst files
|
||||
ASSERT_GE(total_sst_files_size, limit_mb_cb * 1024 * 1024);
|
||||
// clear error to ensure compaction callback is called
|
||||
*bg_error = Status::OK();
|
||||
estimate_multiplier++; // used in the main loop assert
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -735,15 +728,11 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
"CompactionJob::FinishCompactionOutputFile:MaxAllowedSpaceReached",
|
||||
[&](void* /*arg*/) {
|
||||
bg_error_set = true;
|
||||
GetAllSSTFiles(&total_sst_files_size);
|
||||
reached_max_space_on_compaction++;
|
||||
});
|
||||
|
||||
for (auto limit_mb : max_space_limits_mbs) {
|
||||
bg_error_set = false;
|
||||
total_sst_files_size = 0;
|
||||
estimate_multiplier = 1;
|
||||
limit_mb_cb = limit_mb;
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
@@ -757,21 +746,17 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
|
||||
sfm->SetMaxAllowedSpaceUsage(limit_mb * 1024 * 1024);
|
||||
|
||||
int keys_written = 0;
|
||||
uint64_t estimated_db_size = 0;
|
||||
// It is easy to detect if the test is stuck in a loop. No need for
|
||||
// complex termination logic.
|
||||
while (true) {
|
||||
auto s = Put(RandomString(&rnd, 10), RandomString(&rnd, 50));
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
keys_written++;
|
||||
// Check the estimated db size vs the db limit just to make sure we
|
||||
// dont run into an infinite loop
|
||||
estimated_db_size = keys_written * 60; // ~60 bytes per key
|
||||
ASSERT_LT(estimated_db_size,
|
||||
estimate_multiplier * limit_mb * 1024 * 1024 * 2);
|
||||
}
|
||||
ASSERT_TRUE(bg_error_set);
|
||||
uint64_t total_sst_files_size = 0;
|
||||
GetAllSSTFiles(&total_sst_files_size);
|
||||
ASSERT_GE(total_sst_files_size, limit_mb * 1024 * 1024);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
+202
-8
@@ -262,6 +262,196 @@ TEST_F(DBTest, SkipDelay) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MixedSlowdownOptions) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<port::Thread> threads;
|
||||
std::atomic<int> thread_num(0);
|
||||
|
||||
std::function<void()> write_slowdown_func = [&]() {
|
||||
int a = thread_num.fetch_add(1);
|
||||
std::string key = "foo" + std::to_string(a);
|
||||
WriteOptions wo;
|
||||
wo.no_slowdown = false;
|
||||
ASSERT_OK(dbfull()->Put(wo, key, "bar"));
|
||||
};
|
||||
std::function<void()> write_no_slowdown_func = [&]() {
|
||||
int a = thread_num.fetch_add(1);
|
||||
std::string key = "foo" + std::to_string(a);
|
||||
WriteOptions wo;
|
||||
wo.no_slowdown = true;
|
||||
ASSERT_NOK(dbfull()->Put(wo, key, "bar"));
|
||||
};
|
||||
// Use a small number to ensure a large delay that is still effective
|
||||
// when we do Put
|
||||
// TODO(myabandeh): this is time dependent and could potentially make
|
||||
// the test flaky
|
||||
auto token = dbfull()->TEST_write_controler().GetDelayToken(1);
|
||||
std::atomic<int> sleep_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:BeginWriteStallDone",
|
||||
[&](void* /*arg*/) {
|
||||
sleep_count.fetch_add(1);
|
||||
if (threads.empty()) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_slowdown_func);
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_no_slowdown_func);
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = false;
|
||||
wo.disableWAL = false;
|
||||
wo.no_slowdown = false;
|
||||
dbfull()->Put(wo, "foo", "bar");
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
token.reset();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
ASSERT_GE(sleep_count.load(), 1);
|
||||
|
||||
wo.no_slowdown = true;
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo3", "bar"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MixedSlowdownOptionsInQueue) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<port::Thread> threads;
|
||||
std::atomic<int> thread_num(0);
|
||||
|
||||
std::function<void()> write_no_slowdown_func = [&]() {
|
||||
int a = thread_num.fetch_add(1);
|
||||
std::string key = "foo" + std::to_string(a);
|
||||
WriteOptions wo;
|
||||
wo.no_slowdown = true;
|
||||
ASSERT_NOK(dbfull()->Put(wo, key, "bar"));
|
||||
};
|
||||
// Use a small number to ensure a large delay that is still effective
|
||||
// when we do Put
|
||||
// TODO(myabandeh): this is time dependent and could potentially make
|
||||
// the test flaky
|
||||
auto token = dbfull()->TEST_write_controler().GetDelayToken(1);
|
||||
std::atomic<int> sleep_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Sleep",
|
||||
[&](void* /*arg*/) {
|
||||
sleep_count.fetch_add(1);
|
||||
if (threads.empty()) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_no_slowdown_func);
|
||||
}
|
||||
// Sleep for 2s to allow the threads to insert themselves into the
|
||||
// write queue
|
||||
env_->SleepForMicroseconds(3000000ULL);
|
||||
}
|
||||
});
|
||||
std::atomic<int> wait_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Wait",
|
||||
[&](void* /*arg*/) { wait_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = false;
|
||||
wo.disableWAL = false;
|
||||
wo.no_slowdown = false;
|
||||
dbfull()->Put(wo, "foo", "bar");
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
token.reset();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
ASSERT_EQ(sleep_count.load(), 1);
|
||||
ASSERT_GE(wait_count.load(), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MixedSlowdownOptionsStop) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<port::Thread> threads;
|
||||
std::atomic<int> thread_num(0);
|
||||
|
||||
std::function<void()> write_slowdown_func = [&]() {
|
||||
int a = thread_num.fetch_add(1);
|
||||
std::string key = "foo" + std::to_string(a);
|
||||
WriteOptions wo;
|
||||
wo.no_slowdown = false;
|
||||
ASSERT_OK(dbfull()->Put(wo, key, "bar"));
|
||||
};
|
||||
std::function<void()> write_no_slowdown_func = [&]() {
|
||||
int a = thread_num.fetch_add(1);
|
||||
std::string key = "foo" + std::to_string(a);
|
||||
WriteOptions wo;
|
||||
wo.no_slowdown = true;
|
||||
ASSERT_NOK(dbfull()->Put(wo, key, "bar"));
|
||||
};
|
||||
std::function<void()> wakeup_writer = [&]() {
|
||||
dbfull()->mutex_.Lock();
|
||||
dbfull()->bg_cv_.SignalAll();
|
||||
dbfull()->mutex_.Unlock();
|
||||
};
|
||||
// Use a small number to ensure a large delay that is still effective
|
||||
// when we do Put
|
||||
// TODO(myabandeh): this is time dependent and could potentially make
|
||||
// the test flaky
|
||||
auto token = dbfull()->TEST_write_controler().GetStopToken();
|
||||
std::atomic<int> wait_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Wait",
|
||||
[&](void* /*arg*/) {
|
||||
wait_count.fetch_add(1);
|
||||
if (threads.empty()) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_slowdown_func);
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_no_slowdown_func);
|
||||
}
|
||||
// Sleep for 2s to allow the threads to insert themselves into the
|
||||
// write queue
|
||||
env_->SleepForMicroseconds(3000000ULL);
|
||||
}
|
||||
token.reset();
|
||||
threads.emplace_back(wakeup_writer);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = false;
|
||||
wo.disableWAL = false;
|
||||
wo.no_slowdown = false;
|
||||
dbfull()->Put(wo, "foo", "bar");
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
token.reset();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
ASSERT_GE(wait_count.load(), 1);
|
||||
|
||||
wo.no_slowdown = true;
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo3", "bar"));
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, LevelLimitReopen) {
|
||||
@@ -2149,6 +2339,9 @@ static void GCThreadBody(void* arg) {
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifndef TRAVIS
|
||||
// Disable this test temporarily on Travis as it fails intermittently.
|
||||
// Github issue: #4151
|
||||
TEST_F(DBTest, GroupCommitTest) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2195,6 +2388,7 @@ TEST_F(DBTest, GroupCommitTest) {
|
||||
ASSERT_GT(hist_data.average, 0.0);
|
||||
} while (ChangeOptions(kSkipNoSeekToLast));
|
||||
}
|
||||
#endif // TRAVIS
|
||||
|
||||
namespace {
|
||||
typedef std::map<std::string, std::string> KVMap;
|
||||
@@ -4327,7 +4521,7 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
// Clean up memtable and L0. Block compaction threads. If continue to write
|
||||
// and flush memtables. We should see put stop after 8 memtable flushes
|
||||
// since level0_stop_writes_trigger = 8
|
||||
dbfull()->TEST_FlushMemTable(true);
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
// Block compaction
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
@@ -4340,7 +4534,7 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
WriteOptions wo;
|
||||
while (count < 64) {
|
||||
ASSERT_OK(Put(Key(count), RandomString(&rnd, 1024), wo));
|
||||
dbfull()->TEST_FlushMemTable(true);
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
count++;
|
||||
if (dbfull()->TEST_write_controler().IsStopped()) {
|
||||
sleeping_task_low.WakeUp();
|
||||
@@ -4368,7 +4562,7 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
count = 0;
|
||||
while (count < 64) {
|
||||
ASSERT_OK(Put(Key(count), RandomString(&rnd, 1024), wo));
|
||||
dbfull()->TEST_FlushMemTable(true);
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
count++;
|
||||
if (dbfull()->TEST_write_controler().IsStopped()) {
|
||||
sleeping_task_low.WakeUp();
|
||||
@@ -5508,7 +5702,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
for (int i = 0; i < 72; i++) {
|
||||
Put(Key(i), std::string(5000, 'x'));
|
||||
if (i % 10 == 0) {
|
||||
Flush();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -5518,7 +5712,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
for (int i = 0; i < 72; i++) {
|
||||
Put(Key(i), std::string(5000, 'x'));
|
||||
if (i % 10 == 0) {
|
||||
Flush();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -5537,7 +5731,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put(Key(i), std::string(5000, 'x'));
|
||||
Put(Key(100 - i), std::string(5000, 'x'));
|
||||
// Flush the file. File size is around 30KB.
|
||||
Flush();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kDelayed));
|
||||
@@ -5572,7 +5766,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put(Key(10 + i), std::string(5000, 'x'));
|
||||
Put(Key(90 - i), std::string(5000, 'x'));
|
||||
// Flush the file. File size is around 30KB.
|
||||
Flush();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
}
|
||||
|
||||
// Wake up sleep task to enable compaction to run and waits
|
||||
@@ -5593,7 +5787,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put(Key(20 + i), std::string(5000, 'x'));
|
||||
Put(Key(80 - i), std::string(5000, 'x'));
|
||||
// Flush the file. File size is around 30KB.
|
||||
Flush();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
}
|
||||
// Wake up sleep task to enable compaction to run and waits
|
||||
// for it to go to sleep state again to make sure one compaction
|
||||
|
||||
+291
-3
@@ -2321,9 +2321,9 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW);
|
||||
// Include the explicit 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_GE(
|
||||
rate_limited_bytes,
|
||||
static_cast<size_t>(kNumKeysPerFile * kBytesPerKey * kNumL0Files));
|
||||
ASSERT_LT(
|
||||
rate_limited_bytes,
|
||||
static_cast<size_t>(2 * kNumKeysPerFile * kBytesPerKey * kNumL0Files +
|
||||
@@ -2500,6 +2500,108 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceAndReplay) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
ReadOptions ro;
|
||||
WriteOptions wo;
|
||||
TraceOptions trace_opts;
|
||||
EnvOptions env_opts;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
Iterator* single_iter = nullptr;
|
||||
|
||||
std::string trace_filename = dbname_ + "/rocksdb.trace";
|
||||
std::unique_ptr<TraceWriter> trace_writer;
|
||||
ASSERT_OK(NewFileTraceWriter(env_, env_opts, trace_filename, &trace_writer));
|
||||
ASSERT_OK(db_->StartTrace(trace_opts, std::move(trace_writer)));
|
||||
|
||||
ASSERT_OK(Put(0, "a", "1"));
|
||||
ASSERT_OK(Merge(0, "b", "2"));
|
||||
ASSERT_OK(Delete(0, "c"));
|
||||
ASSERT_OK(SingleDelete(0, "d"));
|
||||
ASSERT_OK(db_->DeleteRange(wo, dbfull()->DefaultColumnFamily(), "e", "f"));
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(batch.Put("f", "11"));
|
||||
ASSERT_OK(batch.Merge("g", "12"));
|
||||
ASSERT_OK(batch.Delete("h"));
|
||||
ASSERT_OK(batch.SingleDelete("i"));
|
||||
ASSERT_OK(batch.DeleteRange("j", "k"));
|
||||
ASSERT_OK(db_->Write(wo, &batch));
|
||||
|
||||
single_iter = db_->NewIterator(ro);
|
||||
single_iter->Seek("f");
|
||||
single_iter->SeekForPrev("g");
|
||||
delete single_iter;
|
||||
|
||||
ASSERT_EQ("1", Get(0, "a"));
|
||||
ASSERT_EQ("12", Get(0, "g"));
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "rocksdb", "rocks"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "leveldb"));
|
||||
|
||||
ASSERT_OK(db_->EndTrace());
|
||||
// These should not get into the trace file as it is after EndTrace.
|
||||
Put("hello", "world");
|
||||
Merge("foo", "bar");
|
||||
|
||||
// Open another db, replay, and verify the data
|
||||
std::string value;
|
||||
std::string dbname2 = test::TmpDir(env_) + "/db_replay";
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
|
||||
// Using a different name than db2, to pacify infer's use-after-lifetime
|
||||
// warnings (http://fbinfer.com).
|
||||
DB* db2_init = nullptr;
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(DB::Open(options, dbname2, &db2_init));
|
||||
ColumnFamilyHandle* cf;
|
||||
ASSERT_OK(
|
||||
db2_init->CreateColumnFamily(ColumnFamilyOptions(), "pikachu", &cf));
|
||||
delete cf;
|
||||
delete db2_init;
|
||||
|
||||
DB* db2 = nullptr;
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
column_families.push_back(ColumnFamilyDescriptor("default", cf_options));
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname2, column_families, &handles, &db2));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
// Verify that the keys don't already exist
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "g", &value).IsNotFound());
|
||||
|
||||
std::unique_ptr<TraceReader> trace_reader;
|
||||
ASSERT_OK(NewFileTraceReader(env_, env_opts, trace_filename, &trace_reader));
|
||||
Replayer replayer(db2, handles_, std::move(trace_reader));
|
||||
ASSERT_OK(replayer.Replay());
|
||||
|
||||
ASSERT_OK(db2->Get(ro, handles[0], "a", &value));
|
||||
ASSERT_EQ("1", value);
|
||||
ASSERT_OK(db2->Get(ro, handles[0], "g", &value));
|
||||
ASSERT_EQ("12", value);
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "hello", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "world", &value).IsNotFound());
|
||||
|
||||
ASSERT_OK(db2->Get(ro, handles[1], "foo", &value));
|
||||
ASSERT_EQ("bar", value);
|
||||
ASSERT_OK(db2->Get(ro, handles[1], "rocksdb", &value));
|
||||
ASSERT_EQ("rocks", value);
|
||||
|
||||
for (auto handle : handles) {
|
||||
delete handle;
|
||||
}
|
||||
delete db2;
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, PinnableSliceAndMmapReads) {
|
||||
@@ -2547,6 +2649,192 @@ TEST_F(DBTest2, PinnableSliceAndMmapReads) {
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, DISABLED_IteratorPinnedMemory) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = false;
|
||||
bbto.cache_index_and_filter_blocks = false;
|
||||
bbto.block_cache = NewLRUCache(100000);
|
||||
bbto.block_size = 400; // small block size
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::string v = RandomString(&rnd, 400);
|
||||
|
||||
// Since v is the size of a block, each key should take a block
|
||||
// of 400+ bytes.
|
||||
Put("1", v);
|
||||
Put("3", v);
|
||||
Put("5", v);
|
||||
Put("7", v);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(0, bbto.block_cache->GetPinnedUsage());
|
||||
|
||||
// Verify that iterators don't pin more than one data block in block cache
|
||||
// at each time.
|
||||
{
|
||||
unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->SeekToFirst();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// Block cache should contain exactly one block.
|
||||
ASSERT_GT(bbto.block_cache->GetPinnedUsage(), 0);
|
||||
ASSERT_LT(bbto.block_cache->GetPinnedUsage(), 800);
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Seek("4");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
ASSERT_GT(bbto.block_cache->GetPinnedUsage(), 0);
|
||||
ASSERT_LT(bbto.block_cache->GetPinnedUsage(), 800);
|
||||
|
||||
iter->Seek("3");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
ASSERT_GT(bbto.block_cache->GetPinnedUsage(), 0);
|
||||
ASSERT_LT(bbto.block_cache->GetPinnedUsage(), 800);
|
||||
}
|
||||
ASSERT_EQ(0, bbto.block_cache->GetPinnedUsage());
|
||||
|
||||
// Test compaction case
|
||||
Put("2", v);
|
||||
Put("5", v);
|
||||
Put("6", v);
|
||||
Put("8", v);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Clear existing data in block cache
|
||||
bbto.block_cache->SetCapacity(0);
|
||||
bbto.block_cache->SetCapacity(100000);
|
||||
|
||||
// Verify compaction input iterators don't hold more than one data blocks at
|
||||
// one time.
|
||||
std::atomic<bool> finished(false);
|
||||
std::atomic<int> block_newed(0);
|
||||
std::atomic<int> block_destroyed(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"Block::Block:0", [&](void* /*arg*/) {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
// Two iterators. At most 2 outstanding blocks.
|
||||
EXPECT_GE(block_newed.load(), block_destroyed.load());
|
||||
EXPECT_LE(block_newed.load(), block_destroyed.load() + 1);
|
||||
block_newed.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"Block::~Block", [&](void* /*arg*/) {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
// Two iterators. At most 2 outstanding blocks.
|
||||
EXPECT_GE(block_newed.load(), block_destroyed.load() + 1);
|
||||
EXPECT_LE(block_newed.load(), block_destroyed.load() + 2);
|
||||
block_destroyed.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run:BeforeVerify",
|
||||
[&](void* /*arg*/) { finished = true; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// Two input files. Each of them has 4 data blocks.
|
||||
ASSERT_EQ(8, block_newed.load());
|
||||
ASSERT_EQ(8, block_destroyed.load());
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestBBTTailPrefetch) {
|
||||
std::atomic<bool> called(false);
|
||||
size_t expected_lower_bound = 512 * 1024;
|
||||
size_t expected_higher_bound = 512 * 1024;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::Open::TailPrefetchLen", [&](void* arg) {
|
||||
size_t* prefetch_size = static_cast<size_t*>(arg);
|
||||
EXPECT_LE(expected_lower_bound, *prefetch_size);
|
||||
EXPECT_GE(expected_higher_bound, *prefetch_size);
|
||||
called = true;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put("1", "1");
|
||||
Put("9", "1");
|
||||
Flush();
|
||||
|
||||
expected_lower_bound = 0;
|
||||
expected_higher_bound = 8 * 1024;
|
||||
|
||||
Put("1", "1");
|
||||
Put("9", "1");
|
||||
Flush();
|
||||
|
||||
Put("1", "1");
|
||||
Put("9", "1");
|
||||
Flush();
|
||||
|
||||
// Full compaction to make sure there is no L0 file after the open.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_TRUE(called.load());
|
||||
called = false;
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
std::atomic<bool> first_call(true);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::Open::TailPrefetchLen", [&](void* arg) {
|
||||
size_t* prefetch_size = static_cast<size_t*>(arg);
|
||||
if (first_call) {
|
||||
EXPECT_EQ(4 * 1024, *prefetch_size);
|
||||
first_call = false;
|
||||
} else {
|
||||
EXPECT_GE(4 * 1024, *prefetch_size);
|
||||
}
|
||||
called = true;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.max_file_opening_threads = 1; // one thread
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.max_open_files = -1;
|
||||
Reopen(options);
|
||||
|
||||
Put("1", "1");
|
||||
Put("9", "1");
|
||||
Flush();
|
||||
|
||||
Put("1", "1");
|
||||
Put("9", "1");
|
||||
Flush();
|
||||
|
||||
ASSERT_TRUE(called.load());
|
||||
called = false;
|
||||
|
||||
// Parallel loading SST files
|
||||
options.max_file_opening_threads = 16;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_TRUE(called.load());
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+5
-4
@@ -449,15 +449,16 @@ Options DBTestBase::GetOptions(
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithPartitionedIndexFormat3: {
|
||||
table_options.format_version = 3;
|
||||
// Format 3 changes the binary index format. Since partitioned index is a
|
||||
case kBlockBasedTableWithPartitionedIndexFormat4: {
|
||||
table_options.format_version = 4;
|
||||
// Format 4 changes the binary index format. Since partitioned index is a
|
||||
// super-set of simple indexes, we are also using kTwoLevelIndexSearch to
|
||||
// test this format.
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
// The top-level index in partition filters are also affected by format 3.
|
||||
// The top-level index in partition filters are also affected by format 4.
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_block_restart_interval = 8;
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithIndexRestartInterval: {
|
||||
|
||||
+1
-3
@@ -109,8 +109,6 @@ struct OptionsOverride {
|
||||
// These will be used only if filter_policy is set
|
||||
bool partition_filters = false;
|
||||
uint64_t metadata_block_size = 1024;
|
||||
BlockBasedTableOptions::IndexType index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch;
|
||||
|
||||
// Used as a bit mask of individual enums in which to skip an XF test point
|
||||
int skip_policy = 0;
|
||||
@@ -698,7 +696,7 @@ class DBTestBase : public testing::Test {
|
||||
kLevelSubcompactions,
|
||||
kBlockBasedTableWithIndexRestartInterval,
|
||||
kBlockBasedTableWithPartitionedIndex,
|
||||
kBlockBasedTableWithPartitionedIndexFormat3,
|
||||
kBlockBasedTableWithPartitionedIndexFormat4,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions,
|
||||
kUniversalSubcompactions,
|
||||
// This must be the last line
|
||||
|
||||
@@ -1824,7 +1824,7 @@ TEST_P(DBTestUniversalCompaction, FinalSortedRunCompactFilesConflict) {
|
||||
|
||||
port::Thread compact_files_thread([&]() {
|
||||
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), default_cfh,
|
||||
{first_sst_filename}, num_levels_ - 1));
|
||||
{first_sst_filename}, num_levels_ - 1));
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
|
||||
@@ -48,6 +48,8 @@ EntryType GetEntryType(ValueType value_type) {
|
||||
return kEntryMerge;
|
||||
case kTypeRangeDeletion:
|
||||
return kEntryRangeDeletion;
|
||||
case kTypeBlobIndex:
|
||||
return kEntryBlobIndex;
|
||||
default:
|
||||
return kEntryOther;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
namespace rocksdb {
|
||||
|
||||
Status ExternalSstFileIngestionJob::Prepare(
|
||||
const std::vector<std::string>& external_files_paths, SuperVersion* sv) {
|
||||
const std::vector<std::string>& external_files_paths,
|
||||
uint64_t next_file_number, SuperVersion* sv) {
|
||||
Status status;
|
||||
|
||||
// Read the information of files we are ingesting
|
||||
@@ -90,7 +91,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
|
||||
// Copy/Move external files into DB
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
f.fd = FileDescriptor(versions_->NewFileNumber(), 0, f.file_size);
|
||||
f.fd = FileDescriptor(next_file_number++, 0, f.file_size);
|
||||
|
||||
const std::string path_outside_db = f.external_file_path;
|
||||
const std::string path_inside_db =
|
||||
@@ -475,9 +476,9 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
const SequenceNumber level_largest_seqno =
|
||||
(*max_element(level_files.begin(), level_files.end(),
|
||||
[](FileMetaData* f1, FileMetaData* f2) {
|
||||
return f1->largest_seqno < f2->largest_seqno;
|
||||
return f1->fd.largest_seqno < f2->fd.largest_seqno;
|
||||
}))
|
||||
->largest_seqno;
|
||||
->fd.largest_seqno;
|
||||
// should only assign seqno to current level's largest seqno when
|
||||
// the file fits
|
||||
if (level_largest_seqno != 0 &&
|
||||
@@ -522,7 +523,7 @@ Status ExternalSstFileIngestionJob::CheckLevelForIngestedBehindFile(
|
||||
// at some upper level
|
||||
for (int lvl = 0; lvl < cfd_->NumberLevels() - 1; lvl++) {
|
||||
for (auto file : vstorage->LevelFiles(lvl)) {
|
||||
if (file->smallest_seqno == 0) {
|
||||
if (file->fd.smallest_seqno == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file as despite allow_ingest_behind=true "
|
||||
"there are files with 0 seqno in database at upper levels!");
|
||||
@@ -547,24 +548,27 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
"field");
|
||||
}
|
||||
|
||||
std::unique_ptr<RandomRWFile> rwfile;
|
||||
Status status = env_->NewRandomRWFile(file_to_ingest->internal_file_path,
|
||||
&rwfile, env_options_);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
if (ingestion_options_.write_global_seqno) {
|
||||
// Determine if we can write global_seqno to a given offset of file.
|
||||
// If the file system does not support random write, then we should not.
|
||||
// Otherwise we should.
|
||||
std::unique_ptr<RandomRWFile> rwfile;
|
||||
Status status = env_->NewRandomRWFile(file_to_ingest->internal_file_path,
|
||||
&rwfile, env_options_);
|
||||
if (status.ok()) {
|
||||
std::string seqno_val;
|
||||
PutFixed64(&seqno_val, seqno);
|
||||
status = rwfile->Write(file_to_ingest->global_seqno_offset, seqno_val);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
} else if (!status.IsNotSupported()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the new seqno in the global sequence number field in the file
|
||||
std::string seqno_val;
|
||||
PutFixed64(&seqno_val, seqno);
|
||||
status = rwfile->Write(file_to_ingest->global_seqno_offset, seqno_val);
|
||||
if (status.ok()) {
|
||||
status = rwfile->Fsync();
|
||||
}
|
||||
if (status.ok()) {
|
||||
file_to_ingest->assigned_seqno = seqno;
|
||||
}
|
||||
return status;
|
||||
file_to_ingest->assigned_seqno = seqno;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
|
||||
|
||||
@@ -89,7 +89,7 @@ class ExternalSstFileIngestionJob {
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths,
|
||||
SuperVersion* sv);
|
||||
uint64_t next_file_number, SuperVersion* sv);
|
||||
|
||||
// Check if we need to flush the memtable before running the ingestion job
|
||||
// This will be true if the files we are ingesting are overlapping with any
|
||||
|
||||
+1
-1
@@ -389,7 +389,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Add file to L0
|
||||
edit_->AddFile(0 /* level */, meta_.fd.GetNumber(), meta_.fd.GetPathId(),
|
||||
meta_.fd.GetFileSize(), meta_.smallest, meta_.largest,
|
||||
meta_.smallest_seqno, meta_.largest_seqno,
|
||||
meta_.fd.smallest_seqno, meta_.fd.largest_seqno,
|
||||
meta_.marked_for_compaction);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,19 +147,20 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
|
||||
HistogramData hist;
|
||||
FileMetaData fd;
|
||||
FileMetaData file_meta;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(nullptr, &fd));
|
||||
ASSERT_OK(flush_job.Run(nullptr, &file_meta));
|
||||
mutex_.Unlock();
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
|
||||
ASSERT_EQ(ToString(0), fd.smallest.user_key().ToString());
|
||||
ASSERT_EQ("9999a",
|
||||
fd.largest.user_key().ToString()); // range tombstone end key
|
||||
ASSERT_EQ(1, fd.smallest_seqno);
|
||||
ASSERT_EQ(10000, fd.largest_seqno); // range tombstone seqnum 10000
|
||||
ASSERT_EQ(ToString(0), file_meta.smallest.user_key().ToString());
|
||||
ASSERT_EQ(
|
||||
"9999a",
|
||||
file_meta.largest.user_key().ToString()); // range tombstone end key
|
||||
ASSERT_EQ(1, file_meta.fd.smallest_seqno);
|
||||
ASSERT_EQ(10000, file_meta.fd.largest_seqno); // range tombstone seqnum 10000
|
||||
mock_table_factory_->AssertSingleFile(inserted_keys);
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
+24
-5
@@ -35,6 +35,14 @@ struct SuperVersionContext {
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
||||
|
||||
explicit SuperVersionContext(SuperVersionContext&& other)
|
||||
: superversions_to_free(std::move(other.superversions_to_free)),
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
write_stall_notifications(std::move(other.write_stall_notifications)),
|
||||
#endif
|
||||
new_superversion(std::move(other.new_superversion)) {
|
||||
}
|
||||
|
||||
void NewSuperVersion() {
|
||||
new_superversion = unique_ptr<SuperVersion>(new SuperVersion());
|
||||
}
|
||||
@@ -98,8 +106,15 @@ struct JobContext {
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToClean() const {
|
||||
bool sv_have_sth = false;
|
||||
for (const auto& sv_ctx : superversion_contexts) {
|
||||
if (sv_ctx.HaveSomethingToDelete()) {
|
||||
sv_have_sth = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
superversion_context.HaveSomethingToDelete();
|
||||
sv_have_sth;
|
||||
}
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
@@ -142,7 +157,8 @@ struct JobContext {
|
||||
// a list of memtables to be free
|
||||
autovector<MemTable*> memtables_to_free;
|
||||
|
||||
SuperVersionContext superversion_context;
|
||||
// contexts for installing superversions for multiple column families
|
||||
std::vector<SuperVersionContext> superversion_contexts;
|
||||
|
||||
autovector<log::Writer*> logs_to_free;
|
||||
|
||||
@@ -158,13 +174,14 @@ struct JobContext {
|
||||
size_t num_alive_log_files = 0;
|
||||
uint64_t size_log_to_delete = 0;
|
||||
|
||||
explicit JobContext(int _job_id, bool create_superversion = false)
|
||||
: superversion_context(create_superversion) {
|
||||
explicit JobContext(int _job_id, bool create_superversion = false) {
|
||||
job_id = _job_id;
|
||||
manifest_file_number = 0;
|
||||
pending_manifest_file_number = 0;
|
||||
log_number = 0;
|
||||
prev_log_number = 0;
|
||||
superversion_contexts.emplace_back(
|
||||
SuperVersionContext(create_superversion));
|
||||
}
|
||||
|
||||
// For non-empty JobContext Clean() has to be called at least once before
|
||||
@@ -173,7 +190,9 @@ struct JobContext {
|
||||
// doing potentially slow Clean() with locked DB mutex.
|
||||
void Clean() {
|
||||
// free superversions
|
||||
superversion_context.Clean();
|
||||
for (auto& sv_context : superversion_contexts) {
|
||||
sv_context.Clean();
|
||||
}
|
||||
// free pending memtables
|
||||
for (auto m : memtables_to_free) {
|
||||
delete m;
|
||||
|
||||
+3
-1
@@ -417,7 +417,9 @@ TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
for (int i = 0; static_cast<int>(cf_meta.file_count) < kSlowdownTrigger * 10;
|
||||
++i) {
|
||||
Put(1, ToString(i), std::string(10000, 'x'), WriteOptions());
|
||||
db_->Flush(FlushOptions(), handles_[1]);
|
||||
FlushOptions fo;
|
||||
fo.allow_write_stall = true;
|
||||
db_->Flush(fo, handles_[1]);
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
}
|
||||
ASSERT_GE(listener->slowdown_count, kSlowdownTrigger * 9);
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace rocksdb {
|
||||
class Mutex;
|
||||
class MemTableIterator;
|
||||
class MergeContext;
|
||||
class InternalIterator;
|
||||
|
||||
struct ImmutableMemTableOptions {
|
||||
explicit ImmutableMemTableOptions(const ImmutableCFOptions& ioptions,
|
||||
|
||||
@@ -26,7 +26,6 @@ class Iterator;
|
||||
class Logger;
|
||||
class MergeOperator;
|
||||
class Statistics;
|
||||
class InternalIterator;
|
||||
|
||||
class MergeHelper {
|
||||
public:
|
||||
|
||||
@@ -227,16 +227,24 @@ TEST_F(ObsoleteFilesTest, DeleteObsoleteOptionsFile) {
|
||||
}
|
||||
ASSERT_OK(dbi->EnableFileDeletions(true /* force */));
|
||||
ASSERT_EQ(optsfiles_nums.size(), optsfiles_keep.size());
|
||||
int size = static_cast<int>(optsfiles_nums.size());
|
||||
int kept_opts_files_count = 0;
|
||||
for (int i = 0; i != size; ++i) {
|
||||
if (optsfiles_keep[i]) {
|
||||
++kept_opts_files_count;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(2, kept_opts_files_count);
|
||||
|
||||
CloseDB();
|
||||
|
||||
std::vector<std::string> files;
|
||||
int opts_file_count = 0;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &files));
|
||||
for (const auto& file : files) {
|
||||
uint64_t file_num;
|
||||
Slice dummy_info_log_name_prefix;
|
||||
FileType type;
|
||||
WalFileType log_type;
|
||||
if (ParseFileName(file, &file_num, dummy_info_log_name_prefix, &type,
|
||||
&log_type) &&
|
||||
type == kOptionsFile) {
|
||||
opts_file_count++;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(2, opts_file_count);
|
||||
}
|
||||
|
||||
} //namespace rocksdb
|
||||
|
||||
@@ -493,7 +493,8 @@ Status RangeDelAggregator::AddTombstones(
|
||||
tombstone.end_key_ = largest->user_key();
|
||||
}
|
||||
}
|
||||
GetRangeDelMap(tombstone.seq_).AddTombstone(std::move(tombstone));
|
||||
auto seq = tombstone.seq_;
|
||||
GetRangeDelMap(seq).AddTombstone(std::move(tombstone));
|
||||
input->Next();
|
||||
}
|
||||
if (!first_iter) {
|
||||
|
||||
+3
-3
@@ -120,7 +120,7 @@ Status TableCache::GetTableReader(
|
||||
s = ioptions_.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions_, prefix_extractor, env_options,
|
||||
internal_comparator, skip_filters, immortal_tables_,
|
||||
level),
|
||||
level, fd.largest_seqno),
|
||||
std::move(file_reader), fd.GetFileSize(), table_reader,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
TEST_SYNC_POINT("TableCache::GetTableReader:0");
|
||||
@@ -238,7 +238,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
if (s.ok()) {
|
||||
if (options.table_filter &&
|
||||
!options.table_filter(*table_reader->GetTableProperties())) {
|
||||
result = NewEmptyInternalIterator(arena);
|
||||
result = NewEmptyInternalIterator<Slice>(arena);
|
||||
} else {
|
||||
result = table_reader->NewIterator(options, prefix_extractor, arena,
|
||||
skip_filters, for_compaction);
|
||||
@@ -279,7 +279,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(result == nullptr);
|
||||
result = NewErrorInternalIterator(s, arena);
|
||||
result = NewErrorInternalIterator<Slice>(s, arena);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ class Arena;
|
||||
struct FileDescriptor;
|
||||
class GetContext;
|
||||
class HistogramImpl;
|
||||
class InternalIterator;
|
||||
|
||||
class TableCache {
|
||||
public:
|
||||
|
||||
+19
-17
@@ -35,11 +35,11 @@
|
||||
namespace rocksdb {
|
||||
|
||||
bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b) {
|
||||
if (a->largest_seqno != b->largest_seqno) {
|
||||
return a->largest_seqno > b->largest_seqno;
|
||||
if (a->fd.largest_seqno != b->fd.largest_seqno) {
|
||||
return a->fd.largest_seqno > b->fd.largest_seqno;
|
||||
}
|
||||
if (a->smallest_seqno != b->smallest_seqno) {
|
||||
return a->smallest_seqno > b->smallest_seqno;
|
||||
if (a->fd.smallest_seqno != b->fd.smallest_seqno) {
|
||||
return a->fd.smallest_seqno > b->fd.smallest_seqno;
|
||||
}
|
||||
// Break ties by file number
|
||||
return a->fd.GetNumber() > b->fd.GetNumber();
|
||||
@@ -162,22 +162,24 @@ class VersionBuilder::Rep {
|
||||
abort();
|
||||
}
|
||||
|
||||
if (f2->smallest_seqno == f2->largest_seqno) {
|
||||
if (f2->fd.smallest_seqno == f2->fd.largest_seqno) {
|
||||
// This is an external file that we ingested
|
||||
SequenceNumber external_file_seqno = f2->smallest_seqno;
|
||||
if (!(external_file_seqno < f1->largest_seqno ||
|
||||
SequenceNumber external_file_seqno = f2->fd.smallest_seqno;
|
||||
if (!(external_file_seqno < f1->fd.largest_seqno ||
|
||||
external_file_seqno == 0)) {
|
||||
fprintf(stderr, "L0 file with seqno %" PRIu64 " %" PRIu64
|
||||
" vs. file with global_seqno %" PRIu64 "\n",
|
||||
f1->smallest_seqno, f1->largest_seqno,
|
||||
fprintf(stderr,
|
||||
"L0 file with seqno %" PRIu64 " %" PRIu64
|
||||
" vs. file with global_seqno %" PRIu64 "\n",
|
||||
f1->fd.smallest_seqno, f1->fd.largest_seqno,
|
||||
external_file_seqno);
|
||||
abort();
|
||||
}
|
||||
} else if (f1->smallest_seqno <= f2->smallest_seqno) {
|
||||
fprintf(stderr, "L0 files seqno %" PRIu64 " %" PRIu64
|
||||
" vs. %" PRIu64 " %" PRIu64 "\n",
|
||||
f1->smallest_seqno, f1->largest_seqno, f2->smallest_seqno,
|
||||
f2->largest_seqno);
|
||||
} else if (f1->fd.smallest_seqno <= f2->fd.smallest_seqno) {
|
||||
fprintf(stderr,
|
||||
"L0 files seqno %" PRIu64 " %" PRIu64 " vs. %" PRIu64
|
||||
" %" PRIu64 "\n",
|
||||
f1->fd.smallest_seqno, f1->fd.largest_seqno,
|
||||
f2->fd.smallest_seqno, f2->fd.largest_seqno);
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
@@ -382,7 +384,7 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
|
||||
std::atomic<size_t> next_file_meta_idx(0);
|
||||
std::function<void()> load_handlers_func = [&]() {
|
||||
std::function<void()> load_handlers_func([&]() {
|
||||
while (true) {
|
||||
size_t file_idx = next_file_meta_idx.fetch_add(1);
|
||||
if (file_idx >= files_meta.size()) {
|
||||
@@ -403,7 +405,7 @@ class VersionBuilder::Rep {
|
||||
file_meta->table_reader_handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
std::vector<port::Thread> threads;
|
||||
for (int i = 1; i < max_threads; i++) {
|
||||
|
||||
@@ -63,8 +63,8 @@ class VersionBuilderTest : public testing::Test {
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
f->smallest = GetInternalKey(smallest, smallest_seq);
|
||||
f->largest = GetInternalKey(largest, largest_seq);
|
||||
f->smallest_seqno = smallest_seqno;
|
||||
f->largest_seqno = largest_seqno;
|
||||
f->fd.smallest_seqno = smallest_seqno;
|
||||
f->fd.largest_seqno = largest_seqno;
|
||||
f->compensated_file_size = file_size;
|
||||
f->refs = 0;
|
||||
f->num_entries = num_entries;
|
||||
|
||||
+47
-12
@@ -40,13 +40,15 @@ enum Tag : uint32_t {
|
||||
kColumnFamilyAdd = 201,
|
||||
kColumnFamilyDrop = 202,
|
||||
kMaxColumnFamily = 203,
|
||||
|
||||
kInAtomicGroup = 300,
|
||||
};
|
||||
|
||||
enum CustomTag : uint32_t {
|
||||
kTerminate = 1, // The end of customized fields
|
||||
kNeedCompaction = 2,
|
||||
// Since Manifest is not entirely currently forward-compatible, and the only
|
||||
// forward-compatbile part is the CutsomtTag of kNewFile, we currently encode
|
||||
// forward-compatible part is the CutsomtTag of kNewFile, we currently encode
|
||||
// kMinLogNumberToKeep as part of a CustomTag as a hack. This should be
|
||||
// removed when manifest becomes forward-comptabile.
|
||||
kMinLogNumberToKeepHack = 3,
|
||||
@@ -83,6 +85,8 @@ void VersionEdit::Clear() {
|
||||
is_column_family_add_ = 0;
|
||||
is_column_family_drop_ = 0;
|
||||
column_family_name_.clear();
|
||||
is_in_atomic_group_ = false;
|
||||
remaining_entries_ = 0;
|
||||
}
|
||||
|
||||
bool VersionEdit::EncodeTo(std::string* dst) const {
|
||||
@@ -135,7 +139,7 @@ bool VersionEdit::EncodeTo(std::string* dst) const {
|
||||
PutVarint64(dst, f.fd.GetFileSize());
|
||||
PutLengthPrefixedSlice(dst, f.smallest.Encode());
|
||||
PutLengthPrefixedSlice(dst, f.largest.Encode());
|
||||
PutVarint64Varint64(dst, f.smallest_seqno, f.largest_seqno);
|
||||
PutVarint64Varint64(dst, f.fd.smallest_seqno, f.fd.largest_seqno);
|
||||
if (has_customized_fields) {
|
||||
// Customized fields' format:
|
||||
// +-----------------------------+
|
||||
@@ -200,6 +204,11 @@ bool VersionEdit::EncodeTo(std::string* dst) const {
|
||||
if (is_column_family_drop_) {
|
||||
PutVarint32(dst, kColumnFamilyDrop);
|
||||
}
|
||||
|
||||
if (is_in_atomic_group_) {
|
||||
PutVarint32(dst, kInAtomicGroup);
|
||||
PutVarint32(dst, remaining_entries_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -233,14 +242,16 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input) {
|
||||
uint64_t number;
|
||||
uint32_t path_id = 0;
|
||||
uint64_t file_size;
|
||||
SequenceNumber smallest_seqno;
|
||||
SequenceNumber largest_seqno;
|
||||
// Since this is the only forward-compatible part of the code, we hack new
|
||||
// extension into this record. When we do, we set this boolean to distinguish
|
||||
// the record from the normal NewFile records.
|
||||
if (GetLevel(input, &level, &msg) && GetVarint64(input, &number) &&
|
||||
GetVarint64(input, &file_size) && GetInternalKey(input, &f.smallest) &&
|
||||
GetInternalKey(input, &f.largest) &&
|
||||
GetVarint64(input, &f.smallest_seqno) &&
|
||||
GetVarint64(input, &f.largest_seqno)) {
|
||||
GetVarint64(input, &smallest_seqno) &&
|
||||
GetVarint64(input, &largest_seqno)) {
|
||||
// See comments in VersionEdit::EncodeTo() for format of customized fields
|
||||
while (true) {
|
||||
uint32_t custom_tag;
|
||||
@@ -272,7 +283,7 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input) {
|
||||
break;
|
||||
case kMinLogNumberToKeepHack:
|
||||
// This is a hack to encode kMinLogNumberToKeep in a
|
||||
// forward-compatbile fashion.
|
||||
// forward-compatible fashion.
|
||||
if (!GetFixed64(&field, &min_log_number_to_keep_)) {
|
||||
return "deleted log number malformatted";
|
||||
}
|
||||
@@ -289,7 +300,8 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input) {
|
||||
} else {
|
||||
return "new-file4 entry";
|
||||
}
|
||||
f.fd = FileDescriptor(number, path_id, file_size);
|
||||
f.fd =
|
||||
FileDescriptor(number, path_id, file_size, smallest_seqno, largest_seqno);
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
return nullptr;
|
||||
}
|
||||
@@ -409,13 +421,16 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
case kNewFile2: {
|
||||
uint64_t number;
|
||||
uint64_t file_size;
|
||||
SequenceNumber smallest_seqno;
|
||||
SequenceNumber largest_seqno;
|
||||
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
|
||||
GetVarint64(&input, &file_size) &&
|
||||
GetInternalKey(&input, &f.smallest) &&
|
||||
GetInternalKey(&input, &f.largest) &&
|
||||
GetVarint64(&input, &f.smallest_seqno) &&
|
||||
GetVarint64(&input, &f.largest_seqno)) {
|
||||
f.fd = FileDescriptor(number, 0, file_size);
|
||||
GetVarint64(&input, &smallest_seqno) &&
|
||||
GetVarint64(&input, &largest_seqno)) {
|
||||
f.fd = FileDescriptor(number, 0, file_size, smallest_seqno,
|
||||
largest_seqno);
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
} else {
|
||||
if (!msg) {
|
||||
@@ -429,13 +444,16 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
uint64_t number;
|
||||
uint32_t path_id;
|
||||
uint64_t file_size;
|
||||
SequenceNumber smallest_seqno;
|
||||
SequenceNumber largest_seqno;
|
||||
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
|
||||
GetVarint32(&input, &path_id) && GetVarint64(&input, &file_size) &&
|
||||
GetInternalKey(&input, &f.smallest) &&
|
||||
GetInternalKey(&input, &f.largest) &&
|
||||
GetVarint64(&input, &f.smallest_seqno) &&
|
||||
GetVarint64(&input, &f.largest_seqno)) {
|
||||
f.fd = FileDescriptor(number, path_id, file_size);
|
||||
GetVarint64(&input, &smallest_seqno) &&
|
||||
GetVarint64(&input, &largest_seqno)) {
|
||||
f.fd = FileDescriptor(number, path_id, file_size, smallest_seqno,
|
||||
largest_seqno);
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
} else {
|
||||
if (!msg) {
|
||||
@@ -473,6 +491,15 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
is_column_family_drop_ = true;
|
||||
break;
|
||||
|
||||
case kInAtomicGroup:
|
||||
is_in_atomic_group_ = true;
|
||||
if (!GetVarint32(&input, &remaining_entries_)) {
|
||||
if (!msg) {
|
||||
msg = "remaining entries";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
msg = "unknown tag";
|
||||
break;
|
||||
@@ -551,6 +578,11 @@ std::string VersionEdit::DebugString(bool hex_key) const {
|
||||
r.append("\n MaxColumnFamily: ");
|
||||
AppendNumberTo(&r, max_column_family_);
|
||||
}
|
||||
if (is_in_atomic_group_) {
|
||||
r.append("\n AtomicGroup: ");
|
||||
AppendNumberTo(&r, remaining_entries_);
|
||||
r.append(" entries remains");
|
||||
}
|
||||
r.append("\n}\n");
|
||||
return r;
|
||||
}
|
||||
@@ -623,6 +655,9 @@ std::string VersionEdit::DebugJSON(int edit_num, bool hex_key) const {
|
||||
if (has_min_log_number_to_keep_) {
|
||||
jw << "MinLogNumberToKeep" << min_log_number_to_keep_;
|
||||
}
|
||||
if (is_in_atomic_group_) {
|
||||
jw << "AtomicGroup" << remaining_entries_;
|
||||
}
|
||||
|
||||
jw.EndObject();
|
||||
|
||||
|
||||
+31
-16
@@ -27,7 +27,7 @@ const uint64_t kFileNumberMask = 0x3FFFFFFFFFFFFFFF;
|
||||
extern uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id);
|
||||
|
||||
// A copyable structure contains information needed to read data from an SST
|
||||
// file. It can contains a pointer to a table reader opened for the file, or
|
||||
// file. It can contain a pointer to a table reader opened for the file, or
|
||||
// file number and size, which can be used to create a new table reader for it.
|
||||
// The behavior is undefined when a copied of the structure is used when the
|
||||
// file is not in any live version any more.
|
||||
@@ -36,18 +36,28 @@ struct FileDescriptor {
|
||||
TableReader* table_reader;
|
||||
uint64_t packed_number_and_path_id;
|
||||
uint64_t file_size; // File size in bytes
|
||||
SequenceNumber smallest_seqno; // The smallest seqno in this file
|
||||
SequenceNumber largest_seqno; // The largest seqno in this file
|
||||
|
||||
FileDescriptor() : FileDescriptor(0, 0, 0) {}
|
||||
|
||||
FileDescriptor(uint64_t number, uint32_t path_id, uint64_t _file_size)
|
||||
: FileDescriptor(number, path_id, _file_size, kMaxSequenceNumber, 0) {}
|
||||
|
||||
FileDescriptor(uint64_t number, uint32_t path_id, uint64_t _file_size,
|
||||
SequenceNumber _smallest_seqno, SequenceNumber _largest_seqno)
|
||||
: table_reader(nullptr),
|
||||
packed_number_and_path_id(PackFileNumberAndPathId(number, path_id)),
|
||||
file_size(_file_size) {}
|
||||
file_size(_file_size),
|
||||
smallest_seqno(_smallest_seqno),
|
||||
largest_seqno(_largest_seqno) {}
|
||||
|
||||
FileDescriptor& operator=(const FileDescriptor& fd) {
|
||||
table_reader = fd.table_reader;
|
||||
packed_number_and_path_id = fd.packed_number_and_path_id;
|
||||
file_size = fd.file_size;
|
||||
smallest_seqno = fd.smallest_seqno;
|
||||
largest_seqno = fd.largest_seqno;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -77,8 +87,6 @@ struct FileMetaData {
|
||||
FileDescriptor fd;
|
||||
InternalKey smallest; // Smallest internal key served by table
|
||||
InternalKey largest; // Largest internal key served by table
|
||||
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;
|
||||
@@ -108,9 +116,7 @@ struct FileMetaData {
|
||||
// file.
|
||||
|
||||
FileMetaData()
|
||||
: smallest_seqno(kMaxSequenceNumber),
|
||||
largest_seqno(0),
|
||||
table_reader_handle(nullptr),
|
||||
: table_reader_handle(nullptr),
|
||||
compensated_file_size(0),
|
||||
num_entries(0),
|
||||
num_deletions(0),
|
||||
@@ -128,8 +134,8 @@ struct FileMetaData {
|
||||
smallest.DecodeFrom(key);
|
||||
}
|
||||
largest.DecodeFrom(key);
|
||||
smallest_seqno = std::min(smallest_seqno, seqno);
|
||||
largest_seqno = std::max(largest_seqno, seqno);
|
||||
fd.smallest_seqno = std::min(fd.smallest_seqno, seqno);
|
||||
fd.largest_seqno = std::max(fd.largest_seqno, seqno);
|
||||
}
|
||||
|
||||
// Unlike UpdateBoundaries, ranges do not need to be presented in any
|
||||
@@ -143,8 +149,8 @@ struct FileMetaData {
|
||||
if (largest.size() == 0 || icmp.Compare(largest, end) < 0) {
|
||||
largest = end;
|
||||
}
|
||||
smallest_seqno = std::min(smallest_seqno, seqno);
|
||||
largest_seqno = std::max(largest_seqno, seqno);
|
||||
fd.smallest_seqno = std::min(fd.smallest_seqno, seqno);
|
||||
fd.largest_seqno = std::max(fd.largest_seqno, seqno);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -233,17 +239,18 @@ class VersionEdit {
|
||||
bool marked_for_compaction) {
|
||||
assert(smallest_seqno <= largest_seqno);
|
||||
FileMetaData f;
|
||||
f.fd = FileDescriptor(file, file_path_id, file_size);
|
||||
f.fd = FileDescriptor(file, file_path_id, file_size, smallest_seqno,
|
||||
largest_seqno);
|
||||
f.smallest = smallest;
|
||||
f.largest = largest;
|
||||
f.smallest_seqno = smallest_seqno;
|
||||
f.largest_seqno = largest_seqno;
|
||||
f.fd.smallest_seqno = smallest_seqno;
|
||||
f.fd.largest_seqno = largest_seqno;
|
||||
f.marked_for_compaction = marked_for_compaction;
|
||||
new_files_.emplace_back(level, std::move(f));
|
||||
}
|
||||
|
||||
void AddFile(int level, const FileMetaData& f) {
|
||||
assert(f.smallest_seqno <= f.largest_seqno);
|
||||
assert(f.fd.smallest_seqno <= f.fd.largest_seqno);
|
||||
new_files_.emplace_back(level, f);
|
||||
}
|
||||
|
||||
@@ -293,6 +300,11 @@ class VersionEdit {
|
||||
return new_files_;
|
||||
}
|
||||
|
||||
void MarkAtomicGroup(uint32_t remaining_entries) {
|
||||
is_in_atomic_group_ = true;
|
||||
remaining_entries_ = remaining_entries;
|
||||
}
|
||||
|
||||
std::string DebugString(bool hex_key = false) const;
|
||||
std::string DebugJSON(int edit_num, bool hex_key = false) const;
|
||||
|
||||
@@ -322,7 +334,7 @@ class VersionEdit {
|
||||
DeletedFileSet deleted_files_;
|
||||
std::vector<std::pair<int, FileMetaData>> new_files_;
|
||||
|
||||
// Each version edit record should have column_family_id set
|
||||
// Each version edit record should have column_family_ set
|
||||
// If it's not set, it is default (0)
|
||||
uint32_t column_family_;
|
||||
// a version edit can be either column_family add or
|
||||
@@ -331,6 +343,9 @@ class VersionEdit {
|
||||
bool is_column_family_drop_;
|
||||
bool is_column_family_add_;
|
||||
std::string column_family_name_;
|
||||
|
||||
bool is_in_atomic_group_;
|
||||
uint32_t remaining_entries_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -191,6 +191,12 @@ TEST_F(VersionEditTest, MinLogNumberToKeep) {
|
||||
TestEncodeDecode(edit);
|
||||
}
|
||||
|
||||
TEST_F(VersionEditTest, AtomicGroupTest) {
|
||||
VersionEdit edit;
|
||||
edit.MarkAtomicGroup(1);
|
||||
TestEncodeDecode(edit);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+201
-136
@@ -897,8 +897,8 @@ void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
|
||||
}
|
||||
files.emplace_back(
|
||||
MakeTableFileName("", file->fd.GetNumber()), file_path,
|
||||
file->fd.GetFileSize(), file->smallest_seqno, file->largest_seqno,
|
||||
file->smallest.user_key().ToString(),
|
||||
file->fd.GetFileSize(), file->fd.smallest_seqno,
|
||||
file->fd.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);
|
||||
@@ -1212,12 +1212,9 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
|
||||
// report the counters before returning
|
||||
if (get_context.State() != GetContext::kNotFound &&
|
||||
get_context.State() != GetContext::kMerge) {
|
||||
for (uint32_t t = 0; t < Tickers::TICKER_ENUM_MAX; t++) {
|
||||
if (get_context.tickers_value[t] > 0) {
|
||||
RecordTick(db_statistics_, t, get_context.tickers_value[t]);
|
||||
}
|
||||
}
|
||||
get_context.State() != GetContext::kMerge &&
|
||||
db_statistics_ != nullptr) {
|
||||
get_context.ReportCounters();
|
||||
}
|
||||
switch (get_context.State()) {
|
||||
case GetContext::kNotFound:
|
||||
@@ -1251,10 +1248,8 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
f = fp.GetNextFile();
|
||||
}
|
||||
|
||||
for (uint32_t t = 0; t < Tickers::TICKER_ENUM_MAX; t++) {
|
||||
if (get_context.tickers_value[t] > 0) {
|
||||
RecordTick(db_statistics_, t, get_context.tickers_value[t]);
|
||||
}
|
||||
if (db_statistics_ != nullptr) {
|
||||
get_context.ReportCounters();
|
||||
}
|
||||
if (GetContext::kMerge == get_context.State()) {
|
||||
if (!merge_operator_) {
|
||||
@@ -1896,13 +1891,15 @@ void VersionStorageInfo::UpdateFilesByCompactionPri(
|
||||
case kOldestLargestSeqFirst:
|
||||
std::sort(temp.begin(), temp.end(),
|
||||
[](const Fsize& f1, const Fsize& f2) -> bool {
|
||||
return f1.file->largest_seqno < f2.file->largest_seqno;
|
||||
return f1.file->fd.largest_seqno <
|
||||
f2.file->fd.largest_seqno;
|
||||
});
|
||||
break;
|
||||
case kOldestSmallestSeqFirst:
|
||||
std::sort(temp.begin(), temp.end(),
|
||||
[](const Fsize& f1, const Fsize& f2) -> bool {
|
||||
return f1.file->smallest_seqno < f2.file->smallest_seqno;
|
||||
return f1.file->fd.smallest_seqno <
|
||||
f2.file->fd.smallest_seqno;
|
||||
});
|
||||
break;
|
||||
case kMinOverlappingRatio:
|
||||
@@ -1986,17 +1983,17 @@ void VersionStorageInfo::ComputeBottommostFilesMarkedForCompaction() {
|
||||
bottommost_files_mark_threshold_ = kMaxSequenceNumber;
|
||||
for (auto& level_and_file : bottommost_files_) {
|
||||
if (!level_and_file.second->being_compacted &&
|
||||
level_and_file.second->largest_seqno != 0 &&
|
||||
level_and_file.second->fd.largest_seqno != 0 &&
|
||||
level_and_file.second->num_deletions > 1) {
|
||||
// largest_seqno might be nonzero due to containing the final key in an
|
||||
// earlier compaction, whose seqnum we didn't zero out. Multiple deletions
|
||||
// ensures the file really contains deleted or overwritten keys.
|
||||
if (level_and_file.second->largest_seqno < oldest_snapshot_seqnum_) {
|
||||
if (level_and_file.second->fd.largest_seqno < oldest_snapshot_seqnum_) {
|
||||
bottommost_files_marked_for_compaction_.push_back(level_and_file);
|
||||
} else {
|
||||
bottommost_files_mark_threshold_ =
|
||||
std::min(bottommost_files_mark_threshold_,
|
||||
level_and_file.second->largest_seqno);
|
||||
level_and_file.second->fd.largest_seqno);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2035,7 +2032,7 @@ bool VersionStorageInfo::OverlapInLevel(int level,
|
||||
void VersionStorageInfo::GetOverlappingInputs(
|
||||
int level, const InternalKey* begin, const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs, int hint_index, int* file_index,
|
||||
bool expand_range) const {
|
||||
bool expand_range, InternalKey** next_smallest) const {
|
||||
if (level >= num_non_empty_levels_) {
|
||||
// this level is empty, no overlapping inputs
|
||||
return;
|
||||
@@ -2054,11 +2051,17 @@ void VersionStorageInfo::GetOverlappingInputs(
|
||||
}
|
||||
const Comparator* user_cmp = user_comparator_;
|
||||
if (level > 0) {
|
||||
GetOverlappingInputsRangeBinarySearch(level, begin, end, inputs,
|
||||
hint_index, file_index);
|
||||
GetOverlappingInputsRangeBinarySearch(level, begin, end, inputs, hint_index,
|
||||
file_index, false, next_smallest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_smallest) {
|
||||
// next_smallest key only makes sense for non-level 0, where files are
|
||||
// non-overlapping
|
||||
*next_smallest = nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < level_files_brief_[level].num_files; ) {
|
||||
FdWithKeyRange* f = &(level_files_brief_[level].files[i++]);
|
||||
const Slice file_start = ExtractUserKey(f->smallest_key);
|
||||
@@ -2186,7 +2189,7 @@ int sstableKeyCompare(const Comparator* user_cmp,
|
||||
void VersionStorageInfo::GetOverlappingInputsRangeBinarySearch(
|
||||
int level, const InternalKey* begin, const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs, int hint_index, int* file_index,
|
||||
bool within_interval) const {
|
||||
bool within_interval, InternalKey** next_smallest) const {
|
||||
assert(level > 0);
|
||||
int min = 0;
|
||||
int mid = 0;
|
||||
@@ -2222,6 +2225,9 @@ void VersionStorageInfo::GetOverlappingInputsRangeBinarySearch(
|
||||
|
||||
// If there were no overlapping files, return immediately.
|
||||
if (!foundOverlap) {
|
||||
if (next_smallest) {
|
||||
next_smallest = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// returns the index where an overlap is found
|
||||
@@ -2242,6 +2248,15 @@ void VersionStorageInfo::GetOverlappingInputsRangeBinarySearch(
|
||||
for (int i = start_index; i <= end_index; i++) {
|
||||
inputs->push_back(files_[level][i]);
|
||||
}
|
||||
|
||||
if (next_smallest != nullptr) {
|
||||
// Provide the next key outside the range covered by inputs
|
||||
if (++end_index < static_cast<int>(files_[level].size())) {
|
||||
**next_smallest = files_[level][end_index]->smallest;
|
||||
} else {
|
||||
*next_smallest = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store in *start_index and *end_index the range of all files in
|
||||
@@ -2422,7 +2437,7 @@ const char* VersionStorageInfo::LevelFileSummary(FileSummaryStorage* scratch,
|
||||
AppendHumanBytes(f->fd.GetFileSize(), sztxt, sizeof(sztxt));
|
||||
int ret = snprintf(scratch->buffer + len, sz,
|
||||
"#%" PRIu64 "(seq=%" PRIu64 ",sz=%s,%d) ",
|
||||
f->fd.GetNumber(), f->smallest_seqno, sztxt,
|
||||
f->fd.GetNumber(), f->fd.smallest_seqno, sztxt,
|
||||
static_cast<int>(f->being_compacted));
|
||||
if (ret < 0 || ret >= sz)
|
||||
break;
|
||||
@@ -3211,6 +3226,133 @@ void VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
|
||||
builder->Apply(edit);
|
||||
}
|
||||
|
||||
Status VersionSet::ApplyOneVersionEdit(
|
||||
VersionEdit& edit,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>& name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<uint32_t, BaseReferencedVersionBuilder*>& builders,
|
||||
bool* have_log_number, uint64_t* /* log_number */,
|
||||
bool* have_prev_log_number, uint64_t* previous_log_number,
|
||||
bool* have_next_file, uint64_t* next_file, bool* have_last_sequence,
|
||||
SequenceNumber* last_sequence, uint64_t* min_log_number_to_keep,
|
||||
uint32_t* max_column_family) {
|
||||
// Not found means that user didn't supply that column
|
||||
// family option AND we encountered column family add
|
||||
// record. Once we encounter column family drop record,
|
||||
// we will delete the column family from
|
||||
// column_families_not_found.
|
||||
bool cf_in_not_found = (column_families_not_found.find(edit.column_family_) !=
|
||||
column_families_not_found.end());
|
||||
// in builders means that user supplied that column family
|
||||
// option AND that we encountered column family add record
|
||||
bool cf_in_builders = builders.find(edit.column_family_) != builders.end();
|
||||
|
||||
// they can't both be true
|
||||
assert(!(cf_in_not_found && cf_in_builders));
|
||||
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
|
||||
if (edit.is_column_family_add_) {
|
||||
if (cf_in_builders || cf_in_not_found) {
|
||||
return Status::Corruption(
|
||||
"Manifest adding the same column family twice: " +
|
||||
edit.column_family_name_);
|
||||
}
|
||||
auto cf_options = name_to_options.find(edit.column_family_name_);
|
||||
if (cf_options == name_to_options.end()) {
|
||||
column_families_not_found.insert(
|
||||
{edit.column_family_, edit.column_family_name_});
|
||||
} else {
|
||||
cfd = CreateColumnFamily(cf_options->second, &edit);
|
||||
cfd->set_initialized();
|
||||
builders.insert(
|
||||
{edit.column_family_, new BaseReferencedVersionBuilder(cfd)});
|
||||
}
|
||||
} else if (edit.is_column_family_drop_) {
|
||||
if (cf_in_builders) {
|
||||
auto builder = builders.find(edit.column_family_);
|
||||
assert(builder != builders.end());
|
||||
delete builder->second;
|
||||
builders.erase(builder);
|
||||
cfd = column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
assert(cfd != nullptr);
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
cfd = nullptr;
|
||||
} else {
|
||||
// who else can have reference to cfd!?
|
||||
assert(false);
|
||||
}
|
||||
} else if (cf_in_not_found) {
|
||||
column_families_not_found.erase(edit.column_family_);
|
||||
} else {
|
||||
return Status::Corruption(
|
||||
"Manifest - dropping non-existing column family");
|
||||
}
|
||||
} else if (!cf_in_not_found) {
|
||||
if (!cf_in_builders) {
|
||||
return Status::Corruption(
|
||||
"Manifest record referencing unknown column family");
|
||||
}
|
||||
|
||||
cfd = column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
// this should never happen since cf_in_builders is true
|
||||
assert(cfd != nullptr);
|
||||
|
||||
// if it is not column family add or column family drop,
|
||||
// then it's a file add/delete, which should be forwarded
|
||||
// to builder
|
||||
auto builder = builders.find(edit.column_family_);
|
||||
assert(builder != builders.end());
|
||||
builder->second->version_builder()->Apply(&edit);
|
||||
}
|
||||
|
||||
if (cfd != nullptr) {
|
||||
if (edit.has_log_number_) {
|
||||
if (cfd->GetLogNumber() > edit.log_number_) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_->info_log,
|
||||
"MANIFEST corruption detected, but ignored - Log numbers in "
|
||||
"records NOT monotonically increasing");
|
||||
} else {
|
||||
cfd->SetLogNumber(edit.log_number_);
|
||||
*have_log_number = true;
|
||||
}
|
||||
}
|
||||
if (edit.has_comparator_ &&
|
||||
edit.comparator_ != cfd->user_comparator()->Name()) {
|
||||
return Status::InvalidArgument(
|
||||
cfd->user_comparator()->Name(),
|
||||
"does not match existing comparator " + edit.comparator_);
|
||||
}
|
||||
}
|
||||
|
||||
if (edit.has_prev_log_number_) {
|
||||
*previous_log_number = edit.prev_log_number_;
|
||||
*have_prev_log_number = true;
|
||||
}
|
||||
|
||||
if (edit.has_next_file_number_) {
|
||||
*next_file = edit.next_file_number_;
|
||||
*have_next_file = true;
|
||||
}
|
||||
|
||||
if (edit.has_max_column_family_) {
|
||||
*max_column_family = edit.max_column_family_;
|
||||
}
|
||||
|
||||
if (edit.has_min_log_number_to_keep_) {
|
||||
*min_log_number_to_keep =
|
||||
std::max(*min_log_number_to_keep, edit.min_log_number_to_keep_);
|
||||
}
|
||||
|
||||
if (edit.has_last_sequence_) {
|
||||
*last_sequence = edit.last_sequence_;
|
||||
*have_last_sequence = true;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status VersionSet::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only) {
|
||||
@@ -3299,6 +3441,8 @@ Status VersionSet::Recover(
|
||||
true /*checksum*/, 0 /*initial_offset*/, 0);
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
std::vector<VersionEdit> replay_buffer;
|
||||
size_t num_entries_decoded = 0;
|
||||
while (reader.ReadRecord(&record, &scratch) && s.ok()) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
@@ -3306,123 +3450,44 @@ Status VersionSet::Recover(
|
||||
break;
|
||||
}
|
||||
|
||||
// Not found means that user didn't supply that column
|
||||
// family option AND we encountered column family add
|
||||
// record. Once we encounter column family drop record,
|
||||
// we will delete the column family from
|
||||
// column_families_not_found.
|
||||
bool cf_in_not_found =
|
||||
column_families_not_found.find(edit.column_family_) !=
|
||||
column_families_not_found.end();
|
||||
// in builders means that user supplied that column family
|
||||
// option AND that we encountered column family add record
|
||||
bool cf_in_builders =
|
||||
builders.find(edit.column_family_) != builders.end();
|
||||
|
||||
// they can't both be true
|
||||
assert(!(cf_in_not_found && cf_in_builders));
|
||||
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
|
||||
if (edit.is_column_family_add_) {
|
||||
if (cf_in_builders || cf_in_not_found) {
|
||||
s = Status::Corruption(
|
||||
"Manifest adding the same column family twice");
|
||||
break;
|
||||
if (edit.is_in_atomic_group_) {
|
||||
if (replay_buffer.empty()) {
|
||||
replay_buffer.resize(edit.remaining_entries_ + 1);
|
||||
}
|
||||
auto cf_options = cf_name_to_options.find(edit.column_family_name_);
|
||||
if (cf_options == cf_name_to_options.end()) {
|
||||
column_families_not_found.insert(
|
||||
{edit.column_family_, edit.column_family_name_});
|
||||
} else {
|
||||
cfd = CreateColumnFamily(cf_options->second, &edit);
|
||||
cfd->set_initialized();
|
||||
builders.insert(
|
||||
{edit.column_family_, new BaseReferencedVersionBuilder(cfd)});
|
||||
++num_entries_decoded;
|
||||
if (num_entries_decoded + edit.remaining_entries_ !=
|
||||
static_cast<uint32_t>(replay_buffer.size())) {
|
||||
return Status::Corruption("corrupted atomic group");
|
||||
}
|
||||
} else if (edit.is_column_family_drop_) {
|
||||
if (cf_in_builders) {
|
||||
auto builder = builders.find(edit.column_family_);
|
||||
assert(builder != builders.end());
|
||||
delete builder->second;
|
||||
builders.erase(builder);
|
||||
cfd = column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
cfd = nullptr;
|
||||
} else {
|
||||
// who else can have reference to cfd!?
|
||||
assert(false);
|
||||
replay_buffer[num_entries_decoded - 1] = std::move(edit);
|
||||
if (num_entries_decoded == replay_buffer.size()) {
|
||||
for (auto& e : replay_buffer) {
|
||||
s = ApplyOneVersionEdit(
|
||||
e, cf_name_to_options, column_families_not_found, builders,
|
||||
&have_log_number, &log_number, &have_prev_log_number,
|
||||
&previous_log_number, &have_next_file, &next_file,
|
||||
&have_last_sequence, &last_sequence, &min_log_number_to_keep,
|
||||
&max_column_family);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (cf_in_not_found) {
|
||||
column_families_not_found.erase(edit.column_family_);
|
||||
} else {
|
||||
s = Status::Corruption(
|
||||
"Manifest - dropping non-existing column family");
|
||||
break;
|
||||
replay_buffer.clear();
|
||||
num_entries_decoded = 0;
|
||||
}
|
||||
} else if (!cf_in_not_found) {
|
||||
if (!cf_in_builders) {
|
||||
s = Status::Corruption(
|
||||
"Manifest record referencing unknown column family");
|
||||
break;
|
||||
} else {
|
||||
if (!replay_buffer.empty()) {
|
||||
return Status::Corruption("corrupted atomic group");
|
||||
}
|
||||
|
||||
cfd = column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
// this should never happen since cf_in_builders is true
|
||||
assert(cfd != nullptr);
|
||||
|
||||
// if it is not column family add or column family drop,
|
||||
// then it's a file add/delete, which should be forwarded
|
||||
// to builder
|
||||
auto builder = builders.find(edit.column_family_);
|
||||
assert(builder != builders.end());
|
||||
builder->second->version_builder()->Apply(&edit);
|
||||
s = ApplyOneVersionEdit(
|
||||
edit, cf_name_to_options, column_families_not_found, builders,
|
||||
&have_log_number, &log_number, &have_prev_log_number,
|
||||
&previous_log_number, &have_next_file, &next_file,
|
||||
&have_last_sequence, &last_sequence, &min_log_number_to_keep,
|
||||
&max_column_family);
|
||||
}
|
||||
|
||||
if (cfd != nullptr) {
|
||||
if (edit.has_log_number_) {
|
||||
if (cfd->GetLogNumber() > edit.log_number_) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_->info_log,
|
||||
"MANIFEST corruption detected, but ignored - Log numbers in "
|
||||
"records NOT monotonically increasing");
|
||||
} else {
|
||||
cfd->SetLogNumber(edit.log_number_);
|
||||
have_log_number = true;
|
||||
}
|
||||
}
|
||||
if (edit.has_comparator_ &&
|
||||
edit.comparator_ != cfd->user_comparator()->Name()) {
|
||||
s = Status::InvalidArgument(
|
||||
cfd->user_comparator()->Name(),
|
||||
"does not match existing comparator " + edit.comparator_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (edit.has_prev_log_number_) {
|
||||
previous_log_number = edit.prev_log_number_;
|
||||
have_prev_log_number = true;
|
||||
}
|
||||
|
||||
if (edit.has_next_file_number_) {
|
||||
next_file = edit.next_file_number_;
|
||||
have_next_file = true;
|
||||
}
|
||||
|
||||
if (edit.has_max_column_family_) {
|
||||
max_column_family = edit.max_column_family_;
|
||||
}
|
||||
|
||||
if (edit.has_min_log_number_to_keep_) {
|
||||
min_log_number_to_keep =
|
||||
std::max(min_log_number_to_keep, edit.min_log_number_to_keep_);
|
||||
}
|
||||
|
||||
if (edit.has_last_sequence_) {
|
||||
last_sequence = edit.last_sequence_;
|
||||
have_last_sequence = true;
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3968,7 +4033,7 @@ Status VersionSet::WriteSnapshot(log::Writer* log) {
|
||||
cfd->current()->storage_info()->LevelFiles(level)) {
|
||||
edit.AddFile(level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->smallest_seqno, f->largest_seqno,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
}
|
||||
}
|
||||
@@ -4293,8 +4358,8 @@ void VersionSet::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
|
||||
filemetadata.size = file->fd.GetFileSize();
|
||||
filemetadata.smallestkey = file->smallest.user_key().ToString();
|
||||
filemetadata.largestkey = file->largest.user_key().ToString();
|
||||
filemetadata.smallest_seqno = file->smallest_seqno;
|
||||
filemetadata.largest_seqno = file->largest_seqno;
|
||||
filemetadata.smallest_seqno = file->fd.smallest_seqno;
|
||||
filemetadata.largest_seqno = file->fd.largest_seqno;
|
||||
metadata->push_back(filemetadata);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-6
@@ -52,7 +52,6 @@ class Writer;
|
||||
}
|
||||
|
||||
class Compaction;
|
||||
class InternalIterator;
|
||||
class LogBuffer;
|
||||
class LookupKey;
|
||||
class MemTable;
|
||||
@@ -189,9 +188,11 @@ class VersionStorageInfo {
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
int hint_index = -1, // index of overlap file
|
||||
int* file_index = nullptr, // return index of overlap file
|
||||
bool expand_range = true) // if set, returns files which overlap the
|
||||
const; // range and overlap each other. If false,
|
||||
bool expand_range = true, // if set, returns files which overlap the
|
||||
// range and overlap each other. If false,
|
||||
// then just files intersecting the range
|
||||
InternalKey** next_smallest = nullptr) // if non-null, returns the
|
||||
const; // smallest key of next file not included
|
||||
void GetCleanInputsWithinInterval(
|
||||
int level, const InternalKey* begin, // nullptr means before all keys
|
||||
const InternalKey* end, // nullptr means after all keys
|
||||
@@ -201,14 +202,15 @@ class VersionStorageInfo {
|
||||
const;
|
||||
|
||||
void GetOverlappingInputsRangeBinarySearch(
|
||||
int level, // level > 0
|
||||
int level, // level > 0
|
||||
const InternalKey* begin, // nullptr means before all keys
|
||||
const InternalKey* end, // nullptr means after all keys
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
int hint_index, // index of overlap file
|
||||
int* file_index, // return index of overlap file
|
||||
bool within_interval = false) // if set, force the inputs within interval
|
||||
const;
|
||||
bool within_interval = false, // if set, force the inputs within interval
|
||||
InternalKey** next_smallest = nullptr) // if non-null, returns the
|
||||
const; // smallest key of next file not included
|
||||
|
||||
void ExtendFileRangeOverlappingInterval(
|
||||
int level,
|
||||
@@ -729,6 +731,10 @@ struct ObsoleteFileInfo {
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
class BaseReferencedVersionBuilder;
|
||||
}
|
||||
|
||||
class VersionSet {
|
||||
public:
|
||||
VersionSet(const std::string& dbname, const ImmutableDBOptions* db_options,
|
||||
@@ -832,6 +838,11 @@ class VersionSet {
|
||||
// Allocate and return a new file number
|
||||
uint64_t NewFileNumber() { return next_file_number_.fetch_add(1); }
|
||||
|
||||
// Fetch And Add n new file number
|
||||
uint64_t FetchAddFileNumber(uint64_t n) {
|
||||
return next_file_number_.fetch_add(n);
|
||||
}
|
||||
|
||||
// Return the last sequence number.
|
||||
uint64_t LastSequence() const {
|
||||
return last_sequence_.load(std::memory_order_acquire);
|
||||
@@ -985,6 +996,16 @@ class VersionSet {
|
||||
ColumnFamilyData* CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
VersionEdit* edit);
|
||||
|
||||
Status ApplyOneVersionEdit(
|
||||
VersionEdit& edit,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>& name_to_opts,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<uint32_t, BaseReferencedVersionBuilder*>& builders,
|
||||
bool* have_log_number, uint64_t* log_number, bool* have_prev_log_number,
|
||||
uint64_t* previous_log_number, bool* have_next_file, uint64_t* next_file,
|
||||
bool* have_last_sequence, SequenceNumber* last_sequence,
|
||||
uint64_t* min_log_number_to_keep, uint32_t* max_column_family);
|
||||
|
||||
Status ProcessManifestWrites(std::deque<ManifestWriter>& writers,
|
||||
InstrumentedMutex* mu, Directory* db_directory,
|
||||
bool new_descriptor_log,
|
||||
|
||||
@@ -727,6 +727,11 @@ Status WriteBatchInternal::MarkEndPrepare(WriteBatch* b, const Slice& xid,
|
||||
ContentFlags::HAS_END_PREPARE |
|
||||
ContentFlags::HAS_BEGIN_PREPARE,
|
||||
std::memory_order_relaxed);
|
||||
if (unprepared_batch) {
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_BEGIN_UNPREPARE,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
+58
-1
@@ -24,7 +24,10 @@ WriteThread::WriteThread(const ImmutableDBOptions& db_options)
|
||||
enable_pipelined_write_(db_options.enable_pipelined_write),
|
||||
newest_writer_(nullptr),
|
||||
newest_memtable_writer_(nullptr),
|
||||
last_sequence_(0) {}
|
||||
last_sequence_(0),
|
||||
write_stall_dummy_(),
|
||||
stall_mu_(),
|
||||
stall_cv_(&stall_mu_) {}
|
||||
|
||||
uint8_t WriteThread::BlockingAwaitState(Writer* w, uint8_t goal_mask) {
|
||||
// We're going to block. Lazily create the mutex. We guarantee
|
||||
@@ -219,6 +222,28 @@ bool WriteThread::LinkOne(Writer* w, std::atomic<Writer*>* newest_writer) {
|
||||
assert(w->state == STATE_INIT);
|
||||
Writer* writers = newest_writer->load(std::memory_order_relaxed);
|
||||
while (true) {
|
||||
// If write stall in effect, and w->no_slowdown is not true,
|
||||
// block here until stall is cleared. If its true, then return
|
||||
// immediately
|
||||
if (writers == &write_stall_dummy_) {
|
||||
if (w->no_slowdown) {
|
||||
w->status = Status::Incomplete("Write stall");
|
||||
SetState(w, STATE_COMPLETED);
|
||||
return false;
|
||||
}
|
||||
// Since no_slowdown is false, wait here to be notified of the write
|
||||
// stall clearing
|
||||
{
|
||||
MutexLock lock(&stall_mu_);
|
||||
writers = newest_writer->load(std::memory_order_relaxed);
|
||||
if (writers == &write_stall_dummy_) {
|
||||
stall_cv_.Wait();
|
||||
// Load newest_writers_ again since it may have changed
|
||||
writers = newest_writer->load(std::memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
w->link_older = writers;
|
||||
if (newest_writer->compare_exchange_weak(writers, w)) {
|
||||
return (writers == nullptr);
|
||||
@@ -303,12 +328,44 @@ void WriteThread::CompleteFollower(Writer* w, WriteGroup& write_group) {
|
||||
SetState(w, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
void WriteThread::BeginWriteStall() {
|
||||
LinkOne(&write_stall_dummy_, &newest_writer_);
|
||||
|
||||
// Walk writer list until w->write_group != nullptr. The current write group
|
||||
// will not have a mix of slowdown/no_slowdown, so its ok to stop at that
|
||||
// point
|
||||
Writer* w = write_stall_dummy_.link_older;
|
||||
Writer* prev = &write_stall_dummy_;
|
||||
while (w != nullptr && w->write_group == nullptr) {
|
||||
if (w->no_slowdown) {
|
||||
prev->link_older = w->link_older;
|
||||
w->status = Status::Incomplete("Write stall");
|
||||
SetState(w, STATE_COMPLETED);
|
||||
w = prev->link_older;
|
||||
} else {
|
||||
prev = w;
|
||||
w = w->link_older;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::EndWriteStall() {
|
||||
MutexLock lock(&stall_mu_);
|
||||
|
||||
assert(newest_writer_.load(std::memory_order_relaxed) == &write_stall_dummy_);
|
||||
newest_writer_.exchange(write_stall_dummy_.link_older);
|
||||
|
||||
// Wake up writers
|
||||
stall_cv_.SignalAll();
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext jbg_ctx("JoinBatchGroup");
|
||||
void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -342,6 +342,13 @@ class WriteThread {
|
||||
return last_sequence_;
|
||||
}
|
||||
|
||||
// Insert a dummy writer at the tail of the write queue to indicate a write
|
||||
// stall, and fail any writers in the queue with no_slowdown set to true
|
||||
void BeginWriteStall();
|
||||
|
||||
// Remove the dummy writer and wake up waiting writers
|
||||
void EndWriteStall();
|
||||
|
||||
private:
|
||||
// See AwaitState.
|
||||
const uint64_t max_yield_usec_;
|
||||
@@ -365,6 +372,17 @@ class WriteThread {
|
||||
// is not necessary visible to reads because the writer can be ongoing.
|
||||
SequenceNumber last_sequence_;
|
||||
|
||||
// A dummy writer to indicate a write stall condition. This will be inserted
|
||||
// at the tail of the writer queue by the leader, so newer writers can just
|
||||
// check for this and bail
|
||||
Writer write_stall_dummy_;
|
||||
|
||||
// Mutex and condvar for writers to block on a write stall. During a write
|
||||
// stall, writers with no_slowdown set to false will wait on this rather
|
||||
// on the writer queue
|
||||
port::Mutex stall_mu_;
|
||||
port::CondVar stall_cv_;
|
||||
|
||||
// Waits for w->state & goal_mask using w->StateMutex(). Returns
|
||||
// the state that satisfies goal_mask.
|
||||
uint8_t BlockingAwaitState(Writer* w, uint8_t goal_mask);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Rocksdb Tuning Advisor
|
||||
layout: post
|
||||
author: poojam23
|
||||
category: blog
|
||||
---
|
||||
|
||||
The performance of Rocksdb is contingent on its tuning. However, because
|
||||
of the complexity of its underlying technology and a large number of
|
||||
configurable parameters, a good configuration is sometimes hard to obtain. The aim of
|
||||
the python command-line tool, Rocksdb Advisor, is to automate the process of
|
||||
suggesting improvements in the configuration based on advice from Rocksdb
|
||||
experts.
|
||||
|
||||
### Overview
|
||||
|
||||
Experts share their wisdom as rules comprising of conditions and suggestions in the INI format (refer
|
||||
[rules.ini](https://github.com/facebook/rocksdb/blob/master/tools/advisor/advisor/rules.ini)).
|
||||
Users provide the Rocksdb configuration that they want to improve upon (as the
|
||||
familiar Rocksdb OPTIONS file —
|
||||
[example](https://github.com/facebook/rocksdb/blob/master/examples/rocksdb_option_file_example.ini))
|
||||
and the path of the file which contains Rocksdb logs and statistics.
|
||||
The [Advisor](https://github.com/facebook/rocksdb/blob/master/tools/advisor/advisor/rule_parser_example.py)
|
||||
creates appropriate DataSource objects (for Rocksdb
|
||||
[logs](https://github.com/facebook/rocksdb/blob/master/tools/advisor/advisor/db_log_parser.py),
|
||||
[options](https://github.com/facebook/rocksdb/blob/master/tools/advisor/advisor/db_options_parser.py),
|
||||
[statistics](https://github.com/facebook/rocksdb/blob/master/tools/advisor/advisor/db_stats_fetcher.py) etc.)
|
||||
and provides them to the [Rules Engine](https://github.com/facebook/rocksdb/blob/master/tools/advisor/advisor/rule_parser.py).
|
||||
The Rules uses rules from experts to parse data-sources and trigger appropriate rules.
|
||||
The Advisor's output gives information about which rules were triggered,
|
||||
why they were triggered and what each of them suggests. Each suggestion
|
||||
provided by a triggered rule advises some action on a Rocksdb
|
||||
configuration option, for example, increase CFOptions.write_buffer_size,
|
||||
set bloom_bits to 2 etc.
|
||||
|
||||
### Usage
|
||||
|
||||
An example command to run the tool:
|
||||
|
||||
```shell
|
||||
cd rocksdb/tools/advisor
|
||||
python3 -m advisor.rule_parser_example --rules_spec=advisor/rules.ini --rocksdb_options=test/input_files/OPTIONS-000005 --log_files_path_prefix=test/input_files/LOG-0 --stats_dump_period_sec=20
|
||||
```
|
||||
|
||||
Sample output where a Rocksdb log-based rule has been triggered :
|
||||
|
||||
```shell
|
||||
Rule: stall-too-many-memtables
|
||||
LogCondition: stall-too-many-memtables regex: Stopping writes because we have \d+ immutable memtables \(waiting for flush\), max_write_buffer_number is set to \d+
|
||||
Suggestion: inc-bg-flush option : DBOptions.max_background_flushes action : increase suggested_values : ['2']
|
||||
Suggestion: inc-write-buffer option : CFOptions.max_write_buffer_number action : increase
|
||||
scope: col_fam:
|
||||
{'default'}
|
||||
```
|
||||
|
||||
### Read more
|
||||
|
||||
For more information, refer to [advisor](https://github.com/facebook/rocksdb/tree/master/tools/advisor/README.md).
|
||||
Vendored
+1
-1
@@ -20,7 +20,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#if defined(OS_LINUX) || defined(OS_SOLARIS)
|
||||
#if defined(OS_LINUX) || defined(OS_SOLARIS) || defined(OS_ANDROID)
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
@@ -831,6 +831,12 @@ rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(rocksdb_options_t*,
|
||||
int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_enable_pipelined_write(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_subcompactions(
|
||||
rocksdb_options_t*, uint32_t);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_background_jobs(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_background_compactions(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_base_background_compactions(
|
||||
|
||||
@@ -74,6 +74,12 @@ class Comparator {
|
||||
const Slice& /*t*/) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if two keys with different byte sequences can be regarded
|
||||
// as equal by this comparator.
|
||||
// The major use case is to determine if DataBlockHashIndex is compatible
|
||||
// with the customized comparator.
|
||||
virtual bool CanKeysWithDifferentByteContentsBeEqual() const { return true; }
|
||||
};
|
||||
|
||||
// Return a builtin comparator that uses lexicographic byte-wise
|
||||
|
||||
+10
-5
@@ -53,6 +53,7 @@ struct ExternalSstFileInfo;
|
||||
class WriteBatch;
|
||||
class Env;
|
||||
class EventListener;
|
||||
class TraceWriter;
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
@@ -996,11 +997,6 @@ class DB {
|
||||
std::vector<LiveFileMetaData>* /*metadata*/) {}
|
||||
|
||||
// Obtains the meta data of the specified column family of the DB.
|
||||
// Status::NotFound() will be returned if the current DB does not have
|
||||
// any column family match the specified name.
|
||||
//
|
||||
// If cf_name is not specified, then the metadata of the default
|
||||
// column family will be returned.
|
||||
virtual void GetColumnFamilyMetaData(ColumnFamilyHandle* /*column_family*/,
|
||||
ColumnFamilyMetaData* /*metadata*/) {}
|
||||
|
||||
@@ -1173,6 +1169,15 @@ class DB {
|
||||
return Status::NotSupported("PromoteL0() is not implemented.");
|
||||
}
|
||||
|
||||
// Trace DB operations. Use EndTrace() to stop tracing.
|
||||
virtual Status StartTrace(const TraceOptions& /*options*/,
|
||||
std::unique_ptr<TraceWriter>&& /*trace_writer*/) {
|
||||
return Status::NotSupported("StartTrace() is not implemented.");
|
||||
}
|
||||
|
||||
virtual Status EndTrace() {
|
||||
return Status::NotSupported("EndTrace() is not implemented.");
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Needed for StackableDB
|
||||
|
||||
@@ -430,6 +430,8 @@ struct DBOptions {
|
||||
// (i.e. the ones that are causing all the space amplification). If set to 0
|
||||
// (default), we will dynamically choose the WAL size limit to be
|
||||
// [sum of all write_buffer_size * max_write_buffer_number] * 4
|
||||
// This option takes effect only when there are more than one column family as
|
||||
// otherwise the wal size is dictated by the write_buffer_size.
|
||||
// Default: 0
|
||||
uint64_t max_total_wal_size = 0;
|
||||
|
||||
@@ -1181,8 +1183,13 @@ struct FlushOptions {
|
||||
// If true, the flush will wait until the flush is done.
|
||||
// Default: true
|
||||
bool wait;
|
||||
|
||||
FlushOptions() : wait(true) {}
|
||||
// If true, the flush would proceed immediately even it means writes will
|
||||
// stall for the duration of the flush; if false the operation will wait
|
||||
// until it's possible to do flush w/o causing stall or until required flush
|
||||
// is performed by someone else (foreground call or background thread).
|
||||
// Default: false
|
||||
bool allow_write_stall;
|
||||
FlushOptions() : wait(true), allow_write_stall(false) {}
|
||||
};
|
||||
|
||||
// Create a Logger from provided DBOptions
|
||||
@@ -1194,6 +1201,9 @@ extern Status CreateLoggerFromOptions(const std::string& dbname,
|
||||
struct CompactionOptions {
|
||||
// Compaction output compression type
|
||||
// Default: snappy
|
||||
// If set to `kDisableCompressionOption`, RocksDB will choose compression type
|
||||
// according to the `ColumnFamilyOptions`, taking into account the output
|
||||
// level if `compression_per_level` is specified.
|
||||
CompressionType compression;
|
||||
// Compaction will create files of size `output_file_size_limit`.
|
||||
// Default: MAX, which means that compaction will create a single file
|
||||
@@ -1265,8 +1275,22 @@ struct IngestExternalFileOptions {
|
||||
// with allow_ingest_behind=true since the dawn of time.
|
||||
// All files will be ingested at the bottommost level with seqno=0.
|
||||
bool ingest_behind = false;
|
||||
// Set to true if you would like to write global_seqno to a given offset in
|
||||
// the external SST file for backward compatibility. Older versions of
|
||||
// RocksDB writes a global_seqno to a given offset within ingested SST files,
|
||||
// and new versions of RocksDB do not. If you ingest an external SST using
|
||||
// new version of RocksDB and would like to be able to downgrade to an
|
||||
// older version of RocksDB, you should set 'write_global_seqno' to true. If
|
||||
// your service is just starting to use the new RocksDB, we recommend that
|
||||
// you set this option to false, which brings two benefits:
|
||||
// 1. No extra random write for global_seqno during ingestion.
|
||||
// 2. Without writing external SST file, it's possible to do checksum.
|
||||
// We have a plan to set this option to false by default in the future.
|
||||
bool write_global_seqno = true;
|
||||
};
|
||||
|
||||
struct TraceOptions {};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // STORAGE_ROCKSDB_INCLUDE_OPTIONS_H_
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef __cpp_lib_string_view
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
#include "rocksdb/cleanable.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -41,6 +45,12 @@ class Slice {
|
||||
/* implicit */
|
||||
Slice(const std::string& s) : data_(s.data()), size_(s.size()) { }
|
||||
|
||||
#ifdef __cpp_lib_string_view
|
||||
// Create a slice that refers to the same contents as "sv"
|
||||
/* implicit */
|
||||
Slice(std::string_view sv) : data_(sv.data()), size_(sv.size()) { }
|
||||
#endif
|
||||
|
||||
// Create a slice that refers to s[0,strlen(s)-1]
|
||||
/* implicit */
|
||||
Slice(const char* s) : data_(s) {
|
||||
@@ -86,6 +96,13 @@ class Slice {
|
||||
// when hex is true, returns a string of twice the length hex encoded (0-9A-F)
|
||||
std::string ToString(bool hex = false) const;
|
||||
|
||||
#ifdef __cpp_lib_string_view
|
||||
// Return a string_view that references the same data as this slice.
|
||||
std::string_view ToStringView() const {
|
||||
return std::string_view(data_, size_);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Decodes the current slice interpreted as an hexadecimal string into result,
|
||||
// if successful returns true, if this isn't a valid hex string
|
||||
// (e.g not coming from Slice::ToString(true)) DecodeHex returns false.
|
||||
|
||||
@@ -75,6 +75,10 @@ class SstFileManager {
|
||||
// Update trash/DB size ratio where new files will be deleted immediately
|
||||
// thread-safe
|
||||
virtual void SetMaxTrashDBRatio(double ratio) = 0;
|
||||
|
||||
// Return the total size of trash files
|
||||
// thread-safe
|
||||
virtual uint64_t GetTotalTrashSize() = 0;
|
||||
};
|
||||
|
||||
// Create a new SstFileManager that can be shared among multiple RocksDB
|
||||
|
||||
@@ -282,8 +282,6 @@ class Status {
|
||||
Severity sev_;
|
||||
const char* state_;
|
||||
|
||||
static const char* msgs[static_cast<int>(kMaxSubCode)];
|
||||
|
||||
explicit Status(Code _code, SubCode _subcode = kNone)
|
||||
: code_(_code), subcode_(_subcode), sev_(kNoError), state_(nullptr) {}
|
||||
|
||||
|
||||
@@ -100,6 +100,18 @@ struct BlockBasedTableOptions {
|
||||
|
||||
IndexType index_type = kBinarySearch;
|
||||
|
||||
// The index type that will be used for the data block.
|
||||
enum DataBlockIndexType : char {
|
||||
kDataBlockBinarySearch = 0, // traditional block type
|
||||
kDataBlockBinaryAndHash = 1, // additional hash index
|
||||
};
|
||||
|
||||
DataBlockIndexType data_block_index_type = kDataBlockBinarySearch;
|
||||
|
||||
// #entries/#buckets. It is valid only when data_block_hash_index_type is
|
||||
// kDataBlockBinaryAndHash.
|
||||
double data_block_hash_table_util_ratio = 0.75;
|
||||
|
||||
// This option is now deprecated. No matter what value it is set to,
|
||||
// it will behave as if hash_index_allow_collision=true.
|
||||
bool hash_index_allow_collision = true;
|
||||
|
||||
@@ -34,6 +34,7 @@ struct TablePropertiesNames {
|
||||
static const std::string kIndexPartitions;
|
||||
static const std::string kTopLevelIndexSize;
|
||||
static const std::string kIndexKeyIsUserKey;
|
||||
static const std::string kIndexValueIsDeltaEncoded;
|
||||
static const std::string kFilterSize;
|
||||
static const std::string kRawKeySize;
|
||||
static const std::string kRawValueSize;
|
||||
@@ -139,6 +140,8 @@ struct TableProperties {
|
||||
// Whether the index key is user key. Otherwise it includes 8 byte of sequence
|
||||
// number added by internal key format.
|
||||
uint64_t index_key_is_user_key = 0;
|
||||
// Whether delta encoding is used to encode the index values.
|
||||
uint64_t index_value_is_delta_encoded = 0;
|
||||
// the size of filter block.
|
||||
uint64_t filter_size = 0;
|
||||
// total raw key size
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Allow custom implementations of TraceWriter and TraceReader.
|
||||
// By default, RocksDB provides a way to capture the traces to a file using the
|
||||
// factory NewFileTraceWriter(). But users could also choose to export traces to
|
||||
// any other system by providing custom implementations of TraceWriter and
|
||||
// TraceReader.
|
||||
|
||||
// TraceWriter allows exporting RocksDB traces to any system, one operation at
|
||||
// a time.
|
||||
class TraceWriter {
|
||||
public:
|
||||
TraceWriter() {}
|
||||
virtual ~TraceWriter() {}
|
||||
|
||||
virtual Status Write(const Slice& data) = 0;
|
||||
virtual Status Close() = 0;
|
||||
};
|
||||
|
||||
// TraceReader allows reading RocksDB traces from any system, one operation at
|
||||
// a time. A RocksDB Replayer could depend on this to replay opertions.
|
||||
class TraceReader {
|
||||
public:
|
||||
TraceReader() {}
|
||||
virtual ~TraceReader() {}
|
||||
|
||||
virtual Status Read(std::string* data) = 0;
|
||||
virtual Status Close() = 0;
|
||||
};
|
||||
|
||||
// Factory methods to read/write traces from/to a file.
|
||||
Status NewFileTraceWriter(Env* env, const EnvOptions& env_options,
|
||||
const std::string& trace_filename,
|
||||
std::unique_ptr<TraceWriter>* trace_writer);
|
||||
Status NewFileTraceReader(Env* env, const EnvOptions& env_options,
|
||||
const std::string& trace_filename,
|
||||
std::unique_ptr<TraceReader>* trace_reader);
|
||||
} // namespace rocksdb
|
||||
@@ -23,6 +23,7 @@ enum EntryType {
|
||||
kEntrySingleDelete,
|
||||
kEntryMerge,
|
||||
kEntryRangeDeletion,
|
||||
kEntryBlobIndex,
|
||||
kEntryOther,
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,13 @@ struct KeyVersion {
|
||||
};
|
||||
|
||||
// Returns listing of all versions of keys in the provided user key range.
|
||||
// The range is inclusive-inclusive, i.e., [`begin_key`, `end_key`].
|
||||
// The range is inclusive-inclusive, i.e., [`begin_key`, `end_key`], or
|
||||
// `max_num_ikeys` has been reached. Since all those keys returned will be
|
||||
// copied to memory, if the range covers too many keys, the memory usage
|
||||
// may be huge. `max_num_ikeys` can be used to cap the memory usage.
|
||||
// The result is inserted into the provided vector, `key_versions`.
|
||||
Status GetAllKeyVersions(DB* db, Slice begin_key, Slice end_key,
|
||||
size_t max_num_ikeys,
|
||||
std::vector<KeyVersion>* key_versions);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -152,6 +152,12 @@ class Transaction {
|
||||
// If there is no previous call to SetSavePoint(), returns Status::NotFound()
|
||||
virtual Status RollbackToSavePoint() = 0;
|
||||
|
||||
// Pop the most recent save point.
|
||||
// If there is no previous call to SetSavePoint(), Status::NotFound()
|
||||
// will be returned.
|
||||
// Otherwise returns Status::OK().
|
||||
virtual Status PopSavePoint() = 0;
|
||||
|
||||
// This function is similar to DB::Get() except it will also read pending
|
||||
// changes in this transaction. Currently, this function will return
|
||||
// Status::MergeInProgress if the most recent write to the queried key in
|
||||
|
||||
@@ -231,6 +231,7 @@ class WriteBatchWithIndex : public WriteBatchBase {
|
||||
Status PopSavePoint() override;
|
||||
|
||||
void SetMaxBytes(size_t max_bytes) override;
|
||||
size_t GetDataSize() const;
|
||||
|
||||
private:
|
||||
friend class PessimisticTransactionDB;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#define ROCKSDB_MAJOR 5
|
||||
#define ROCKSDB_MINOR 15
|
||||
#define ROCKSDB_PATCH 10
|
||||
#define ROCKSDB_MINOR 16
|
||||
#define ROCKSDB_PATCH 6
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -13,6 +13,7 @@ set(JNI_NATIVE_SOURCES
|
||||
rocksjni/compaction_filter_factory_jnicallback.cc
|
||||
rocksjni/compaction_options_fifo.cc
|
||||
rocksjni/compaction_options_universal.cc
|
||||
rocksjni/compact_range_options.cc
|
||||
rocksjni/comparator.cc
|
||||
rocksjni/comparatorjnicallback.cc
|
||||
rocksjni/compression_options.cc
|
||||
@@ -79,6 +80,7 @@ set(NATIVE_JAVA_CLASSES
|
||||
org.rocksdb.ColumnFamilyOptions
|
||||
org.rocksdb.CompactionOptionsFIFO
|
||||
org.rocksdb.CompactionOptionsUniversal
|
||||
org.rocksdb.CompactRangeOptions
|
||||
org.rocksdb.Comparator
|
||||
org.rocksdb.ComparatorOptions
|
||||
org.rocksdb.CompressionOptions
|
||||
@@ -192,6 +194,7 @@ add_jar(
|
||||
src/main/java/org/rocksdb/CompactionOptionsFIFO.java
|
||||
src/main/java/org/rocksdb/CompactionOptionsUniversal.java
|
||||
src/main/java/org/rocksdb/CompactionPriority.java
|
||||
src/main/java/org/rocksdb/CompactRangeOptions.java
|
||||
src/main/java/org/rocksdb/CompactionStopStyle.java
|
||||
src/main/java/org/rocksdb/CompactionStyle.java
|
||||
src/main/java/org/rocksdb/Comparator.java
|
||||
|
||||
@@ -14,6 +14,7 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\
|
||||
org.rocksdb.ColumnFamilyOptions\
|
||||
org.rocksdb.CompactionOptionsFIFO\
|
||||
org.rocksdb.CompactionOptionsUniversal\
|
||||
org.rocksdb.CompactRangeOptions\
|
||||
org.rocksdb.Comparator\
|
||||
org.rocksdb.ComparatorOptions\
|
||||
org.rocksdb.CompressionOptions\
|
||||
|
||||
@@ -26,6 +26,6 @@ export JAVA_HOME=/usr/lib/jvm/java-1.7.0
|
||||
# build rocksdb
|
||||
cd /rocksdb
|
||||
scl enable devtoolset-2 'make jclean clean'
|
||||
scl enable devtoolset-2 'PORTABLE=1 make rocksdbjavastatic'
|
||||
scl enable devtoolset-2 'PORTABLE=1 make -j8 rocksdbjavastatic'
|
||||
cp /rocksdb/java/target/librocksdbjni-* /rocksdb-build
|
||||
cp /rocksdb/java/target/rocksdbjni-* /rocksdb-build
|
||||
@@ -9,10 +9,10 @@ cd /rocksdb-local
|
||||
# Use scl devtoolset if available (i.e. CentOS <7)
|
||||
if hash scl 2>/dev/null; then
|
||||
scl enable devtoolset-2 'make jclean clean'
|
||||
scl enable devtoolset-2 'PORTABLE=1 make rocksdbjavastatic'
|
||||
scl enable devtoolset-2 'PORTABLE=1 make -j8 rocksdbjavastatic'
|
||||
else
|
||||
make jclean clean
|
||||
PORTABLE=1 make rocksdbjavastatic
|
||||
PORTABLE=1 make -j8 rocksdbjavastatic
|
||||
fi
|
||||
|
||||
cp java/target/librocksdbjni-linux*.so java/target/rocksdbjni-*-linux*.jar /rocksdb-host/java/target
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// This file implements the "bridge" between Java and C++ for
|
||||
// rocksdb::CompactRangeOptions.
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "include/org_rocksdb_CompactRangeOptions.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksjni/portal.h"
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: newCompactRangeOptions
|
||||
* Signature: ()J
|
||||
*/
|
||||
jlong Java_org_rocksdb_CompactRangeOptions_newCompactRangeOptions(
|
||||
JNIEnv* /*env*/, jclass /*jclazz*/) {
|
||||
auto* options = new rocksdb::CompactRangeOptions();
|
||||
return reinterpret_cast<jlong>(options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: exclusiveManualCompaction
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_CompactRangeOptions_exclusiveManualCompaction(
|
||||
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return static_cast<jboolean>(options->exclusive_manual_compaction);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setExclusiveManualCompaction
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setExclusiveManualCompaction(
|
||||
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, jboolean exclusive_manual_compaction) {
|
||||
auto* options =
|
||||
reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->exclusive_manual_compaction = static_cast<bool>(exclusive_manual_compaction);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: bottommostLevelCompaction
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_CompactRangeOptions_bottommostLevelCompaction(
|
||||
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return rocksdb::BottommostLevelCompactionJni::toJavaBottommostLevelCompaction(
|
||||
options->bottommost_level_compaction);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setBottommostLevelCompaction
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setBottommostLevelCompaction(
|
||||
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle,
|
||||
jint bottommost_level_compaction) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->bottommost_level_compaction =
|
||||
rocksdb::BottommostLevelCompactionJni::toCppBottommostLevelCompaction(bottommost_level_compaction);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: changeLevel
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_CompactRangeOptions_changeLevel
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return static_cast<jboolean>(options->change_level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setChangeLevel
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setChangeLevel
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, jboolean change_level) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->change_level = static_cast<bool>(change_level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: targetLevel
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_CompactRangeOptions_targetLevel
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return static_cast<jint>(options->target_level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setTargetLevel
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setTargetLevel
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, jint target_level) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->target_level = static_cast<int>(target_level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: targetPathId
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_CompactRangeOptions_targetPathId
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return static_cast<jint>(options->target_path_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setTargetPathId
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setTargetPathId
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, jint target_path_id) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->target_path_id = static_cast<uint32_t>(target_path_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: allowWriteStall
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_CompactRangeOptions_allowWriteStall
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return static_cast<jboolean>(options->allow_write_stall);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setAllowWriteStall
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setAllowWriteStall
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, jboolean allow_write_stall) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->allow_write_stall = static_cast<bool>(allow_write_stall);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: maxSubcompactions
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_CompactRangeOptions_maxSubcompactions
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
return static_cast<jint>(options->max_subcompactions);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: setMaxSubcompactions
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_setMaxSubcompactions
|
||||
(JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, jint max_subcompactions) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
options->max_subcompactions = static_cast<uint32_t>(max_subcompactions);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_CompactRangeOptions
|
||||
* Method: disposeInternal
|
||||
* Signature: (J)V
|
||||
*/
|
||||
void Java_org_rocksdb_CompactRangeOptions_disposeInternal(
|
||||
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle) {
|
||||
auto* options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jhandle);
|
||||
delete options;
|
||||
}
|
||||
@@ -2896,6 +2896,43 @@ class BatchResultJni : public JavaClass {
|
||||
}
|
||||
};
|
||||
|
||||
// The portal class for org.rocksdb.BottommostLevelCompaction
|
||||
class BottommostLevelCompactionJni {
|
||||
public:
|
||||
// Returns the equivalent org.rocksdb.BottommostLevelCompaction for the provided
|
||||
// C++ rocksdb::BottommostLevelCompaction enum
|
||||
static jint toJavaBottommostLevelCompaction(
|
||||
const rocksdb::BottommostLevelCompaction& bottommost_level_compaction) {
|
||||
switch(bottommost_level_compaction) {
|
||||
case rocksdb::BottommostLevelCompaction::kSkip:
|
||||
return 0x0;
|
||||
case rocksdb::BottommostLevelCompaction::kIfHaveCompactionFilter:
|
||||
return 0x1;
|
||||
case rocksdb::BottommostLevelCompaction::kForce:
|
||||
return 0x2;
|
||||
default:
|
||||
return 0x7F; // undefined
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the equivalent C++ rocksdb::BottommostLevelCompaction enum for the
|
||||
// provided Java org.rocksdb.BottommostLevelCompaction
|
||||
static rocksdb::BottommostLevelCompaction toCppBottommostLevelCompaction(
|
||||
jint bottommost_level_compaction) {
|
||||
switch(bottommost_level_compaction) {
|
||||
case 0x0:
|
||||
return rocksdb::BottommostLevelCompaction::kSkip;
|
||||
case 0x1:
|
||||
return rocksdb::BottommostLevelCompaction::kIfHaveCompactionFilter;
|
||||
case 0x2:
|
||||
return rocksdb::BottommostLevelCompaction::kForce;
|
||||
default:
|
||||
// undefined/default
|
||||
return rocksdb::BottommostLevelCompaction::kIfHaveCompactionFilter;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The portal class for org.rocksdb.CompactionStopStyle
|
||||
class CompactionStopStyleJni {
|
||||
public:
|
||||
|
||||
@@ -1955,8 +1955,7 @@ bool rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db,
|
||||
rocksdb::ColumnFamilyHandle* cf_handle,
|
||||
jbyteArray jbegin, jint jbegin_len,
|
||||
jbyteArray jend, jint jend_len,
|
||||
jboolean jreduce_level, jint jtarget_level,
|
||||
jint jtarget_path_id) {
|
||||
const rocksdb::CompactRangeOptions& compact_options) {
|
||||
jbyte* begin = env->GetByteArrayElements(jbegin, nullptr);
|
||||
if (begin == nullptr) {
|
||||
// exception thrown: OutOfMemoryError
|
||||
@@ -1974,10 +1973,6 @@ bool rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db,
|
||||
const rocksdb::Slice end_slice(reinterpret_cast<char*>(end), jend_len);
|
||||
|
||||
rocksdb::Status s;
|
||||
rocksdb::CompactRangeOptions compact_options;
|
||||
compact_options.change_level = jreduce_level;
|
||||
compact_options.target_level = jtarget_level;
|
||||
compact_options.target_path_id = static_cast<uint32_t>(jtarget_path_id);
|
||||
if (cf_handle != nullptr) {
|
||||
s = db->CompactRange(compact_options, cf_handle, &begin_slice, &end_slice);
|
||||
} else {
|
||||
@@ -1996,6 +1991,25 @@ bool rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db,
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the compact range succeeded, false if a Java Exception
|
||||
* was thrown
|
||||
*/
|
||||
bool rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db,
|
||||
rocksdb::ColumnFamilyHandle* cf_handle,
|
||||
jbyteArray jbegin, jint jbegin_len,
|
||||
jbyteArray jend, jint jend_len,
|
||||
jboolean jreduce_level, jint jtarget_level,
|
||||
jint jtarget_path_id) {
|
||||
rocksdb::CompactRangeOptions compact_options;
|
||||
compact_options.change_level = jreduce_level;
|
||||
compact_options.target_level = jtarget_level;
|
||||
compact_options.target_path_id = static_cast<uint32_t>(jtarget_path_id);
|
||||
|
||||
return rocksdb_compactrange_helper(env, db, cf_handle, jbegin, jbegin_len,
|
||||
jend, jend_len, compact_options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_RocksDB
|
||||
* Method: compactRange0
|
||||
@@ -2027,6 +2041,20 @@ void Java_org_rocksdb_RocksDB_compactRange__J_3BI_3BIZIIJ(
|
||||
jtarget_path_id);
|
||||
}
|
||||
|
||||
|
||||
void Java_org_rocksdb_RocksDB_compactRange__J_3BI_3BIJJ(
|
||||
JNIEnv* env, jobject /*jdb*/, jlong jdb_handle, jbyteArray jbegin,
|
||||
jint jbegin_len, jbyteArray jend, jint jend_len,
|
||||
jlong jcompact_options_handle, jlong jcf_handle) {
|
||||
auto* db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
|
||||
auto* cf_handle = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jcf_handle);
|
||||
auto* compact_options = reinterpret_cast<rocksdb::CompactRangeOptions*>(jcompact_options_handle);
|
||||
|
||||
rocksdb_compactrange_helper(env, db, cf_handle, jbegin, jbegin_len, jend,
|
||||
jend_len, *compact_options);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// rocksdb::DB::PauseBackgroundWork
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ using namespace std::placeholders;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4503) // identifier' : decorated name length exceeded, name was truncated
|
||||
#pragma warning(disable : 4503) // identifier' : decorated name length
|
||||
// exceeded, name was truncated
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -271,8 +272,8 @@ typedef std::function<std::vector<rocksdb::Status>(
|
||||
|
||||
void free_parts(
|
||||
JNIEnv* env,
|
||||
std::vector<std::tuple<jbyteArray, jbyte*, jobject>> &parts_to_free) {
|
||||
for (auto &value : parts_to_free) {
|
||||
std::vector<std::tuple<jbyteArray, jbyte*, jobject>>& parts_to_free) {
|
||||
for (auto& value : parts_to_free) {
|
||||
jobject jk;
|
||||
jbyteArray jk_ba;
|
||||
jbyte* jk_val;
|
||||
@@ -675,10 +676,10 @@ void txn_write_kv_parts_helper(JNIEnv* env,
|
||||
return;
|
||||
}
|
||||
|
||||
jparts_to_free.push_back(std::make_tuple(
|
||||
jba_key_part, jkey_part, jobj_key_part));
|
||||
jparts_to_free.push_back(std::make_tuple(
|
||||
jba_value_part, jvalue_part, jobj_value_part));
|
||||
jparts_to_free.push_back(
|
||||
std::make_tuple(jba_key_part, jkey_part, jobj_key_part));
|
||||
jparts_to_free.push_back(
|
||||
std::make_tuple(jba_value_part, jvalue_part, jobj_value_part));
|
||||
|
||||
key_parts.push_back(
|
||||
rocksdb::Slice(reinterpret_cast<char*>(jkey_part), jkey_part_len));
|
||||
@@ -688,8 +689,8 @@ void txn_write_kv_parts_helper(JNIEnv* env,
|
||||
|
||||
// call the write_multi function
|
||||
rocksdb::Status s = fn_write_kv_parts(
|
||||
rocksdb::SliceParts(key_parts.data(), (int)key_parts.size()),
|
||||
rocksdb::SliceParts(value_parts.data(), (int)value_parts.size()));
|
||||
rocksdb::SliceParts(key_parts.data(), (int)key_parts.size()),
|
||||
rocksdb::SliceParts(value_parts.data(), (int)value_parts.size()));
|
||||
|
||||
// cleanup temporary memory
|
||||
free_parts(env, jparts_to_free);
|
||||
@@ -834,13 +835,11 @@ void Java_org_rocksdb_Transaction_delete__J_3BI(JNIEnv* env, jobject /*jobj*/,
|
||||
typedef std::function<rocksdb::Status(const rocksdb::SliceParts&)>
|
||||
FnWriteKParts;
|
||||
|
||||
|
||||
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
|
||||
void txn_write_k_parts_helper(JNIEnv* env,
|
||||
const FnWriteKParts& fn_write_k_parts,
|
||||
const jobjectArray& jkey_parts,
|
||||
const jint& jkey_parts_len) {
|
||||
|
||||
std::vector<rocksdb::Slice> key_parts;
|
||||
std::vector<std::tuple<jbyteArray, jbyte*, jobject>> jkey_parts_to_free;
|
||||
|
||||
@@ -872,12 +871,13 @@ void txn_write_k_parts_helper(JNIEnv* env,
|
||||
jkey_parts_to_free.push_back(std::tuple<jbyteArray, jbyte*, jobject>(
|
||||
jba_key_part, jkey_part, jobj_key_part));
|
||||
|
||||
key_parts.push_back(rocksdb::Slice(reinterpret_cast<char*>(jkey_part), jkey_part_len));
|
||||
key_parts.push_back(
|
||||
rocksdb::Slice(reinterpret_cast<char*>(jkey_part), jkey_part_len));
|
||||
}
|
||||
|
||||
// call the write_multi function
|
||||
rocksdb::Status s =
|
||||
fn_write_k_parts(rocksdb::SliceParts(key_parts.data(), (int)key_parts.size()));
|
||||
rocksdb::Status s = fn_write_k_parts(
|
||||
rocksdb::SliceParts(key_parts.data(), (int)key_parts.size()));
|
||||
|
||||
// cleanup temporary memory
|
||||
free_parts(env, jkey_parts_to_free);
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
// 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).
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* CompactRangeOptions is used by CompactRange() call. In the documentation of the methods "the compaction" refers to
|
||||
* any compaction that is using this CompactRangeOptions.
|
||||
*/
|
||||
public class CompactRangeOptions extends RocksObject {
|
||||
|
||||
private final static byte VALUE_kSkip = 0;
|
||||
private final static byte VALUE_kIfHaveCompactionFilter = 1;
|
||||
private final static byte VALUE_kForce = 2;
|
||||
|
||||
// For level based compaction, we can configure if we want to skip/force bottommost level compaction.
|
||||
// The order of this neum MUST follow the C++ layer. See BottommostLevelCompaction in db/options.h
|
||||
public enum BottommostLevelCompaction {
|
||||
/**
|
||||
* Skip bottommost level compaction
|
||||
*/
|
||||
kSkip((byte)VALUE_kSkip),
|
||||
/**
|
||||
* Only compact bottommost level if there is a compaction filter. This is the default option
|
||||
*/
|
||||
kIfHaveCompactionFilter(VALUE_kIfHaveCompactionFilter),
|
||||
/**
|
||||
* Always compact bottommost level
|
||||
*/
|
||||
kForce(VALUE_kForce);
|
||||
|
||||
private final byte value;
|
||||
|
||||
BottommostLevelCompaction(final byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the byte value of the enumerations value.</p>
|
||||
*
|
||||
* @return byte representation
|
||||
*/
|
||||
public byte getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BottommostLevelCompaction for the given C++ rocks enum value.
|
||||
* @param bottommostLevelCompaction The value of the BottommostLevelCompaction
|
||||
* @return BottommostLevelCompaction instance, or null if none matches
|
||||
*/
|
||||
public static BottommostLevelCompaction fromRocksId(final int bottommostLevelCompaction) {
|
||||
switch (bottommostLevelCompaction) {
|
||||
case VALUE_kSkip: return kSkip;
|
||||
case VALUE_kIfHaveCompactionFilter: return kIfHaveCompactionFilter;
|
||||
case VALUE_kForce: return kForce;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct CompactRangeOptions.
|
||||
*/
|
||||
public CompactRangeOptions() {
|
||||
super(newCompactRangeOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the compaction is exclusive or other compactions may run concurrently at the same time.
|
||||
*
|
||||
* @return true if exclusive, false if concurrent
|
||||
*/
|
||||
public boolean exclusiveManualCompaction() {
|
||||
return exclusiveManualCompaction(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the compaction is exclusive or other compaction are allowed run concurrently at the same time.
|
||||
*
|
||||
* @param exclusiveCompaction true if compaction should be exclusive
|
||||
* @return This CompactRangeOptions
|
||||
*/
|
||||
public CompactRangeOptions setExclusiveManualCompaction(final boolean exclusiveCompaction) {
|
||||
setExclusiveManualCompaction(nativeHandle_, exclusiveCompaction);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the policy for compacting the bottommost level
|
||||
* @return The BottommostLevelCompaction policy
|
||||
*/
|
||||
public BottommostLevelCompaction bottommostLevelCompaction() {
|
||||
return BottommostLevelCompaction.fromRocksId(bottommostLevelCompaction(nativeHandle_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the policy for compacting the bottommost level
|
||||
*
|
||||
* @param bottommostLevelCompaction The policy for compacting the bottommost level
|
||||
* @return This CompactRangeOptions
|
||||
*/
|
||||
public CompactRangeOptions setBottommostLevelCompaction(final BottommostLevelCompaction bottommostLevelCompaction) {
|
||||
setBottommostLevelCompaction(nativeHandle_, bottommostLevelCompaction.getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether compacted files will be moved to the minimum level capable of holding the data or given level
|
||||
* (specified non-negative target_level).
|
||||
* @return true, if compacted files will be moved to the minimum level
|
||||
*/
|
||||
public boolean changeLevel() {
|
||||
return changeLevel(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether compacted files will be moved to the minimum level capable of holding the data or given level
|
||||
* (specified non-negative target_level).
|
||||
*
|
||||
* @param changeLevel If true, compacted files will be moved to the minimum level
|
||||
* @return This CompactRangeOptions
|
||||
*/
|
||||
public CompactRangeOptions setChangeLevel(final boolean changeLevel) {
|
||||
setChangeLevel(nativeHandle_, changeLevel);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If change_level is true and target_level have non-negative value, compacted files will be moved to target_level.
|
||||
* @return The target level for the compacted files
|
||||
*/
|
||||
public int targetLevel() {
|
||||
return targetLevel(nativeHandle_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If change_level is true and target_level have non-negative value, compacted files will be moved to target_level.
|
||||
*
|
||||
* @param targetLevel target level for the compacted files
|
||||
* @return This CompactRangeOptions
|
||||
*/
|
||||
public CompactRangeOptions setTargetLevel(final int targetLevel) {
|
||||
setTargetLevel(nativeHandle_, targetLevel);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* target_path_id for compaction output. Compaction outputs will be placed in options.db_paths[target_path_id].
|
||||
*
|
||||
* @return target_path_id
|
||||
*/
|
||||
public int targetPathId() {
|
||||
return targetPathId(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compaction outputs will be placed in options.db_paths[target_path_id]. Behavior is undefined if target_path_id is
|
||||
* out of range.
|
||||
*
|
||||
* @param targetPathId target path id
|
||||
* @return This CompactRangeOptions
|
||||
*/
|
||||
public CompactRangeOptions setTargetPathId(final int targetPathId) {
|
||||
setTargetPathId(nativeHandle_, targetPathId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, compaction will execute immediately even if doing so would cause the DB to
|
||||
* enter write stall mode. Otherwise, it'll sleep until load is low enough.
|
||||
* @return true if compaction will execute immediately
|
||||
*/
|
||||
public boolean allowWriteStall() {
|
||||
return allowWriteStall(nativeHandle_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If true, compaction will execute immediately even if doing so would cause the DB to
|
||||
* enter write stall mode. Otherwise, it'll sleep until load is low enough.
|
||||
*
|
||||
* @return This CompactRangeOptions
|
||||
* @param allowWriteStall true if compaction should execute immediately
|
||||
*/
|
||||
public CompactRangeOptions setAllowWriteStall(final boolean allowWriteStall) {
|
||||
setAllowWriteStall(nativeHandle_, allowWriteStall);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If > 0, it will replace the option in the DBOptions for this compaction
|
||||
* @return number of subcompactions
|
||||
*/
|
||||
public int maxSubcompactions() {
|
||||
return maxSubcompactions(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* If > 0, it will replace the option in the DBOptions for this compaction
|
||||
*
|
||||
* @param maxSubcompactions number of subcompactions
|
||||
* @return This CompactRangeOptions
|
||||
*/
|
||||
public CompactRangeOptions setMaxSubcompactions(final int maxSubcompactions) {
|
||||
setMaxSubcompactions(nativeHandle_, maxSubcompactions);
|
||||
return this;
|
||||
}
|
||||
|
||||
private native static long newCompactRangeOptions();
|
||||
private native boolean exclusiveManualCompaction(final long handle);
|
||||
private native void setExclusiveManualCompaction(final long handle, final boolean exclusive_manual_compaction);
|
||||
private native int bottommostLevelCompaction(final long handle);
|
||||
private native void setBottommostLevelCompaction(final long handle, final int bottommostLevelCompaction);
|
||||
private native boolean changeLevel(final long handle);
|
||||
private native void setChangeLevel(final long handle, final boolean changeLevel);
|
||||
private native int targetLevel(final long handle);
|
||||
private native void setTargetLevel(final long handle, final int targetLevel);
|
||||
private native int targetPathId(final long handle);
|
||||
private native void setTargetPathId(final long handle, final int /* uint32_t */ targetPathId);
|
||||
private native boolean allowWriteStall(final long handle);
|
||||
private native void setAllowWriteStall(final long handle, final boolean allowWriteStall);
|
||||
private native void setMaxSubcompactions(final long handle, final int /* uint32_t */ maxSubcompactions);
|
||||
private native int maxSubcompactions(final long handle);
|
||||
|
||||
@Override
|
||||
protected final native void disposeInternal(final long handle);
|
||||
|
||||
}
|
||||
@@ -262,6 +262,8 @@ public interface DBOptionsInterface<T extends DBOptionsInterface> {
|
||||
* </p>
|
||||
* <p>If set to 0 (default), we will dynamically choose the WAL size limit to
|
||||
* be [sum of all write_buffer_size * max_write_buffer_number] * 2</p>
|
||||
* <p>This option takes effect only when there are more than one column family as
|
||||
* otherwise the wal size is dictated by the write_buffer_size.</p>
|
||||
* <p>Default: 0</p>
|
||||
*
|
||||
* @param maxTotalWalSize max total wal size.
|
||||
|
||||
@@ -1823,6 +1823,8 @@ public class RocksDB extends RocksObject {
|
||||
* <li>{@link #compactRange(byte[], byte[], boolean, int, int)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @deprecated Use {@link #compactRange(ColumnFamilyHandle, byte[], byte[], CompactRangeOptions)} instead
|
||||
*
|
||||
* @param reduce_level reduce level after compaction
|
||||
* @param target_level target level to compact to
|
||||
* @param target_path_id the target path id of output path
|
||||
@@ -1830,6 +1832,7 @@ public class RocksDB extends RocksObject {
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
@Deprecated
|
||||
public void compactRange(final boolean reduce_level,
|
||||
final int target_level, final int target_path_id)
|
||||
throws RocksDBException {
|
||||
@@ -1855,6 +1858,8 @@ public class RocksDB extends RocksObject {
|
||||
* <li>{@link #compactRange(byte[], byte[])}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @deprecated Use {@link #compactRange(ColumnFamilyHandle, byte[], byte[], CompactRangeOptions)} instead
|
||||
*
|
||||
* @param begin start of key range (included in range)
|
||||
* @param end end of key range (excluded from range)
|
||||
* @param reduce_level reduce level after compaction
|
||||
@@ -1864,6 +1869,7 @@ public class RocksDB extends RocksObject {
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
@Deprecated
|
||||
public void compactRange(final byte[] begin, final byte[] end,
|
||||
final boolean reduce_level, final int target_level,
|
||||
final int target_path_id) throws RocksDBException {
|
||||
@@ -1935,6 +1941,27 @@ public class RocksDB extends RocksObject {
|
||||
false, -1, 0, columnFamilyHandle.nativeHandle_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Range compaction of column family.</p>
|
||||
* <p><strong>Note</strong>: After the database has been compacted,
|
||||
* all data will have been pushed down to the last level containing
|
||||
* any data.</p>
|
||||
*
|
||||
* @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle} instance.
|
||||
* @param begin start of key range (included in range)
|
||||
* @param end end of key range (excluded from range)
|
||||
* @param compactRangeOptions options for the compaction
|
||||
*
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
public void compactRange(final ColumnFamilyHandle columnFamilyHandle,
|
||||
final byte[] begin, final byte[] end, CompactRangeOptions compactRangeOptions) throws RocksDBException {
|
||||
compactRange(nativeHandle_, begin, begin.length, end, end.length,
|
||||
compactRangeOptions.nativeHandle_, columnFamilyHandle.nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Range compaction of column family.</p>
|
||||
* <p><strong>Note</strong>: After the database has been compacted,
|
||||
@@ -1957,6 +1984,8 @@ public class RocksDB extends RocksObject {
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @deprecated Use {@link #compactRange(ColumnFamilyHandle, byte[], byte[], CompactRangeOptions)} instead
|
||||
*
|
||||
* @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle}
|
||||
* instance.
|
||||
* @param reduce_level reduce level after compaction
|
||||
@@ -1966,6 +1995,7 @@ public class RocksDB extends RocksObject {
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
@Deprecated
|
||||
public void compactRange(final ColumnFamilyHandle columnFamilyHandle,
|
||||
final boolean reduce_level, final int target_level,
|
||||
final int target_path_id) throws RocksDBException {
|
||||
@@ -1994,6 +2024,8 @@ public class RocksDB extends RocksObject {
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @deprecated Use {@link #compactRange(ColumnFamilyHandle, byte[], byte[], CompactRangeOptions)} instead
|
||||
*
|
||||
* @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle}
|
||||
* instance.
|
||||
* @param begin start of key range (included in range)
|
||||
@@ -2005,6 +2037,7 @@ public class RocksDB extends RocksObject {
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
@Deprecated
|
||||
public void compactRange(final ColumnFamilyHandle columnFamilyHandle,
|
||||
final byte[] begin, final byte[] end, final boolean reduce_level,
|
||||
final int target_level, final int target_path_id)
|
||||
@@ -2377,6 +2410,9 @@ public class RocksDB extends RocksObject {
|
||||
private native void compactRange0(long handle, byte[] begin, int beginLen,
|
||||
byte[] end, int endLen, boolean reduce_level, int target_level,
|
||||
int target_path_id) throws RocksDBException;
|
||||
private native void compactRange(long handle, byte[] begin, int beginLen,
|
||||
byte[] end, int endLen, long compactRangeOptHandle, long cfHandle)
|
||||
throws RocksDBException;
|
||||
private native void compactRange(long handle, boolean reduce_level,
|
||||
int target_level, int target_path_id, long cfHandle)
|
||||
throws RocksDBException;
|
||||
|
||||
@@ -686,13 +686,12 @@ public abstract class AbstractTransactionTest {
|
||||
@Test
|
||||
public void elapsedTime() throws RocksDBException, InterruptedException {
|
||||
final long preStartTxnTime = System.currentTimeMillis();
|
||||
try(final DBContainer dbContainer = startDb();
|
||||
final Transaction txn = dbContainer.beginTransaction()) {
|
||||
try (final DBContainer dbContainer = startDb();
|
||||
final Transaction txn = dbContainer.beginTransaction()) {
|
||||
Thread.sleep(2);
|
||||
|
||||
final long txnElapsedTime = txn.getElapsedTime();
|
||||
assertThat(txnElapsedTime).isLessThan(System.currentTimeMillis()
|
||||
- preStartTxnTime);
|
||||
assertThat(txnElapsedTime).isLessThan(System.currentTimeMillis() - preStartTxnTime);
|
||||
assertThat(txnElapsedTime).isGreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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).
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.rocksdb.CompactRangeOptions.BottommostLevelCompaction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CompactRangeOptionsTest {
|
||||
|
||||
static {
|
||||
RocksDB.loadLibrary();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exclusiveManualCompaction() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
boolean value = false;
|
||||
opt.setExclusiveManualCompaction(value);
|
||||
assertThat(opt.exclusiveManualCompaction()).isEqualTo(value);
|
||||
value = true;
|
||||
opt.setExclusiveManualCompaction(value);
|
||||
assertThat(opt.exclusiveManualCompaction()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bottommostLevelCompaction() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
BottommostLevelCompaction value = BottommostLevelCompaction.kSkip;
|
||||
opt.setBottommostLevelCompaction(value);
|
||||
assertThat(opt.bottommostLevelCompaction()).isEqualTo(value);
|
||||
value = BottommostLevelCompaction.kForce;
|
||||
opt.setBottommostLevelCompaction(value);
|
||||
assertThat(opt.bottommostLevelCompaction()).isEqualTo(value);
|
||||
value = BottommostLevelCompaction.kIfHaveCompactionFilter;
|
||||
opt.setBottommostLevelCompaction(value);
|
||||
assertThat(opt.bottommostLevelCompaction()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeLevel() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
boolean value = false;
|
||||
opt.setChangeLevel(value);
|
||||
assertThat(opt.changeLevel()).isEqualTo(value);
|
||||
value = true;
|
||||
opt.setChangeLevel(value);
|
||||
assertThat(opt.changeLevel()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void targetLevel() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
int value = 2;
|
||||
opt.setTargetLevel(value);
|
||||
assertThat(opt.targetLevel()).isEqualTo(value);
|
||||
value = 3;
|
||||
opt.setTargetLevel(value);
|
||||
assertThat(opt.targetLevel()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void targetPathId() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
int value = 2;
|
||||
opt.setTargetPathId(value);
|
||||
assertThat(opt.targetPathId()).isEqualTo(value);
|
||||
value = 3;
|
||||
opt.setTargetPathId(value);
|
||||
assertThat(opt.targetPathId()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowWriteStall() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
boolean value = false;
|
||||
opt.setAllowWriteStall(value);
|
||||
assertThat(opt.allowWriteStall()).isEqualTo(value);
|
||||
value = true;
|
||||
opt.setAllowWriteStall(value);
|
||||
assertThat(opt.allowWriteStall()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxSubcompactions() {
|
||||
CompactRangeOptions opt = new CompactRangeOptions();
|
||||
int value = 2;
|
||||
opt.setMaxSubcompactions(value);
|
||||
assertThat(opt.maxSubcompactions()).isEqualTo(value);
|
||||
value = 3;
|
||||
opt.setMaxSubcompactions(value);
|
||||
assertThat(opt.maxSubcompactions()).isEqualTo(value);
|
||||
}
|
||||
}
|
||||
@@ -479,6 +479,9 @@ ColumnFamilyOptions* ColumnFamilyOptions::OptimizeForPointLookup(
|
||||
prefix_extractor.reset(NewNoopTransform());
|
||||
BlockBasedTableOptions block_based_options;
|
||||
block_based_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
block_based_options.data_block_index_type =
|
||||
BlockBasedTableOptions::kDataBlockBinaryAndHash;
|
||||
block_based_options.data_block_hash_table_util_ratio = 0.75;
|
||||
block_based_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
block_based_options.block_cache =
|
||||
NewLRUCache(static_cast<size_t>(block_cache_size_mb * 1024 * 1024));
|
||||
|
||||
@@ -494,6 +494,11 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type,
|
||||
return ParseEnum<BlockBasedTableOptions::IndexType>(
|
||||
block_base_table_index_type_string_map, value,
|
||||
reinterpret_cast<BlockBasedTableOptions::IndexType*>(opt_address));
|
||||
case OptionType::kBlockBasedTableDataBlockIndexType:
|
||||
return ParseEnum<BlockBasedTableOptions::DataBlockIndexType>(
|
||||
block_base_table_data_block_index_type_string_map, value,
|
||||
reinterpret_cast<BlockBasedTableOptions::DataBlockIndexType*>(
|
||||
opt_address));
|
||||
case OptionType::kEncodingType:
|
||||
return ParseEnum<EncodingType>(
|
||||
encoding_type_string_map, value,
|
||||
@@ -673,6 +678,12 @@ bool SerializeSingleOptionHelper(const char* opt_address,
|
||||
*reinterpret_cast<const BlockBasedTableOptions::IndexType*>(
|
||||
opt_address),
|
||||
value);
|
||||
case OptionType::kBlockBasedTableDataBlockIndexType:
|
||||
return SerializeEnum<BlockBasedTableOptions::DataBlockIndexType>(
|
||||
block_base_table_data_block_index_type_string_map,
|
||||
*reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType*>(
|
||||
opt_address),
|
||||
value);
|
||||
case OptionType::kFlushBlockPolicyFactory: {
|
||||
const auto* ptr =
|
||||
reinterpret_cast<const std::shared_ptr<FlushBlockPolicyFactory>*>(
|
||||
@@ -1552,6 +1563,13 @@ std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
{"kTwoLevelIndexSearch",
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch}};
|
||||
|
||||
std::unordered_map<std::string, BlockBasedTableOptions::DataBlockIndexType>
|
||||
OptionsHelper::block_base_table_data_block_index_type_string_map = {
|
||||
{"kDataBlockBinarySearch",
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch},
|
||||
{"kDataBlockBinaryAndHash",
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinaryAndHash}};
|
||||
|
||||
std::unordered_map<std::string, EncodingType>
|
||||
OptionsHelper::encoding_type_string_map = {{"kPlain", kPlain},
|
||||
{"kPrefix", kPrefix}};
|
||||
|
||||
@@ -69,6 +69,7 @@ enum class OptionType {
|
||||
kMergeOperator,
|
||||
kMemTableRepFactory,
|
||||
kBlockBasedTableIndexType,
|
||||
kBlockBasedTableDataBlockIndexType,
|
||||
kFilterPolicy,
|
||||
kFlushBlockPolicyFactory,
|
||||
kChecksumType,
|
||||
@@ -163,6 +164,9 @@ struct OptionsHelper {
|
||||
lru_cache_options_type_info;
|
||||
static std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
block_base_table_index_type_string_map;
|
||||
static std::unordered_map<std::string,
|
||||
BlockBasedTableOptions::DataBlockIndexType>
|
||||
block_base_table_data_block_index_type_string_map;
|
||||
static std::unordered_map<std::string, EncodingType> encoding_type_string_map;
|
||||
static std::unordered_map<std::string, CompactionStyle>
|
||||
compaction_style_string_map;
|
||||
@@ -203,6 +207,8 @@ static auto& compression_type_string_map =
|
||||
OptionsHelper::compression_type_string_map;
|
||||
static auto& block_base_table_index_type_string_map =
|
||||
OptionsHelper::block_base_table_index_type_string_map;
|
||||
static auto& block_base_table_data_block_index_type_string_map =
|
||||
OptionsHelper::block_base_table_data_block_index_type_string_map;
|
||||
static auto& encoding_type_string_map = OptionsHelper::encoding_type_string_map;
|
||||
static auto& compaction_style_string_map =
|
||||
OptionsHelper::compaction_style_string_map;
|
||||
|
||||
@@ -200,45 +200,6 @@ Status RocksDBOptionsParser::ParseStatement(std::string* name,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ReadOneLine(std::istringstream* iss, SequentialFile* seq_file,
|
||||
std::string* output, bool* has_data, Status* result) {
|
||||
const int kBufferSize = 8192;
|
||||
char buffer[kBufferSize + 1];
|
||||
Slice input_slice;
|
||||
|
||||
std::string line;
|
||||
bool has_complete_line = false;
|
||||
while (!has_complete_line) {
|
||||
if (std::getline(*iss, line)) {
|
||||
has_complete_line = !iss->eof();
|
||||
} else {
|
||||
has_complete_line = false;
|
||||
}
|
||||
if (!has_complete_line) {
|
||||
// if we're not sure whether we have a complete line,
|
||||
// further read from the file.
|
||||
if (*has_data) {
|
||||
*result = seq_file->Read(kBufferSize, &input_slice, buffer);
|
||||
}
|
||||
if (input_slice.size() == 0) {
|
||||
// meaning we have read all the data
|
||||
*has_data = false;
|
||||
break;
|
||||
} else {
|
||||
iss->str(line + input_slice.ToString());
|
||||
// reset the internal state of iss so that we can keep reading it.
|
||||
iss->clear();
|
||||
*has_data = (input_slice.size() == kBufferSize);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
*output = line;
|
||||
return *has_data || has_complete_line;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status RocksDBOptionsParser::Parse(const std::string& file_name, Env* env,
|
||||
bool ignore_unknown_options) {
|
||||
Reset();
|
||||
@@ -592,6 +553,12 @@ bool AreEqualOptions(
|
||||
*reinterpret_cast<const BlockBasedTableOptions::IndexType*>(
|
||||
offset1) ==
|
||||
*reinterpret_cast<const BlockBasedTableOptions::IndexType*>(offset2));
|
||||
case OptionType::kBlockBasedTableDataBlockIndexType:
|
||||
return (
|
||||
*reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType*>(
|
||||
offset1) ==
|
||||
*reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType*>(
|
||||
offset2));
|
||||
case OptionType::kWALRecoveryMode:
|
||||
return (*reinterpret_cast<const WALRecoveryMode*>(offset1) ==
|
||||
*reinterpret_cast<const WALRecoveryMode*>(offset2));
|
||||
|
||||
@@ -142,6 +142,8 @@ TEST_F(OptionsSettableTest, BlockBasedTableOptionsAllFieldsSettable) {
|
||||
"pin_l0_filter_and_index_blocks_in_cache=1;"
|
||||
"pin_top_level_index_and_filter=1;"
|
||||
"index_type=kHashSearch;"
|
||||
"data_block_index_type=kDataBlockBinaryAndHash;"
|
||||
"data_block_hash_table_util_ratio=0.75;"
|
||||
"checksum=kxxHash;hash_index_allow_collision=1;no_block_cache=1;"
|
||||
"block_cache=1M;block_cache_compressed=1k;block_size=1024;"
|
||||
"block_size_deviation=8;block_restart_interval=4; "
|
||||
|
||||
Loaded 100 of 231 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user