mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba02251a7c | ||
|
|
628a7fd74b | ||
|
|
fa1c26dc0c | ||
|
|
f9fc8c627e | ||
|
|
662b8febcb | ||
|
|
37eb632072 | ||
|
|
bb2eb06720 | ||
|
|
e36748cbc7 | ||
|
|
524609d2b1 | ||
|
|
a1f9583dab | ||
|
|
ffbcc0d132 | ||
|
|
42c61ae1e7 | ||
|
|
50eea98ca3 | ||
|
|
c4f5d0aa15 | ||
|
|
f1be6aeb1c | ||
|
|
48c8d8445e | ||
|
|
cf98df34c1 | ||
|
|
0f4244fe00 | ||
|
|
bcdc8c8b19 | ||
|
|
93f7e7a450 | ||
|
|
7d23210226 | ||
|
|
3c5d1b16b1 | ||
|
|
ed995c6a69 | ||
|
|
c2affccc18 | ||
|
|
26a33ee5bd | ||
|
|
4db46aa2e6 | ||
|
|
3231a2e581 | ||
|
|
ca89ac2ba9 | ||
|
|
c8c8104d7e | ||
|
|
4fc442029a | ||
|
|
34b55dd8c0 | ||
|
|
51a90415e2 | ||
|
|
5af9446ee6 | ||
|
|
a69d4deefb | ||
|
|
33b33235ff | ||
|
|
49ddd7ec4f | ||
|
|
eafb09a380 | ||
|
|
c5a64cffd2 | ||
|
|
62f70f6d14 | ||
|
|
79496d71ed | ||
|
|
d6b9b3b884 | ||
|
|
576d2d6c60 | ||
|
|
1218704b61 | ||
|
|
9144d1f186 | ||
|
|
2d049ab7e8 | ||
|
|
5d9a623e2c | ||
|
|
08809f5e6c | ||
|
|
10d14693ac | ||
|
|
39fb88f14e | ||
|
|
1a761e6a6c | ||
|
|
f48758e939 | ||
|
|
cf3a671733 | ||
|
|
199fabc197 | ||
|
|
d9c9f3c809 | ||
|
|
71cae59a99 | ||
|
|
00ed41daee | ||
|
|
c9a52cbdc8 | ||
|
|
8fe073324f | ||
|
|
31221bb7e8 | ||
|
|
dcb73e7735 | ||
|
|
30468d8eb4 | ||
|
|
59244447e3 | ||
|
|
4091597c67 | ||
|
|
842cdc11dd | ||
|
|
0ea57115a3 | ||
|
|
35e5689e11 | ||
|
|
32a6dd9a41 | ||
|
|
158da7a6ee | ||
|
|
d0d484b132 | ||
|
|
95604d13e9 | ||
|
|
4978caaa6f | ||
|
|
ee1818081f | ||
|
|
e2547103fd | ||
|
|
e242fa4664 | ||
|
|
bc7d1661a8 | ||
|
|
f184bee77b | ||
|
|
e1de88c8c7 | ||
|
|
fc53839bfa | ||
|
|
e07aa8669d | ||
|
|
8ec3e72551 | ||
|
|
43defe9872 | ||
|
|
d94aa2f7db | ||
|
|
27054d837b | ||
|
|
0cead31d10 | ||
|
|
5bf941966b | ||
|
|
1eded07f00 | ||
|
|
cbe0239270 | ||
|
|
08b8cea69f | ||
|
|
b2ba06856a | ||
|
|
8189c184ec | ||
|
|
ce8e88d2d7 | ||
|
|
16a5ac5b69 | ||
|
|
01013ae766 | ||
|
|
b1ad6ebba8 | ||
|
|
7fd9813b9f | ||
|
|
73ff15c07b | ||
|
|
dd9eca1c58 | ||
|
|
3cfc7515fc | ||
|
|
128f532858 | ||
|
|
e79df377c5 | ||
|
|
5d4fddfa52 | ||
|
|
cad99a6031 | ||
|
|
7d13f307ff | ||
|
|
55e03b67df | ||
|
|
d50c10ed37 | ||
|
|
301da345ae | ||
|
|
4e37251b4d | ||
|
|
856ac24484 | ||
|
|
6a4ec41fed | ||
|
|
349c7cceff | ||
|
|
1fb2e274c5 | ||
|
|
d56ac22b44 | ||
|
|
75714b4c08 | ||
|
|
ffc9f84649 | ||
|
|
f3a99e8a4d | ||
|
|
8641e9adf7 | ||
|
|
cd227d74ba | ||
|
|
e686caffec | ||
|
|
0ed98bf89e | ||
|
|
42135523a0 | ||
|
|
a3ab59d030 | ||
|
|
cf852fdf55 | ||
|
|
9e2c804fe6 | ||
|
|
a07175af65 | ||
|
|
77a8d4d476 | ||
|
|
8c79f79208 | ||
|
|
74f7d7551e | ||
|
|
e4feb78606 | ||
|
|
073285363e | ||
|
|
565b5bdc42 | ||
|
|
ace543a815 | ||
|
|
e8210e44da | ||
|
|
b9d6eccac1 | ||
|
|
7d65bd5ce4 | ||
|
|
f8d5c1b012 | ||
|
|
ec68091d19 | ||
|
|
f0dda35d7d | ||
|
|
46e3209e0d | ||
|
|
b1288cdc24 | ||
|
|
ca32fc1718 | ||
|
|
80bf8975fd | ||
|
|
e0be1bc4f1 | ||
|
|
da1c64b6e7 | ||
|
|
02bfc5831e | ||
|
|
8bf73208a4 | ||
|
|
71a69d9b68 | ||
|
|
1b0c9ce396 | ||
|
|
257b458121 | ||
|
|
671a7eb36f | ||
|
|
c15df15f07 | ||
|
|
68d949b3e3 | ||
|
|
81b6b09f6b | ||
|
|
311cd8cf2f | ||
|
|
75a333ad2d | ||
|
|
a62c6626e0 | ||
|
|
84001cfa96 | ||
|
|
abf931afa6 | ||
|
|
a914a1c6dc | ||
|
|
4ed3c1eb88 | ||
|
|
d6dfe516ff | ||
|
|
8d2b74d287 | ||
|
|
349542332a | ||
|
|
4fce44fc8b | ||
|
|
67e5b5420e | ||
|
|
2670fe8c73 | ||
|
|
a8b9891f95 | ||
|
|
0aa17c1002 | ||
|
|
4862720e08 | ||
|
|
ae25546a7a | ||
|
|
49666d76cf | ||
|
|
cad248f5c6 | ||
|
|
d3daa0db8b | ||
|
|
dde3ef1116 | ||
|
|
f8943ec003 | ||
|
|
8261e0026b | ||
|
|
64aabc9183 | ||
|
|
10e7de7705 | ||
|
|
21fca397cc | ||
|
|
f307479ba6 | ||
|
|
1b01d23be2 | ||
|
|
4048762cbe | ||
|
|
0463f61837 | ||
|
|
c41c60be13 | ||
|
|
b878f93c70 |
No files matched your search
+10
-24
@@ -94,7 +94,13 @@ else()
|
||||
if(WITH_ZLIB)
|
||||
find_package(zlib REQUIRED)
|
||||
add_definitions(-DZLIB)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
if(ZLIB_INCLUDE_DIRS)
|
||||
# CMake 3
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
else()
|
||||
# CMake 2
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
endif()
|
||||
list(APPEND THIRDPARTY_LIBS ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
@@ -495,6 +501,7 @@ set(SOURCES
|
||||
db/flush_scheduler.cc
|
||||
db/forward_iterator.cc
|
||||
db/internal_stats.cc
|
||||
db/in_memory_stats_history.cc
|
||||
db/logs_with_prep_tracker.cc
|
||||
db/log_reader.cc
|
||||
db/log_writer.cc
|
||||
@@ -504,7 +511,6 @@ set(SOURCES
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/range_del_aggregator.cc
|
||||
db/range_del_aggregator_v2.cc
|
||||
db/range_tombstone_fragmenter.cc
|
||||
db/repair.cc
|
||||
db/snapshot_impl.cc
|
||||
@@ -525,7 +531,6 @@ set(SOURCES
|
||||
env/env_hdfs.cc
|
||||
env/mock_env.cc
|
||||
memtable/alloc_tracker.cc
|
||||
memtable/hash_cuckoo_rep.cc
|
||||
memtable/hash_linklist_rep.cc
|
||||
memtable/hash_skiplist_rep.cc
|
||||
memtable/skiplistrep.cc
|
||||
@@ -597,6 +602,7 @@ set(SOURCES
|
||||
util/comparator.cc
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_arena.cc
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
util/delete_scheduler.cc
|
||||
util/dynamic_bloom.cc
|
||||
@@ -637,20 +643,11 @@ set(SOURCES
|
||||
utilities/cassandra/format.cc
|
||||
utilities/cassandra/merge_operator.cc
|
||||
utilities/checkpoint/checkpoint_impl.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/date_tiered/date_tiered_db_impl.cc
|
||||
utilities/debug.cc
|
||||
utilities/document/document_db.cc
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
utilities/env_mirror.cc
|
||||
utilities/env_timed.cc
|
||||
utilities/geodb/geodb_impl.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/lua/rocks_lua_compaction_filter.cc
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators/bytesxor.cc
|
||||
utilities/merge_operators/max.cc
|
||||
@@ -665,9 +662,7 @@ set(SOURCES
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/redis/redis_lists.cc
|
||||
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
|
||||
@@ -823,6 +818,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
TARGETS ${ROCKSDB_SHARED_LIB}
|
||||
EXPORT RocksDBTargets
|
||||
COMPONENT runtime
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
@@ -907,7 +903,6 @@ if(WITH_TESTS)
|
||||
db/plain_table_db_test.cc
|
||||
db/prefix_test.cc
|
||||
db/range_del_aggregator_test.cc
|
||||
db/range_del_aggregator_v2_test.cc
|
||||
db/range_tombstone_fragmenter_test.cc
|
||||
db/repair_test.cc
|
||||
db/table_properties_collector_test.cc
|
||||
@@ -969,12 +964,6 @@ if(WITH_TESTS)
|
||||
utilities/cassandra/cassandra_row_merge_test.cc
|
||||
utilities/cassandra/cassandra_serialize_test.cc
|
||||
utilities/checkpoint/checkpoint_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
utilities/document/document_db_test.cc
|
||||
utilities/document/json_document_test.cc
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/lua/rocks_lua_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
@@ -982,8 +971,6 @@ if(WITH_TESTS)
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/redis/redis_lists_test.cc
|
||||
utilities/spatialdb/spatial_db_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
@@ -1003,7 +990,6 @@ if(WITH_TESTS)
|
||||
db/range_del_aggregator_bench.cc
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
utilities/column_aware_encoding_exp.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT util/testharness.cc)
|
||||
foreach(sourcefile ${BENCHMARKS})
|
||||
|
||||
+54
-1
@@ -1,11 +1,64 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
|
||||
## 6.0.2 (4/23/2019)
|
||||
## Bug Fixes
|
||||
* Fix build failures due to missing JEMALLOC_CXX_THROW macro (#5053)
|
||||
* Fix compilation on db_bench_tool.cc on Windows (#5227)
|
||||
|
||||
## 6.0.1 (3/26/2019)
|
||||
### New Features
|
||||
### Public API Change
|
||||
* Added many new features to the Java API to bring it closer to the C++ API.
|
||||
|
||||
### Bug Fixes
|
||||
* Make BlobDB wait for all background tasks on shutdown.
|
||||
* Fixed a BlobDB issue where some trash files are not tracked causing them to remain forever.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix JEMALLOC_CXX_THROW macro missing from older Jemalloc versions, causing build failures on some platforms.
|
||||
|
||||
|
||||
## 6.0.0 (2/19/2019)
|
||||
### New Features
|
||||
* Enabled checkpoint on readonly db (DBImplReadOnly).
|
||||
* Make DB ignore dropped column families while committing results of atomic flush.
|
||||
* RocksDB may choose to preopen some files even if options.max_open_files != -1. This may make DB open slightly longer.
|
||||
* For users of dictionary compression with ZSTD v0.7.0+, we now reuse the same digested dictionary when compressing each of an SST file's data blocks for faster compression speeds.
|
||||
* For all users of dictionary compression who set `cache_index_and_filter_blocks == true`, we now store dictionary data used for decompression in the block cache for better control over memory usage. For users of ZSTD v1.1.4+ who compile with -DZSTD_STATIC_LINKING_ONLY, this includes a digested dictionary, which is used to increase decompression speed.
|
||||
* Add support for block checksums verification for external SST files before ingestion.
|
||||
* Introduce stats history which periodically saves Statistics snapshots and added `GetStatsHistory` API to retrieve these snapshots.
|
||||
* Add a place holder in manifest which indicate a record from future that can be safely ignored.
|
||||
* Add support for trace sampling.
|
||||
* Enable properties block checksum verification for block-based tables.
|
||||
* For all users of dictionary compression, we now generate a separate dictionary for compressing each bottom-level SST file. Previously we reused a single dictionary for a whole compaction to bottom level. The new approach achieves better compression ratios; however, it uses more memory and CPU for buffering/sampling data blocks and training dictionaries.
|
||||
* Add whole key bloom filter support in memtable.
|
||||
* Files written by `SstFileWriter` will now use dictionary compression if it is configured in the file writer's `CompressionOptions`.
|
||||
|
||||
### Public API Change
|
||||
* Disallow CompactionFilter::IgnoreSnapshots() = false, because it is not very useful and the behavior is confusing. The filter will filter everything if there is no snapshot declared by the time the compaction starts. However, users can define a snapshot after the compaction starts and before it finishes and this new snapshot won't be repeatable, because after the compaction finishes, some keys may be dropped.
|
||||
* CompactionPri = kMinOverlappingRatio also uses compensated file size, which boosts file with lots of tombstones to be compacted first.
|
||||
* Transaction::GetForUpdate is extended with a do_validate parameter with default value of true. If false it skips validating the snapshot before doing the read. Similarly ::Merge, ::Put, ::Delete, and ::SingleDelete are extended with assume_tracked with default value of false. If true it indicates that call is assumed to be after a ::GetForUpdate.
|
||||
* `TableProperties::num_entries` and `TableProperties::num_deletions` now also account for number of range tombstones.
|
||||
* Remove geodb, spatial_db, document_db, json_document, date_tiered_db, and redis_lists.
|
||||
* With "ldb ----try_load_options", when wal_dir specified by the option file doesn't exist, ignore it.
|
||||
* Change time resolution in FileOperationInfo.
|
||||
* Deleting Blob files also go through SStFileManager.
|
||||
* Remove CuckooHash memtable.
|
||||
* The counter stat `number.block.not_compressed` now also counts blocks not compressed due to poor compression ratio.
|
||||
* Remove ttl option from `CompactionOptionsFIFO`. The option has been deprecated and ttl in `ColumnFamilyOptions` is used instead.
|
||||
* Support SST file ingestion across multiple column families via DB::IngestExternalFiles. See the function's comment about atomicity.
|
||||
* Remove Lua compaction filter.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls.
|
||||
* Fix a memory leak when files with range tombstones are read in mmap mode and block cache is enabled
|
||||
* Fix handling of corrupt range tombstone blocks such that corruptions cannot cause deleted keys to reappear
|
||||
* Lock free MultiGet
|
||||
* Fix incorrect `NotFound` point lookup result when querying the endpoint of a file that has been extended by a range tombstone.
|
||||
* Fix with pipelined write, write leaders's callback failure lead to the whole write group fail.
|
||||
|
||||
### Change Default Options
|
||||
* Change options.compaction_pri's default to kMinOverlappingRatio
|
||||
|
||||
## 5.18.0 (11/30/2018)
|
||||
### New Features
|
||||
|
||||
@@ -43,6 +43,8 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
command line flags processing. You can compile rocksdb library even
|
||||
if you don't have gflags installed.
|
||||
|
||||
* If you wish to build the RocksJava static target, then cmake is required for building Snappy.
|
||||
|
||||
## Supported platforms
|
||||
|
||||
* **Linux - Ubuntu**
|
||||
|
||||
@@ -198,7 +198,7 @@ include make_config.mk
|
||||
CLEAN_FILES += make_config.mk
|
||||
|
||||
missing_make_config_paths := $(shell \
|
||||
grep "\/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
grep "\./\S*\|/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
while read path; \
|
||||
do [ -e $$path ] || echo $$path; \
|
||||
done | sort | uniq)
|
||||
@@ -403,7 +403,7 @@ BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
ANALYZETOOLOBJECTS = $(ANALYZER_LIB_SOURCES:.cc=.o)
|
||||
|
||||
EXPOBJECTS = $(EXP_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
EXPOBJECTS = $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
db_basic_test \
|
||||
@@ -482,7 +482,6 @@ TESTS = \
|
||||
merger_test \
|
||||
util_merge_operators_test \
|
||||
options_file_test \
|
||||
redis_test \
|
||||
reduce_levels_test \
|
||||
plain_table_db_test \
|
||||
comparator_db_test \
|
||||
@@ -496,12 +495,8 @@ TESTS = \
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
ttl_test \
|
||||
date_tiered_test \
|
||||
backupable_db_test \
|
||||
document_db_test \
|
||||
json_document_test \
|
||||
sim_cache_test \
|
||||
spatial_db_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
compaction_picker_test \
|
||||
@@ -513,7 +508,6 @@ TESTS = \
|
||||
deletefile_test \
|
||||
obsolete_files_test \
|
||||
table_test \
|
||||
geodb_test \
|
||||
delete_scheduler_test \
|
||||
options_test \
|
||||
options_settable_test \
|
||||
@@ -530,7 +524,6 @@ TESTS = \
|
||||
compaction_job_test \
|
||||
thread_list_test \
|
||||
sst_dump_test \
|
||||
column_aware_encoding_test \
|
||||
compact_files_test \
|
||||
optimistic_transaction_test \
|
||||
write_callback_test \
|
||||
@@ -542,8 +535,6 @@ TESTS = \
|
||||
ldb_cmd_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
lua_test \
|
||||
range_del_aggregator_test \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
repair_test \
|
||||
@@ -554,7 +545,7 @@ TESTS = \
|
||||
trace_analyzer_test \
|
||||
repeatable_thread_test \
|
||||
range_tombstone_fragmenter_test \
|
||||
range_del_aggregator_v2_test \
|
||||
range_del_aggregator_test \
|
||||
sst_file_reader_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
@@ -605,7 +596,7 @@ TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
|
||||
# TODO: add back forward_iterator_bench, after making it build in all environemnts.
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench column_aware_encoding_exp persistent_cache_bench range_del_aggregator_bench
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench persistent_cache_bench range_del_aggregator_bench
|
||||
|
||||
# if user didn't config LIBNAME, set the default
|
||||
ifeq ($(LIBNAME),)
|
||||
@@ -695,7 +686,8 @@ endif # PLATFORM_SHARED_EXT
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
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
|
||||
analyze tools tools_lib \
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
@@ -825,7 +817,6 @@ J ?= 100%
|
||||
# Use this regexp to select the subset of tests whose names match.
|
||||
tests-regexp = .
|
||||
|
||||
t_run = $(wildcard t/run-*)
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
@@ -835,7 +826,7 @@ check_0:
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out $(PARALLEL_TEST),$(TESTS)); \
|
||||
printf '%s\n' $(t_run); \
|
||||
find t -name 'run-*' -print; \
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
@@ -852,7 +843,7 @@ valgrind_check_0:
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out $(PARALLEL_TEST) %skiplist_test options_settable_test, $(TESTS)); \
|
||||
printf '%s\n' $(t_run); \
|
||||
find t -name 'run-*' -print; \
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
@@ -904,23 +895,29 @@ ldb_tests: ldb
|
||||
|
||||
crash_test: whitebox_crash_test blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush: whitebox_crash_test_with_atomic_flush blackbox_crash_test_with_atomic_flush
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
@@ -931,6 +928,11 @@ asan_crash_test:
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_atomic_flush:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) check -j32
|
||||
@@ -941,6 +943,11 @@ ubsan_crash_test:
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_crash_test_with_atomic_flush:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
@@ -1154,9 +1161,6 @@ cassandra_row_merge_test: utilities/cassandra/cassandra_row_merge_test.o utiliti
|
||||
cassandra_serialize_test: utilities/cassandra/cassandra_serialize_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
redis_test: utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
hash_table_test: utilities/persistent_cache/hash_table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1295,18 +1299,9 @@ backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TE
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
document_db_test: utilities/document/document_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
json_document_test: utilities/document/json_document_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sim_cache_test: utilities/simulator_cache/sim_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_mirror_test: utilities/env_mirror_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1324,9 +1319,6 @@ object_registry_test: utilities/object_registry_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
date_tiered_test: utilities/date_tiered/date_tiered_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1453,9 +1445,6 @@ deletefile_test: db/deletefile_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
obsolete_files_test: db/obsolete_files_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
geodb_test: utilities/geodb/geodb_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
rocksdb_dump: tools/dump/rocksdb_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1504,9 +1493,6 @@ timer_queue_test: util/timer_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
sst_dump_test: tools/sst_dump_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_test: utilities/column_aware_encoding_test.o $(TESTHARNESS) $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
optimistic_transaction_test: utilities/transactions/optimistic_transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1546,9 +1532,6 @@ sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
blob_dump: tools/blob_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_exp: utilities/column_aware_encoding_exp.o $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
repair_test: db/repair_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1570,9 +1553,6 @@ statistics_test: monitoring/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
lru_cache_test: cache/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lua_test: utilities/lua/rocks_lua_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
range_del_aggregator_test: db/range_del_aggregator_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1588,9 +1568,6 @@ repeatable_thread_test: util/repeatable_thread_test.o $(LIBOBJECTS) $(TESTHARNES
|
||||
range_tombstone_fragmenter_test: db/range_tombstone_fragmenter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
range_del_aggregator_v2_test: db/range_del_aggregator_v2_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_file_reader_test: table/sst_file_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1665,14 +1642,14 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= https://web.archive.org/web/20180624184835/http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.4
|
||||
SNAPPY_SHA256 ?= 134bfe122fd25599bb807bb8130e7ba6d9bdb851e0b16efcb83ac4f5d0b70057
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/releases/download
|
||||
LZ4_VER ?= 1.8.0
|
||||
LZ4_SHA256 ?= 2ca482ea7a9bb103603108b5a7510b7592b90158c151ff50a28f1ca8389fccf6
|
||||
SNAPPY_VER ?= 1.1.7
|
||||
SNAPPY_SHA256 ?= 3dfa02e873ff51a11ee02b9ca391807f0c8ea0529a4924afa645fbf97163f9d4
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.8.3
|
||||
LZ4_SHA256 ?= 33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.3.3
|
||||
ZSTD_SHA256 ?= a77c47153ee7de02626c5b2a097005786b71688be61e9fb81806a011f90b297b
|
||||
ZSTD_VER ?= 1.3.7
|
||||
ZSTD_SHA256 ?= 5dd1e90eb16c25425880c8a91327f63de22891ffed082fcc17e5ae84fce0d5fb
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -1711,7 +1688,9 @@ endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
curl -O -L ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
ifeq (,$(wildcard ./zlib-$(ZLIB_VER).tar.gz))
|
||||
curl --output zlib-$(ZLIB_VER).tar.gz -L ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
endif
|
||||
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZLIB_SHA256)" != "$$ZLIB_SHA256_ACTUAL" ]; then \
|
||||
echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
|
||||
@@ -1723,7 +1702,9 @@ libz.a:
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
curl -O -L ${BZIP2_DOWNLOAD_BASE}/$(BZIP2_VER)/bzip2-$(BZIP2_VER).tar.gz
|
||||
ifeq (,$(wildcard ./bzip2-$(BZIP2_VER).tar.gz))
|
||||
curl --output bzip2-$(BZIP2_VER).tar.gz -L ${BZIP2_DOWNLOAD_BASE}/$(BZIP2_VER)/bzip2-$(BZIP2_VER).tar.gz
|
||||
endif
|
||||
BZIP2_SHA256_ACTUAL=`$(SHA256_CMD) bzip2-$(BZIP2_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(BZIP2_SHA256)" != "$$BZIP2_SHA256_ACTUAL" ]; then \
|
||||
echo bzip2-$(BZIP2_VER).tar.gz checksum mismatch, expected=\"$(BZIP2_SHA256)\" actual=\"$$BZIP2_SHA256_ACTUAL\"; \
|
||||
@@ -1735,21 +1716,24 @@ libbz2.a:
|
||||
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
curl -O -L ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER)/snappy-$(SNAPPY_VER).tar.gz
|
||||
ifeq (,$(wildcard ./snappy-$(SNAPPY_VER).tar.gz))
|
||||
curl --output snappy-$(SNAPPY_VER).tar.gz -L ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER).tar.gz
|
||||
endif
|
||||
SNAPPY_SHA256_ACTUAL=`$(SHA256_CMD) snappy-$(SNAPPY_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(SNAPPY_SHA256)" != "$$SNAPPY_SHA256_ACTUAL" ]; then \
|
||||
echo snappy-$(SNAPPY_VER).tar.gz checksum mismatch, expected=\"$(SNAPPY_SHA256)\" actual=\"$$SNAPPY_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
cd snappy-$(SNAPPY_VER) && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --with-pic --enable-static --disable-shared
|
||||
cd snappy-$(SNAPPY_VER) && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/.libs/libsnappy.a .
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
curl -O -L ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
mv v$(LZ4_VER).tar.gz lz4-$(LZ4_VER).tar.gz
|
||||
ifeq (,$(wildcard ./lz4-$(LZ4_VER).tar.gz))
|
||||
curl --output lz4-$(LZ4_VER).tar.gz -L ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
endif
|
||||
LZ4_SHA256_ACTUAL=`$(SHA256_CMD) lz4-$(LZ4_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(LZ4_SHA256)" != "$$LZ4_SHA256_ACTUAL" ]; then \
|
||||
echo lz4-$(LZ4_VER).tar.gz checksum mismatch, expected=\"$(LZ4_SHA256)\" actual=\"$$LZ4_SHA256_ACTUAL\"; \
|
||||
@@ -1761,8 +1745,9 @@ liblz4.a:
|
||||
|
||||
libzstd.a:
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
curl -O -L ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
mv v$(ZSTD_VER).tar.gz zstd-$(ZSTD_VER).tar.gz
|
||||
ifeq (,$(wildcard ./zstd-$(ZSTD_VER).tar.gz))
|
||||
curl --output zstd-$(ZSTD_VER).tar.gz -L ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
endif
|
||||
ZSTD_SHA256_ACTUAL=`$(SHA256_CMD) zstd-$(ZSTD_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZSTD_SHA256)" != "$$ZSTD_SHA256_ACTUAL" ]; then \
|
||||
echo zstd-$(ZSTD_VER).tar.gz checksum mismatch, expected=\"$(ZSTD_SHA256)\" actual=\"$$ZSTD_SHA256_ACTUAL\"; \
|
||||
@@ -1931,7 +1916,8 @@ commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
ifeq ($(PLATFORM), IOS)
|
||||
# For iOS, create universal object files to be used on both the simulator and
|
||||
# a device.
|
||||
PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms
|
||||
XCODEROOT=$(shell xcode-select -print-path)
|
||||
PLATFORMSROOT=$(XCODEROOT)/Platforms
|
||||
SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
|
||||
DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
|
||||
IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
|
||||
@@ -1968,7 +1954,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) $(ANALYZER_LIB_SOURCES)
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_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,12 +1,10 @@
|
||||
load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
load("@fbcode_macros//build_defs:cpp_library.bzl", "cpp_library")
|
||||
load(":defs.bzl", "test_binary")
|
||||
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
|
||||
rocksdb_compiler_flags = [
|
||||
ROCKSDB_COMPILER_FLAGS = [
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
@@ -22,6 +20,7 @@ rocksdb_compiler_flags = [
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
@@ -33,7 +32,7 @@ rocksdb_compiler_flags = [
|
||||
"-Wnarrowing",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
@@ -45,13 +44,13 @@ rocksdb_external_deps = [
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
rocksdb_arch_preprocessor_flags = {
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
"x86_64": [
|
||||
"-DHAVE_SSE42",
|
||||
"-DHAVE_PCLMUL",
|
||||
@@ -64,18 +63,15 @@ is_opt_mode = build_mode.startswith("opt")
|
||||
|
||||
# -DNDEBUG is added by default in opt mode in fbcode. But adding it twice
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
|
||||
default_allocator = read_config("fbcode", "default_allocator")
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Let RocksDB aware of jemalloc existence.
|
||||
# Do not enable it if sanitizer presents.
|
||||
if is_opt_mode and default_allocator.startswith("jemalloc") and sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
@@ -114,6 +110,7 @@ cpp_library(
|
||||
"db/flush_job.cc",
|
||||
"db/flush_scheduler.cc",
|
||||
"db/forward_iterator.cc",
|
||||
"db/in_memory_stats_history.cc",
|
||||
"db/internal_stats.cc",
|
||||
"db/log_reader.cc",
|
||||
"db/log_writer.cc",
|
||||
@@ -124,7 +121,6 @@ cpp_library(
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
"db/range_del_aggregator_v2.cc",
|
||||
"db/range_tombstone_fragmenter.cc",
|
||||
"db/repair.cc",
|
||||
"db/snapshot_impl.cc",
|
||||
@@ -147,7 +143,6 @@ cpp_library(
|
||||
"env/io_posix.cc",
|
||||
"env/mock_env.cc",
|
||||
"memtable/alloc_tracker.cc",
|
||||
"memtable/hash_cuckoo_rep.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
"memtable/skiplistrep.cc",
|
||||
@@ -220,6 +215,7 @@ cpp_library(
|
||||
"util/comparator.cc",
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_arena.cc",
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/delete_scheduler.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
@@ -261,16 +257,10 @@ cpp_library(
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/date_tiered/date_tiered_db_impl.cc",
|
||||
"utilities/debug.cc",
|
||||
"utilities/document/document_db.cc",
|
||||
"utilities/document/json_document.cc",
|
||||
"utilities/document/json_document_builder.cc",
|
||||
"utilities/env_mirror.cc",
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/geodb/geodb_impl.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/lua/rocks_lua_compaction_filter.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators/bytesxor.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
@@ -285,9 +275,7 @@ cpp_library(
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/redis/redis_lists.cc",
|
||||
"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",
|
||||
@@ -308,11 +296,11 @@ cpp_library(
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -325,16 +313,13 @@ cpp_library(
|
||||
"util/testharness.cc",
|
||||
"util/testutil.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
"utilities/col_buf_decoder.cc",
|
||||
"utilities/col_buf_encoder.cc",
|
||||
"utilities/column_aware_encoding_util.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -345,22 +330,22 @@ cpp_library(
|
||||
"util/testutil.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "env_basic_test_lib",
|
||||
srcs = ["env/env_basic_test.cc"],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
# [test_name, test_src, test_type]
|
||||
@@ -450,11 +435,6 @@ ROCKS_TESTS = [
|
||||
"util/coding_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"column_aware_encoding_test",
|
||||
"utilities/column_aware_encoding_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"column_family_test",
|
||||
"db/column_family_test.cc",
|
||||
@@ -525,11 +505,6 @@ ROCKS_TESTS = [
|
||||
"table/data_block_hash_index_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"date_tiered_test",
|
||||
"utilities/date_tiered/date_tiered_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"db_basic_test",
|
||||
"db/db_basic_test.cc",
|
||||
@@ -690,11 +665,6 @@ ROCKS_TESTS = [
|
||||
"db/deletefile_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"document_db_test",
|
||||
"utilities/document/document_db_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"dynamic_bloom_test",
|
||||
"util/dynamic_bloom_test.cc",
|
||||
@@ -770,11 +740,6 @@ ROCKS_TESTS = [
|
||||
"table/full_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"geodb_test",
|
||||
"utilities/geodb/geodb_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"hash_table_test",
|
||||
"utilities/persistent_cache/hash_table_test.cc",
|
||||
@@ -805,11 +770,6 @@ ROCKS_TESTS = [
|
||||
"monitoring/iostats_context_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"json_document_test",
|
||||
"utilities/document/json_document_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"ldb_cmd_test",
|
||||
"tools/ldb_cmd_test.cc",
|
||||
@@ -935,11 +895,6 @@ ROCKS_TESTS = [
|
||||
"db/range_del_aggregator_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"range_del_aggregator_v2_test",
|
||||
"db/range_del_aggregator_v2_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"range_tombstone_fragmenter_test",
|
||||
"db/range_tombstone_fragmenter_test.cc",
|
||||
@@ -980,11 +935,6 @@ ROCKS_TESTS = [
|
||||
"util/slice_transform_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"spatial_db_test",
|
||||
"utilities/spatialdb/spatial_db_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"sst_dump_test",
|
||||
"tools/sst_dump_test.cc",
|
||||
@@ -1110,26 +1060,16 @@ ROCKS_TESTS = [
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
if not is_opt_mode:
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary(
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin],
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
)
|
||||
[
|
||||
test_binary(
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
)
|
||||
for test_name, test_cc, parallelism in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
]
|
||||
+1
-1
@@ -3,7 +3,7 @@ image: Visual Studio 2017
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
|
||||
+34
-48
@@ -3,14 +3,12 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
rocksdb_target_header = """load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
load("@fbcode_macros//build_defs:cpp_library.bzl", "cpp_library")
|
||||
load(":defs.bzl", "test_binary")
|
||||
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
|
||||
rocksdb_compiler_flags = [
|
||||
ROCKSDB_COMPILER_FLAGS = [
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
@@ -26,6 +24,7 @@ rocksdb_compiler_flags = [
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
@@ -37,7 +36,7 @@ rocksdb_compiler_flags = [
|
||||
"-Wnarrowing",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
@@ -49,13 +48,13 @@ rocksdb_external_deps = [
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
rocksdb_arch_preprocessor_flags = {
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
"x86_64": [
|
||||
"-DHAVE_SSE42",
|
||||
"-DHAVE_PCLMUL",
|
||||
@@ -68,18 +67,15 @@ is_opt_mode = build_mode.startswith("opt")
|
||||
|
||||
# -DNDEBUG is added by default in opt mode in fbcode. But adding it twice
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
|
||||
default_allocator = read_config("fbcode", "default_allocator")
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Let RocksDB aware of jemalloc existence.
|
||||
# Do not enable it if sanitizer presents.
|
||||
if is_opt_mode and default_allocator.startswith("jemalloc") and sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
"""
|
||||
|
||||
|
||||
@@ -88,11 +84,11 @@ cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -100,11 +96,11 @@ binary_template = """
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
srcs = [%s],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [%s],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -123,27 +119,17 @@ ROCKS_TESTS = [
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
if not is_opt_mode:
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary(
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin],
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
)
|
||||
[
|
||||
test_binary(
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
)
|
||||
for test_name, test_cc, parallelism in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
]
|
||||
"""
|
||||
@@ -97,7 +97,7 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
}
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// Git thinks it is an uncommited change.
|
||||
// Git thinks it is an uncommitted change.
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
|
||||
@@ -53,11 +53,13 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -368,6 +370,16 @@ EOF
|
||||
# JEMALLOC can be enabled either using the flag (like here) or by
|
||||
# providing direct link to the jemalloc library
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
# check for JEMALLOC installed with HomeBrew
|
||||
if [ "$PLATFORM" == "OS_MACOSX" ]; then
|
||||
if hash brew 2>/dev/null && brew ls --versions jemalloc > /dev/null; then
|
||||
JEMALLOC_VER=$(brew ls --versions jemalloc | tail -n 1 | cut -f 2 -d ' ')
|
||||
JEMALLOC_INCLUDE="-I/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS $JEMALLOC_LIB"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if ! test $JEMALLOC && ! test $ROCKSDB_DISABLE_TCMALLOC; then
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/60d6f124a78798b73944f5ba87c2306ae3460153/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/3ee276cbacfad3074e3f07bf826ac47f06970f4e/1.3.5/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/9c910d36d6235cc40e8ff559358f1833452300ca/master/platform007/5b0f53e
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/92ff90349e2f43ea0a8246d8b1cf17b6869013e3/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -132,11 +132,14 @@ _TEST_NAME_TO_PARSERS = {
|
||||
'lite': [CompilerErrorParser],
|
||||
'lite_test': [CompilerErrorParser, GTestErrorParser],
|
||||
'stress_crash': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'stress_crash_with_atomic_flush': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'write_stress': [CompilerErrorParser, WriteStressErrorParser],
|
||||
'asan': [CompilerErrorParser, GTestErrorParser, AsanErrorParser],
|
||||
'asan_crash': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'asan_crash_with_atomic_flush': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan': [CompilerErrorParser, GTestErrorParser, UbsanErrorParser],
|
||||
'ubsan_crash': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan_crash_with_atomic_flush': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'valgrind': [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser],
|
||||
'tsan': [CompilerErrorParser, GTestErrorParser, TsanErrorParser],
|
||||
'format_compatible': [CompilerErrorParser, CompatErrorParser],
|
||||
|
||||
@@ -51,7 +51,7 @@ if test -z $PIC_BUILD; then
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -85,7 +85,9 @@ NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
# in gcc-5 there are known problems with TSAN like https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71090.
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
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"
|
||||
@@ -398,6 +400,35 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB stress/crash test with atomic flush
|
||||
#
|
||||
STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress/Crash Test (atomic flush)',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'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 with atomic flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$ARTIFACTS,
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
# RocksDB write stress test.
|
||||
# We run on disk device on purpose (i.e. no $SHM)
|
||||
# because we want to add some randomness to fsync commands
|
||||
@@ -462,6 +493,28 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing with atomic flush under address sanitizer
|
||||
#
|
||||
ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test (atomic flush) under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB test under undefined behavior sanitizer
|
||||
#
|
||||
@@ -504,6 +557,28 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing with atomic flush under undefined behavior sanitizer
|
||||
#
|
||||
UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test (atomic flush) under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test under valgrind
|
||||
#
|
||||
@@ -570,6 +645,28 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash test with atomic flush under TSAN
|
||||
#
|
||||
TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test with atomic flush under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=tsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB format compatible
|
||||
#
|
||||
@@ -609,7 +706,7 @@ run_no_compression()
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
make clean
|
||||
cat build_tools/fbcode_config.sh | grep -iv dzlib | grep -iv dlz4 | grep -iv dsnappy | grep -iv dbzip2 > .tmp.fbcode_config.sh
|
||||
cat build_tools/fbcode_config.sh | grep -iv dzstd | grep -iv dzlib | grep -iv dlz4 | grep -iv dsnappy | grep -iv dbzip2 > .tmp.fbcode_config.sh
|
||||
mv .tmp.fbcode_config.sh build_tools/fbcode_config.sh
|
||||
cat Makefile | grep -v tools/ldb_test.py > .tmp.Makefile
|
||||
mv .tmp.Makefile Makefile
|
||||
@@ -751,6 +848,9 @@ case $1 in
|
||||
stress_crash)
|
||||
echo $STRESS_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
stress_crash_with_atomic_flush)
|
||||
echo $STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
write_stress)
|
||||
echo $WRITE_STRESS_COMMANDS
|
||||
;;
|
||||
@@ -760,12 +860,18 @@ case $1 in
|
||||
asan_crash)
|
||||
echo $ASAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
asan_crash_with_atomic_flush)
|
||||
echo $ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
ubsan)
|
||||
echo $UBSAN_TEST_COMMANDS
|
||||
;;
|
||||
ubsan_crash)
|
||||
echo $UBSAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
ubsan_crash_with_atomic_flush)
|
||||
echo $UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
valgrind)
|
||||
echo $VALGRIND_TEST_COMMANDS
|
||||
;;
|
||||
@@ -775,6 +881,9 @@ case $1 in
|
||||
tsan_crash)
|
||||
echo $TSAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
tsan_crash_with_atomic_flush)
|
||||
echo $TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
format_compatible)
|
||||
echo $FORMAT_COMPATIBLE_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -53,6 +53,45 @@ function get_lib_base()
|
||||
log_variable $__res_var
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# platform007 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 7.x platform007
|
||||
get_lib_base glibc 2.26 platform007
|
||||
get_lib_base snappy LATEST platform007
|
||||
get_lib_base zlib LATEST platform007
|
||||
get_lib_base bzip2 LATEST platform007
|
||||
get_lib_base lz4 LATEST platform007
|
||||
get_lib_base zstd LATEST platform007
|
||||
get_lib_base gflags LATEST platform007
|
||||
get_lib_base jemalloc LATEST platform007
|
||||
get_lib_base numa LATEST platform007
|
||||
get_lib_base libunwind LATEST platform007
|
||||
get_lib_base tbb LATEST platform007
|
||||
|
||||
get_lib_base kernel-headers fb platform007
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform007
|
||||
get_lib_base lua 5.3.4 platform007
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
###########################################################
|
||||
|
||||
Vendored
+1
-2
@@ -73,8 +73,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
current_ = this;
|
||||
}
|
||||
|
||||
~CacheTest() {
|
||||
}
|
||||
~CacheTest() override {}
|
||||
|
||||
std::shared_ptr<Cache> NewCache(size_t capacity) {
|
||||
auto type = GetParam();
|
||||
|
||||
Vendored
+23
-26
@@ -240,32 +240,29 @@ class ClockCacheShard : public CacheShard {
|
||||
typedef tbb::concurrent_hash_map<CacheKey, CacheHandle*, CacheKey> HashTable;
|
||||
|
||||
ClockCacheShard();
|
||||
~ClockCacheShard();
|
||||
~ClockCacheShard() override;
|
||||
|
||||
// Interfaces
|
||||
virtual void SetCapacity(size_t capacity) override;
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Cache::Handle** handle,
|
||||
Cache::Priority priority) override;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
|
||||
void SetCapacity(size_t capacity) override;
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Cache::Handle** handle, Cache::Priority priority) override;
|
||||
Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
|
||||
// If the entry in in cache, increase reference count and return true.
|
||||
// Return false otherwise.
|
||||
//
|
||||
// Not necessary to hold mutex_ before being called.
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
bool force_erase = false) override;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) override;
|
||||
bool Ref(Cache::Handle* handle) override;
|
||||
bool Release(Cache::Handle* handle, bool force_erase = false) override;
|
||||
void Erase(const Slice& key, uint32_t hash) override;
|
||||
bool EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
CleanupContext* context);
|
||||
virtual size_t GetUsage() const override;
|
||||
virtual size_t GetPinnedUsage() const override;
|
||||
virtual void EraseUnRefEntries() override;
|
||||
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override;
|
||||
size_t GetUsage() const override;
|
||||
size_t GetPinnedUsage() const override;
|
||||
void EraseUnRefEntries() override;
|
||||
void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override;
|
||||
|
||||
private:
|
||||
static const uint32_t kInCacheBit = 1;
|
||||
@@ -685,31 +682,31 @@ class ClockCache : public ShardedCache {
|
||||
SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
virtual ~ClockCache() { delete[] shards_; }
|
||||
~ClockCache() override { delete[] shards_; }
|
||||
|
||||
virtual const char* Name() const override { return "ClockCache"; }
|
||||
const char* Name() const override { return "ClockCache"; }
|
||||
|
||||
virtual CacheShard* GetShard(int shard) override {
|
||||
CacheShard* GetShard(int shard) override {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
}
|
||||
|
||||
virtual const CacheShard* GetShard(int shard) const override {
|
||||
const CacheShard* GetShard(int shard) const override {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
}
|
||||
|
||||
virtual void* Value(Handle* handle) override {
|
||||
void* Value(Handle* handle) override {
|
||||
return reinterpret_cast<const CacheHandle*>(handle)->value;
|
||||
}
|
||||
|
||||
virtual size_t GetCharge(Handle* handle) const override {
|
||||
size_t GetCharge(Handle* handle) const override {
|
||||
return reinterpret_cast<const CacheHandle*>(handle)->charge;
|
||||
}
|
||||
|
||||
virtual uint32_t GetHash(Handle* handle) const override {
|
||||
uint32_t GetHash(Handle* handle) const override {
|
||||
return reinterpret_cast<const CacheHandle*>(handle)->hash;
|
||||
}
|
||||
|
||||
virtual void DisownData() override { shards_ = nullptr; }
|
||||
void DisownData() override { shards_ = nullptr; }
|
||||
|
||||
private:
|
||||
ClockCacheShard* shards_;
|
||||
|
||||
Vendored
+1
-1
@@ -15,7 +15,7 @@ namespace rocksdb {
|
||||
class LRUCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUCacheTest() {}
|
||||
~LRUCacheTest() { DeleteCache(); }
|
||||
~LRUCacheTest() override { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
if (cache_ != nullptr) {
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# - Find zlib
|
||||
# Find the zlib compression library and includes
|
||||
#
|
||||
# ZLIB_INCLUDE_DIR - where to find zlib.h, etc.
|
||||
# ZLIB_LIBRARIES - List of libraries when using zlib.
|
||||
# ZLIB_FOUND - True if zlib found.
|
||||
|
||||
find_path(ZLIB_INCLUDE_DIR
|
||||
NAMES zlib.h
|
||||
HINTS ${ZLIB_ROOT_DIR}/include)
|
||||
|
||||
find_library(ZLIB_LIBRARIES
|
||||
NAMES z
|
||||
HINTS ${ZLIB_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zlib DEFAULT_MSG ZLIB_LIBRARIES ZLIB_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
ZLIB_LIBRARIES
|
||||
ZLIB_INCLUDE_DIR)
|
||||
+27
-20
@@ -18,6 +18,7 @@
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
@@ -47,17 +48,17 @@ TableBuilder* NewTableBuilder(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const std::string* compression_dict, const bool skip_filters,
|
||||
const uint64_t creation_time, const uint64_t oldest_key_time) {
|
||||
const bool skip_filters, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, const uint64_t target_file_size) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
return ioptions.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(ioptions, moptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, compression_dict, skip_filters,
|
||||
column_family_name, level, creation_time,
|
||||
oldest_key_time),
|
||||
compression_opts, skip_filters, column_family_name,
|
||||
level, creation_time, oldest_key_time,
|
||||
target_file_size),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
@@ -65,8 +66,9 @@ Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::unique_ptr<InternalIterator> range_del_iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
@@ -86,12 +88,10 @@ Status BuildTable(
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg(
|
||||
new RangeDelAggregator(internal_comparator, snapshots));
|
||||
s = range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
if (!s.ok()) {
|
||||
// may be non-ok if a range tombstone key is unparsable
|
||||
return s;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg(
|
||||
new CompactionRangeDelAggregator(&internal_comparator, snapshots));
|
||||
for (auto& range_del_iter : range_del_iters) {
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
}
|
||||
|
||||
std::string fname = TableFileName(ioptions.cf_paths, meta->fd.GetNumber(),
|
||||
@@ -105,6 +105,11 @@ Status BuildTable(
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
TableBuilder* builder;
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
// Currently we only enable dictionary compression during compaction to the
|
||||
// bottommost level.
|
||||
CompressionOptions compression_opts_for_flush(compression_opts);
|
||||
compression_opts_for_flush.max_dict_bytes = 0;
|
||||
compression_opts_for_flush.zstd_max_train_bytes = 0;
|
||||
{
|
||||
std::unique_ptr<WritableFile> file;
|
||||
#ifndef NDEBUG
|
||||
@@ -121,14 +126,14 @@ Status BuildTable(
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
|
||||
file_writer.reset(new WritableFileWriter(std::move(file), fname,
|
||||
env_options, ioptions.statistics,
|
||||
ioptions.listeners));
|
||||
file_writer.reset(
|
||||
new WritableFileWriter(std::move(file), fname, env_options, env,
|
||||
ioptions.statistics, ioptions.listeners));
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
int_tbl_prop_collector_factories, column_family_id,
|
||||
column_family_name, file_writer.get(), compression, compression_opts,
|
||||
level, nullptr /* compression_dict */, false /* skip_filters */,
|
||||
column_family_name, file_writer.get(), compression,
|
||||
compression_opts_for_flush, level, false /* skip_filters */,
|
||||
creation_time, oldest_key_time);
|
||||
}
|
||||
|
||||
@@ -158,8 +163,10 @@ Status BuildTable(
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = range_del_agg->NewIterator(); it->Valid(); it->Next()) {
|
||||
auto tombstone = it->Tombstone();
|
||||
auto range_del_it = range_del_agg->NewIterator();
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone.SerializeEndKey(),
|
||||
|
||||
+5
-6
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
@@ -39,8 +40,6 @@ class InternalStats;
|
||||
// @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
|
||||
// TableBuilder returned by this function.
|
||||
// @param compression_dict Data for presetting the compression library's
|
||||
// dictionary, or nullptr.
|
||||
TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& options, const MutableCFOptions& moptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
@@ -49,9 +48,8 @@ TableBuilder* NewTableBuilder(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const std::string* compression_dict = nullptr,
|
||||
const bool skip_filters = false, const uint64_t creation_time = 0,
|
||||
const uint64_t oldest_key_time = 0);
|
||||
const uint64_t oldest_key_time = 0, const uint64_t target_file_size = 0);
|
||||
|
||||
// Build a Table file from the contents of *iter. The generated file
|
||||
// will be named according to number specified in meta. On success, the rest of
|
||||
@@ -65,8 +63,9 @@ extern Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& options,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::unique_ptr<InternalIterator> range_del_iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
|
||||
@@ -208,13 +208,10 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
const char* (*name_)(void*);
|
||||
unsigned char ignore_snapshots_;
|
||||
|
||||
virtual ~rocksdb_compactionfilter_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_compactionfilter_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
bool Filter(int level, const Slice& key, const Slice& existing_value,
|
||||
std::string* new_value, bool* value_changed) const override {
|
||||
char* c_new_value = nullptr;
|
||||
size_t new_value_length = 0;
|
||||
unsigned char c_value_changed = 0;
|
||||
@@ -231,9 +228,9 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return ignore_snapshots_; }
|
||||
bool IgnoreSnapshots() const override { return ignore_snapshots_; }
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
@@ -243,9 +240,9 @@ struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
void*, rocksdb_compactionfiltercontext_t* context);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
virtual ~rocksdb_compactionfilterfactory_t() { (*destructor_)(state_); }
|
||||
~rocksdb_compactionfilterfactory_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
rocksdb_compactionfiltercontext_t ccontext;
|
||||
ccontext.rep = context;
|
||||
@@ -253,7 +250,7 @@ struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
return std::unique_ptr<CompactionFilter>(cf);
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
};
|
||||
|
||||
struct rocksdb_comparator_t : public Comparator {
|
||||
@@ -265,20 +262,17 @@ struct rocksdb_comparator_t : public Comparator {
|
||||
const char* b, size_t blen);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
virtual ~rocksdb_comparator_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_comparator_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
return (*compare_)(state_, a.data(), a.size(), b.data(), b.size());
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
// No-ops since the C binding does not support key shortening methods.
|
||||
virtual void FindShortestSeparator(std::string*,
|
||||
const Slice&) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortestSeparator(std::string*, const Slice&) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
@@ -298,14 +292,11 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
void*,
|
||||
const char* filter, size_t filter_length);
|
||||
|
||||
virtual ~rocksdb_filterpolicy_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_filterpolicy_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const override {
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const override {
|
||||
std::vector<const char*> key_pointers(n);
|
||||
std::vector<size_t> key_sizes(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
@@ -323,8 +314,7 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool KeyMayMatch(const Slice& key,
|
||||
const Slice& filter) const override {
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override {
|
||||
return (*key_match_)(state_, key.data(), key.size(),
|
||||
filter.data(), filter.size());
|
||||
}
|
||||
@@ -349,14 +339,12 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
void*,
|
||||
const char* value, size_t value_length);
|
||||
|
||||
virtual ~rocksdb_mergeoperator_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_mergeoperator_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
size_t n = merge_in.operand_list.size();
|
||||
std::vector<const char*> operand_pointers(n);
|
||||
std::vector<size_t> operand_sizes(n);
|
||||
@@ -390,10 +378,10 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
return success;
|
||||
}
|
||||
|
||||
virtual bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
size_t operand_count = operand_list.size();
|
||||
std::vector<const char*> operand_pointers(operand_count);
|
||||
std::vector<size_t> operand_sizes(operand_count);
|
||||
@@ -444,23 +432,21 @@ struct rocksdb_slicetransform_t : public SliceTransform {
|
||||
void*,
|
||||
const char* key, size_t length);
|
||||
|
||||
virtual ~rocksdb_slicetransform_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_slicetransform_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual Slice Transform(const Slice& src) const override {
|
||||
Slice Transform(const Slice& src) const override {
|
||||
size_t len;
|
||||
char* dst = (*transform_)(state_, src.data(), src.size(), &len);
|
||||
return Slice(dst, len);
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return (*in_domain_)(state_, src.data(), src.size());
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& src) const override {
|
||||
bool InRange(const Slice& src) const override {
|
||||
return (*in_range_)(state_, src.data(), src.size());
|
||||
}
|
||||
};
|
||||
@@ -1495,10 +1481,10 @@ class H : public WriteBatch::Handler {
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
@@ -1784,11 +1770,11 @@ rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base(
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = wbwi->rep->NewIteratorWithBase(column_family->rep, base_iterator->rep);
|
||||
result->rep =
|
||||
wbwi->rep->NewIteratorWithBase(column_family->rep, base_iterator->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
@@ -2067,6 +2053,15 @@ void rocksdb_set_options(
|
||||
db->rep->SetOptions(options_map));
|
||||
}
|
||||
|
||||
void rocksdb_set_options_cf(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* handle, int count, const char* const keys[], const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i=0; i<count; i++)
|
||||
options_map[keys[i]] = values[i];
|
||||
SaveError(errptr,
|
||||
db->rep->SetOptions(handle->rep, options_map));
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create() {
|
||||
return new rocksdb_options_t;
|
||||
}
|
||||
@@ -2857,7 +2852,7 @@ rocksdb_compactionfilter_t* rocksdb_compactionfilter_create(
|
||||
result->state_ = state;
|
||||
result->destructor_ = destructor;
|
||||
result->filter_ = filter;
|
||||
result->ignore_snapshots_ = false;
|
||||
result->ignore_snapshots_ = true;
|
||||
result->name_ = name;
|
||||
return result;
|
||||
}
|
||||
@@ -2957,7 +2952,7 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(int bits_per_ke
|
||||
// supplied C functions.
|
||||
struct Wrapper : public rocksdb_filterpolicy_t {
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const override {
|
||||
@@ -3412,7 +3407,7 @@ void rocksdb_slicetransform_destroy(rocksdb_slicetransform_t* st) {
|
||||
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
|
||||
+3
-2
@@ -917,7 +917,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_wi_delete(wbi, "foo", 3);
|
||||
rocksdb_iterator_t* iter = rocksdb_writebatch_wi_create_iterator_with_base(wbi, base_iter);
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_writebatch_wi_create_iterator_with_base(wbi, base_iter);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
@@ -1527,7 +1528,7 @@ int main(int argc, char** argv) {
|
||||
const rocksdb_snapshot_t* snapshot;
|
||||
snapshot = rocksdb_transactiondb_create_snapshot(txn_db);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snapshot);
|
||||
|
||||
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
|
||||
+5
-9
@@ -25,7 +25,7 @@
|
||||
#include "db/db_impl.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/range_del_aggregator_v2.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
@@ -129,14 +129,10 @@ Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
}
|
||||
}
|
||||
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
|
||||
if (!CompressionTypeSupported(CompressionType::kZSTD)) {
|
||||
// Dictionary trainer is available since v0.6.1, but ZSTD was marked
|
||||
// stable only since v0.8.0. For now we enable the feature in stable
|
||||
// versions only.
|
||||
if (!ZSTD_TrainDictionarySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"zstd dictionary trainer cannot be used because " +
|
||||
CompressionTypeToString(CompressionType::kZSTD) +
|
||||
" is not linked with the binary.");
|
||||
"zstd dictionary trainer cannot be used because ZSTD 1.1.3+ "
|
||||
"is not linked with the binary.");
|
||||
}
|
||||
if (cf_options.compression_opts.max_dict_bytes == 0) {
|
||||
return Status::InvalidArgument(
|
||||
@@ -945,7 +941,7 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
auto read_seq = super_version->current->version_set()->LastSequence();
|
||||
RangeDelAggregatorV2 range_del_agg(&internal_comparator_, read_seq);
|
||||
ReadRangeDelAggregator range_del_agg(&internal_comparator_, read_seq);
|
||||
auto* active_range_del_iter =
|
||||
super_version->mem->NewRangeTombstoneIterator(read_opts, read_seq);
|
||||
range_del_agg.AddTombstones(
|
||||
|
||||
@@ -383,6 +383,8 @@ class ColumnFamilyData {
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const;
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
|
||||
+27
-23
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -68,7 +69,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
DestroyDB(dbname_, Options(db_options_, column_family_options_));
|
||||
}
|
||||
|
||||
virtual ~ColumnFamilyTestBase() {
|
||||
~ColumnFamilyTestBase() override {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (auto h : handles_) {
|
||||
ColumnFamilyDescriptor cfdescriptor;
|
||||
@@ -1206,29 +1207,32 @@ TEST_P(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // Cuckoo is not supported in lite
|
||||
TEST_P(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
db_options_.allow_concurrent_memtable_write = false;
|
||||
Open();
|
||||
auto* s1 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s1 != nullptr);
|
||||
dbfull()->ReleaseSnapshot(s1);
|
||||
// The test is commented out because we want to test that snapshot is
|
||||
// not created for memtables not supported it, but There isn't a memtable
|
||||
// that doesn't support snapshot right now. If we have one later, we can
|
||||
// re-enable the test.
|
||||
//
|
||||
// #ifndef ROCKSDB_LITE // Cuckoo is not supported in lite
|
||||
// TEST_P(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
// db_options_.allow_concurrent_memtable_write = false;
|
||||
// Open();
|
||||
// auto* s1 = dbfull()->GetSnapshot();
|
||||
// ASSERT_TRUE(s1 != nullptr);
|
||||
// dbfull()->ReleaseSnapshot(s1);
|
||||
|
||||
// Add a column family that doesn't support snapshot
|
||||
ColumnFamilyOptions first;
|
||||
first.memtable_factory.reset(NewHashCuckooRepFactory(1024 * 1024));
|
||||
CreateColumnFamilies({"first"}, {first});
|
||||
auto* s2 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s2 == nullptr);
|
||||
// // Add a column family that doesn't support snapshot
|
||||
// ColumnFamilyOptions first;
|
||||
// first.memtable_factory.reset(new DummyMemtableNotSupportingSnapshot());
|
||||
// CreateColumnFamilies({"first"}, {first});
|
||||
// auto* s2 = dbfull()->GetSnapshot();
|
||||
// ASSERT_TRUE(s2 == nullptr);
|
||||
|
||||
// Add a column family that supports snapshot. Snapshot stays not supported.
|
||||
ColumnFamilyOptions second;
|
||||
CreateColumnFamilies({"second"}, {second});
|
||||
auto* s3 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s3 == nullptr);
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
// // Add a column family that supports snapshot. Snapshot stays not
|
||||
// supported. ColumnFamilyOptions second; CreateColumnFamilies({"second"},
|
||||
// {second}); auto* s3 = dbfull()->GetSnapshot(); ASSERT_TRUE(s3 == nullptr);
|
||||
// Close();
|
||||
// }
|
||||
// #endif // !ROCKSDB_LITE
|
||||
|
||||
class TestComparator : public Comparator {
|
||||
int Compare(const rocksdb::Slice& /*a*/,
|
||||
@@ -2042,7 +2046,7 @@ TEST_P(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // Tailing interator not supported
|
||||
#ifndef ROCKSDB_LITE // Tailing iterator not supported
|
||||
namespace {
|
||||
std::string IterStatus(Iterator* iter) {
|
||||
std::string result;
|
||||
|
||||
@@ -35,9 +35,9 @@ class CompactFilesTest : public testing::Test {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -256,9 +256,9 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
class FilterWithGet : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
if (db_ == nullptr) {
|
||||
return true;
|
||||
}
|
||||
@@ -271,7 +271,7 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
db_ = db;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "FilterWithGet"; }
|
||||
const char* Name() const override { return "FilterWithGet"; }
|
||||
|
||||
private:
|
||||
DB* db_;
|
||||
@@ -357,6 +357,51 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, GetCompactionJobInfo) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
options.level0_slowdown_writes_trigger = 1000;
|
||||
options.level0_stop_writes_trigger = 1000;
|
||||
options.write_buffer_size = 65536;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
options.max_compaction_bytes = 5000;
|
||||
|
||||
// Add listener
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(db_name_, options);
|
||||
Status s = DB::Open(options, db_name_, &db);
|
||||
assert(s.ok());
|
||||
assert(db);
|
||||
|
||||
// create couple files
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
db->Put(WriteOptions(), ToString(i), std::string(1000, 'a' + (i % 26)));
|
||||
}
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForFlushMemTable();
|
||||
auto l0_files_1 = collector->GetFlushedFiles();
|
||||
CompactionOptions co;
|
||||
co.compression = CompressionType::kLZ4Compression;
|
||||
CompactionJobInfo compaction_job_info;
|
||||
ASSERT_OK(
|
||||
db->CompactFiles(co, l0_files_1, 0, -1, nullptr, &compaction_job_info));
|
||||
ASSERT_EQ(compaction_job_info.base_input_level, 0);
|
||||
ASSERT_EQ(compaction_job_info.cf_id, db->DefaultColumnFamily()->GetID());
|
||||
ASSERT_EQ(compaction_job_info.cf_name, db->DefaultColumnFamily()->GetName());
|
||||
ASSERT_EQ(compaction_job_info.compaction_reason,
|
||||
CompactionReason::kManualCompaction);
|
||||
ASSERT_EQ(compaction_job_info.compression, CompressionType::kLZ4Compression);
|
||||
ASSERT_EQ(compaction_job_info.output_level, 0);
|
||||
ASSERT_OK(compaction_job_info.status);
|
||||
// no assertion failure
|
||||
delete db;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -67,10 +67,11 @@ class CompactedDBImpl : public DBImpl {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
}
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
|
||||
+8
-2
@@ -250,6 +250,12 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
if (max_subcompactions_ == 0) {
|
||||
max_subcompactions_ = immutable_cf_options_.max_subcompactions;
|
||||
}
|
||||
if (!bottommost_level_) {
|
||||
// Currently we only enable dictionary compression during compaction to the
|
||||
// bottommost level.
|
||||
output_compression_opts_.max_dict_bytes = 0;
|
||||
output_compression_opts_.zstd_max_train_bytes = 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
@@ -373,8 +379,8 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) {
|
||||
// We've advanced far enough
|
||||
if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) {
|
||||
// Key falls in this file's range, so definitely
|
||||
// exists beyond output level
|
||||
// Key falls in this file's range, so it may
|
||||
// exist beyond output level
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
+99
-59
@@ -9,6 +9,23 @@
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
#define DEFINITELY_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) <= (snapshot) && \
|
||||
(snapshot_checker_ == nullptr || \
|
||||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) == \
|
||||
SnapshotCheckerResult::kInSnapshot)))
|
||||
|
||||
#define DEFINITELY_NOT_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) > (snapshot) || \
|
||||
(snapshot_checker_ != nullptr && \
|
||||
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) == \
|
||||
SnapshotCheckerResult::kNotInSnapshot)))
|
||||
|
||||
#define IN_EARLIEST_SNAPSHOT(seq) \
|
||||
((seq) <= earliest_snapshot_ && \
|
||||
(snapshot_checker_ == nullptr || LIKELY(IsInEarliestSnapshot(seq))))
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -18,7 +35,7 @@ CompactionIterator::CompactionIterator(
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg, const Compaction* compaction,
|
||||
CompactionRangeDelAggregator* range_del_agg, const Compaction* compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
@@ -36,7 +53,7 @@ CompactionIterator::CompactionIterator(
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
@@ -55,42 +72,37 @@ CompactionIterator::CompactionIterator(
|
||||
compaction_filter_(compaction_filter),
|
||||
shutting_down_(shutting_down),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
ignore_snapshots_(false),
|
||||
current_user_key_sequence_(0),
|
||||
current_user_key_snapshot_(0),
|
||||
merge_out_iter_(merge_helper_),
|
||||
current_key_committed_(false) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
assert(snapshots_ != nullptr);
|
||||
bottommost_level_ =
|
||||
compaction_ == nullptr ? false : compaction_->bottommost_level();
|
||||
if (compaction_ != nullptr) {
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
}
|
||||
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_iter_ = snapshots_->end();
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_iter_ = snapshots_->begin();
|
||||
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));
|
||||
assert(snapshots_->at(i - 1) < snapshots_->at(i));
|
||||
}
|
||||
#endif
|
||||
if (compaction_filter_ != nullptr) {
|
||||
if (compaction_filter_->IgnoreSnapshots()) {
|
||||
ignore_snapshots_ = true;
|
||||
}
|
||||
} else {
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:AfterInit", compaction_.get());
|
||||
}
|
||||
|
||||
CompactionIterator::~CompactionIterator() {
|
||||
@@ -160,12 +172,7 @@ void CompactionIterator::Next() {
|
||||
void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
Slice* skip_until) {
|
||||
if (compaction_filter_ != nullptr &&
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex) &&
|
||||
(visible_at_tip_ || ignore_snapshots_ ||
|
||||
ikey_.sequence > latest_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
latest_snapshot_))))) {
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex)) {
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
@@ -241,6 +248,7 @@ void CompactionIterator::NextFromInput() {
|
||||
valid_ = true;
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:ProcessKV", &ikey_);
|
||||
|
||||
// Update input statistics
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion) {
|
||||
@@ -269,9 +277,7 @@ void CompactionIterator::NextFromInput() {
|
||||
has_outputted_key_ = false;
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
current_key_committed_ =
|
||||
(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber));
|
||||
current_key_committed_ = KeyCommitted(ikey_.sequence);
|
||||
|
||||
// Apply the compaction filter to the first committed version of the user
|
||||
// key.
|
||||
@@ -293,8 +299,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// to query snapshot_checker_ in that case.
|
||||
if (UNLIKELY(!current_key_committed_)) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
current_key_committed_ =
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber);
|
||||
current_key_committed_ = KeyCommitted(ikey_.sequence);
|
||||
// Apply the compaction filter to the first committed version of the
|
||||
// user key.
|
||||
if (current_key_committed_) {
|
||||
@@ -378,10 +383,8 @@ void CompactionIterator::NextFromInput() {
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(next_ikey.sequence,
|
||||
prev_snapshot)))) {
|
||||
if (prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot)) {
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
// We encountered two SingleDeletes in a row. This could be due to
|
||||
// unexpected user input.
|
||||
@@ -393,11 +396,8 @@ void CompactionIterator::NextFromInput() {
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if (has_outputted_key_ ||
|
||||
(ikey_.sequence <= earliest_write_conflict_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence,
|
||||
earliest_write_conflict_snapshot_))))) {
|
||||
DEFINITELY_IN_SNAPSHOT(
|
||||
ikey_.sequence, earliest_write_conflict_snapshot_)) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
// value. It is safe to drop both records since we've already
|
||||
// outputted a key in this snapshot, or there is no earlier
|
||||
@@ -407,7 +407,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// is an unexpected Merge or Delete. We will compact it out
|
||||
// either way. We will maintain counts of how many mismatches
|
||||
// happened
|
||||
if (next_ikey.type != kTypeValue) {
|
||||
if (next_ikey.type != kTypeValue &&
|
||||
next_ikey.type != kTypeBlobIndex) {
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
@@ -444,10 +445,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// iteration. If the next key is corrupt, we return before the
|
||||
// comparison, so the value of has_current_user_key does not matter.
|
||||
has_current_user_key_ = false;
|
||||
if (compaction_ != nullptr && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
if (compaction_ != nullptr && IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// Key doesn't exist outside of this range.
|
||||
@@ -466,24 +464,37 @@ void CompactionIterator::NextFromInput() {
|
||||
if (valid_) {
|
||||
at_next_ = true;
|
||||
}
|
||||
} else if (last_snapshot == current_user_key_snapshot_) {
|
||||
} else if (last_snapshot == current_user_key_snapshot_ ||
|
||||
(last_snapshot > 0 &&
|
||||
last_snapshot < current_user_key_snapshot_)) {
|
||||
// If the earliest snapshot is which this key is visible in
|
||||
// is the same as the visibility of a previous instance of the
|
||||
// same key, then this kv is not visible in any snapshot.
|
||||
// Hidden by an newer entry for same user key
|
||||
// TODO(noetzli): why not > ?
|
||||
//
|
||||
// Note: Dropping this key will not affect TransactionDB write-conflict
|
||||
// checking since there has already been a record returned for this key
|
||||
// in this snapshot.
|
||||
assert(last_sequence >= current_user_key_sequence_);
|
||||
|
||||
// Note2: if last_snapshot < current_user_key_snapshot, it can only
|
||||
// mean last_snapshot is released between we process last value and
|
||||
// this value, and findEarliestVisibleSnapshot returns the next snapshot
|
||||
// as current_user_key_snapshot. In this case last value and current
|
||||
// value are both in current_user_key_snapshot currently.
|
||||
// Although last_snapshot is released we might still get a definitive
|
||||
// response when key sequence number changes, e.g., when seq is determined
|
||||
// too old and visible in all snapshots.
|
||||
assert(last_snapshot == current_user_key_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
snapshot_checker_->CheckInSnapshot(current_user_key_sequence_,
|
||||
last_snapshot) !=
|
||||
SnapshotCheckerResult::kNotInSnapshot));
|
||||
|
||||
++iter_stats_.num_record_drop_hidden; // (A)
|
||||
input_->Next();
|
||||
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
||||
ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
@@ -520,13 +531,10 @@ void CompactionIterator::NextFromInput() {
|
||||
input_->Next();
|
||||
// Skip over all versions of this key that happen to occur in the same snapshot
|
||||
// range as the delete
|
||||
while (input_->Valid() &&
|
||||
ParseInternalKey(input_->key(), &next_ikey) &&
|
||||
while (input_->Valid() && ParseInternalKey(input_->key(), &next_ikey) &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key) &&
|
||||
(prev_snapshot == 0 || next_ikey.sequence > prev_snapshot ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(next_ikey.sequence,
|
||||
prev_snapshot))))) {
|
||||
(prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot))) {
|
||||
input_->Next();
|
||||
}
|
||||
// If you find you still need to output a row with this key, we need to output the
|
||||
@@ -617,14 +625,9 @@ void CompactionIterator::PrepareOutput() {
|
||||
//
|
||||
// Can we do the same for levels above bottom level as long as
|
||||
// KeyNotExistsBeyondOutputLevel() return true?
|
||||
if ((compaction_ != nullptr &&
|
||||
!compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
bottommost_level_ && valid_ && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr || LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence, earliest_snapshot_))) &&
|
||||
ikey_.type != kTypeMerge &&
|
||||
!cmp_->Equal(compaction_->GetLargestUserKey(), ikey_.user_key)) {
|
||||
if ((compaction_ != nullptr && !compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() && bottommost_level_ && valid_ &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) && ikey_.type != kTypeMerge) {
|
||||
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
||||
ikey_.sequence = 0;
|
||||
current_key_.UpdateInternalKey(0, ikey_.type);
|
||||
@@ -642,12 +645,23 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
*prev_snapshot = *std::prev(snapshots_iter);
|
||||
assert(*prev_snapshot < in);
|
||||
}
|
||||
if (snapshot_checker_ == nullptr) {
|
||||
return snapshots_iter != snapshots_->end()
|
||||
? *snapshots_iter : kMaxSequenceNumber;
|
||||
}
|
||||
bool has_released_snapshot = !released_snapshots_.empty();
|
||||
for (; snapshots_iter != snapshots_->end(); ++snapshots_iter) {
|
||||
auto cur = *snapshots_iter;
|
||||
assert(in <= cur);
|
||||
if (snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(in, cur)) {
|
||||
// Skip if cur is in released_snapshots.
|
||||
if (has_released_snapshot && released_snapshots_.count(cur) > 0) {
|
||||
continue;
|
||||
}
|
||||
auto res = snapshot_checker_->CheckInSnapshot(in, cur);
|
||||
if (res == SnapshotCheckerResult::kInSnapshot) {
|
||||
return cur;
|
||||
} else if (res == SnapshotCheckerResult::kSnapshotReleased) {
|
||||
released_snapshots_.insert(cur);
|
||||
}
|
||||
*prev_snapshot = cur;
|
||||
}
|
||||
@@ -661,4 +675,30 @@ inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
|
||||
(ikey_.sequence < preserve_deletes_seqnum_);
|
||||
}
|
||||
|
||||
bool CompactionIterator::IsInEarliestSnapshot(SequenceNumber sequence) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
assert(earliest_snapshot_ == kMaxSequenceNumber ||
|
||||
(earliest_snapshot_iter_ != snapshots_->end() &&
|
||||
*earliest_snapshot_iter_ == earliest_snapshot_));
|
||||
auto in_snapshot =
|
||||
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
|
||||
while (UNLIKELY(in_snapshot == SnapshotCheckerResult::kSnapshotReleased)) {
|
||||
// Avoid the the current earliest_snapshot_ being return as
|
||||
// earliest visible snapshot for the next value. So if a value's sequence
|
||||
// is zero-ed out by PrepareOutput(), the next value will be compact out.
|
||||
released_snapshots_.insert(earliest_snapshot_);
|
||||
earliest_snapshot_iter_++;
|
||||
|
||||
if (earliest_snapshot_iter_ == snapshots_->end()) {
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
} else {
|
||||
earliest_snapshot_ = *earliest_snapshot_iter_;
|
||||
}
|
||||
in_snapshot =
|
||||
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
|
||||
}
|
||||
assert(in_snapshot != SnapshotCheckerResult::kSnapshotReleased);
|
||||
return in_snapshot == SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction.h"
|
||||
@@ -64,7 +65,7 @@ class CompactionIterator {
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
@@ -77,7 +78,7 @@ class CompactionIterator {
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
@@ -132,16 +133,31 @@ class CompactionIterator {
|
||||
// or seqnum be zero-ed out even if all other conditions for it are met.
|
||||
inline bool ikeyNotNeededForIncrementalSnapshot();
|
||||
|
||||
inline bool KeyCommitted(SequenceNumber sequence) {
|
||||
return snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->CheckInSnapshot(sequence, kMaxSequenceNumber) ==
|
||||
SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
bool IsInEarliestSnapshot(SequenceNumber sequence);
|
||||
|
||||
InternalIterator* input_;
|
||||
const Comparator* cmp_;
|
||||
MergeHelper* merge_helper_;
|
||||
const std::vector<SequenceNumber>* snapshots_;
|
||||
// List of snapshots released during compaction.
|
||||
// findEarliestVisibleSnapshot() find them out from return of
|
||||
// snapshot_checker, and make sure they will not be returned as
|
||||
// earliest visible snapshot of an older value.
|
||||
// See WritePreparedTransactionTest::ReleaseSnapshotDuringCompaction3.
|
||||
std::unordered_set<SequenceNumber> released_snapshots_;
|
||||
std::vector<SequenceNumber>::const_iterator earliest_snapshot_iter_;
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
Env* env_;
|
||||
bool report_detailed_time_;
|
||||
bool expect_valid_internal_key_;
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
CompactionRangeDelAggregator* range_del_agg_;
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
@@ -151,7 +167,6 @@ class CompactionIterator {
|
||||
bool visible_at_tip_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
bool ignore_snapshots_;
|
||||
|
||||
// State
|
||||
//
|
||||
|
||||
@@ -112,39 +112,39 @@ class LoggingForwardVectorIterator : public InternalIterator {
|
||||
assert(keys_.size() == values_.size());
|
||||
}
|
||||
|
||||
virtual bool Valid() const override { return current_ < keys_.size(); }
|
||||
bool Valid() const override { return current_ < keys_.size(); }
|
||||
|
||||
virtual void SeekToFirst() override {
|
||||
void SeekToFirst() override {
|
||||
log.emplace_back(Action::Type::SEEK_TO_FIRST);
|
||||
current_ = 0;
|
||||
}
|
||||
virtual void SeekToLast() override { assert(false); }
|
||||
void SeekToLast() override { assert(false); }
|
||||
|
||||
virtual void Seek(const Slice& target) override {
|
||||
void Seek(const Slice& target) override {
|
||||
log.emplace_back(Action::Type::SEEK, target.ToString());
|
||||
current_ = std::lower_bound(keys_.begin(), keys_.end(), target.ToString()) -
|
||||
keys_.begin();
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& /*target*/) override { assert(false); }
|
||||
void SeekForPrev(const Slice& /*target*/) override { assert(false); }
|
||||
|
||||
virtual void Next() override {
|
||||
void Next() override {
|
||||
assert(Valid());
|
||||
log.emplace_back(Action::Type::NEXT);
|
||||
current_++;
|
||||
}
|
||||
virtual void Prev() override { assert(false); }
|
||||
void Prev() override { assert(false); }
|
||||
|
||||
virtual Slice key() const override {
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return Slice(keys_[current_]);
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return Slice(values_[current_]);
|
||||
}
|
||||
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
std::vector<Action> log;
|
||||
|
||||
@@ -158,22 +158,20 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
public:
|
||||
FakeCompaction() = default;
|
||||
|
||||
virtual int level(size_t /*compaction_input_level*/) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
int level(size_t /*compaction_input_level*/) const override { return 0; }
|
||||
bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& /*user_key*/,
|
||||
std::vector<size_t>* /*level_ptrs*/) const override {
|
||||
return is_bottommost_level || key_not_exists_beyond_output_level;
|
||||
}
|
||||
virtual bool bottommost_level() const override { return is_bottommost_level; }
|
||||
virtual int number_levels() const override { return 1; }
|
||||
virtual Slice GetLargestUserKey() const override {
|
||||
bool bottommost_level() const override { return is_bottommost_level; }
|
||||
int number_levels() const override { return 1; }
|
||||
Slice GetLargestUserKey() const override {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
virtual bool allow_ingest_behind() const override { return false; }
|
||||
bool allow_ingest_behind() const override { return false; }
|
||||
|
||||
virtual bool preserve_deletes() const override { return false; }
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
@@ -190,13 +188,17 @@ class TestSnapshotChecker : public SnapshotChecker {
|
||||
: last_committed_sequence_(last_committed_sequence),
|
||||
snapshots_(snapshots) {}
|
||||
|
||||
bool IsInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot_seq) const override {
|
||||
SnapshotCheckerResult CheckInSnapshot(
|
||||
SequenceNumber seq, SequenceNumber snapshot_seq) const override {
|
||||
if (snapshot_seq == kMaxSequenceNumber) {
|
||||
return seq <= last_committed_sequence_;
|
||||
return seq <= last_committed_sequence_
|
||||
? SnapshotCheckerResult::kInSnapshot
|
||||
: SnapshotCheckerResult::kNotInSnapshot;
|
||||
}
|
||||
assert(snapshots_.count(snapshot_seq) > 0);
|
||||
return seq <= snapshots_.at(snapshot_seq);
|
||||
return seq <= snapshots_.at(snapshot_seq)
|
||||
? SnapshotCheckerResult::kInSnapshot
|
||||
: SnapshotCheckerResult::kNotInSnapshot;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -221,10 +223,15 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
MergeOperator* merge_op = nullptr, CompactionFilter* filter = nullptr,
|
||||
bool bottommost_level = false,
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
std::unique_ptr<InternalIterator> unfragmented_range_del_iter(
|
||||
new test::VectorIterator(range_del_ks, range_del_vs));
|
||||
range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
|
||||
ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));
|
||||
auto tombstone_list = std::make_shared<FragmentedRangeTombstoneList>(
|
||||
std::move(unfragmented_range_del_iter), icmp_);
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
new FragmentedRangeTombstoneIterator(tombstone_list, icmp_,
|
||||
kMaxSequenceNumber));
|
||||
range_del_agg_.reset(new CompactionRangeDelAggregator(&icmp_, snapshots_));
|
||||
range_del_agg_->AddTombstones(std::move(range_del_iter));
|
||||
|
||||
std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
|
||||
if (filter || bottommost_level) {
|
||||
@@ -292,7 +299,7 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
std::unique_ptr<MergeHelper> merge_helper_;
|
||||
std::unique_ptr<LoggingForwardVectorIterator> iter_;
|
||||
std::unique_ptr<CompactionIterator> c_iter_;
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg_;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
|
||||
std::unique_ptr<SnapshotChecker> snapshot_checker_;
|
||||
std::atomic<bool> shutting_down_{false};
|
||||
FakeCompaction* compaction_proxy_;
|
||||
@@ -368,10 +375,9 @@ TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
|
||||
TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
@@ -551,10 +557,9 @@ TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
|
||||
|
||||
+124
-149
@@ -36,7 +36,7 @@
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator_v2.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
@@ -157,7 +157,6 @@ struct CompactionJob::SubcompactionState {
|
||||
uint64_t overlapped_bytes = 0;
|
||||
// A flag determine whether the key has been seen in ShouldStopBefore()
|
||||
bool seen_key = false;
|
||||
std::string compression_dict;
|
||||
|
||||
SubcompactionState(Compaction* c, Slice* _start, Slice* _end,
|
||||
uint64_t size = 0)
|
||||
@@ -173,8 +172,7 @@ struct CompactionJob::SubcompactionState {
|
||||
approx_size(size),
|
||||
grandparent_index(0),
|
||||
overlapped_bytes(0),
|
||||
seen_key(false),
|
||||
compression_dict() {
|
||||
seen_key(false) {
|
||||
assert(compaction != nullptr);
|
||||
}
|
||||
|
||||
@@ -197,7 +195,6 @@ struct CompactionJob::SubcompactionState {
|
||||
grandparent_index = std::move(o.grandparent_index);
|
||||
overlapped_bytes = std::move(o.overlapped_bytes);
|
||||
seen_key = std::move(o.seen_key);
|
||||
compression_dict = std::move(o.compression_dict);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -604,7 +601,14 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
|
||||
compaction_stats_.micros = env_->NowMicros() - start_micros;
|
||||
compaction_stats_.cpu_micros = 0;
|
||||
for (size_t i = 0; i < compact_->sub_compact_states.size(); i++) {
|
||||
compaction_stats_.cpu_micros +=
|
||||
compact_->sub_compact_states[i].compaction_job_stats.cpu_micros;
|
||||
}
|
||||
|
||||
MeasureTime(stats_, COMPACTION_TIME, compaction_stats_.micros);
|
||||
MeasureTime(stats_, COMPACTION_CPU_TIME, compaction_stats_.cpu_micros);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run:BeforeVerify");
|
||||
|
||||
@@ -767,6 +771,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_finished"
|
||||
<< "compaction_time_micros" << compaction_stats_.micros
|
||||
<< "compaction_time_cpu_micros" << compaction_stats_.cpu_micros
|
||||
<< "output_level" << compact_->compaction->output_level()
|
||||
<< "num_output_files" << compact_->NumOutputFiles()
|
||||
<< "total_output_size" << compact_->total_bytes << "num_input_records"
|
||||
@@ -804,16 +809,35 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact != nullptr);
|
||||
|
||||
uint64_t prev_cpu_micros = env_->NowCPUNanos() / 1000;
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
RangeDelAggregatorV2 range_del_agg_v2(&cfd->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
auto* range_del_agg =
|
||||
range_del_agg_v2.DelegateToRangeDelAggregator(existing_snapshots_);
|
||||
|
||||
// Create compaction filter and fail the compaction if
|
||||
// IgnoreSnapshots() = false because it is not supported anymore
|
||||
const CompactionFilter* compaction_filter =
|
||||
cfd->ioptions()->compaction_filter;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_from_factory = nullptr;
|
||||
if (compaction_filter == nullptr) {
|
||||
compaction_filter_from_factory =
|
||||
sub_compact->compaction->CreateCompactionFilter();
|
||||
compaction_filter = compaction_filter_from_factory.get();
|
||||
}
|
||||
if (compaction_filter != nullptr && !compaction_filter->IgnoreSnapshots()) {
|
||||
sub_compact->status = Status::NotSupported(
|
||||
"CompactionFilter::IgnoreSnapshots() = false is not supported "
|
||||
"anymore.");
|
||||
return;
|
||||
}
|
||||
|
||||
CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
|
||||
existing_snapshots_);
|
||||
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, &range_del_agg_v2, env_optiosn_for_read_));
|
||||
sub_compact->compaction, &range_del_agg, env_optiosn_for_read_));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -825,58 +849,19 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
uint64_t prev_fsync_nanos = 0;
|
||||
uint64_t prev_range_sync_nanos = 0;
|
||||
uint64_t prev_prepare_write_nanos = 0;
|
||||
uint64_t prev_cpu_write_nanos = 0;
|
||||
uint64_t prev_cpu_read_nanos = 0;
|
||||
if (measure_io_stats_) {
|
||||
prev_perf_level = GetPerfLevel();
|
||||
SetPerfLevel(PerfLevel::kEnableTime);
|
||||
SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
|
||||
prev_write_nanos = IOSTATS(write_nanos);
|
||||
prev_fsync_nanos = IOSTATS(fsync_nanos);
|
||||
prev_range_sync_nanos = IOSTATS(range_sync_nanos);
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
prev_cpu_write_nanos = IOSTATS(cpu_write_nanos);
|
||||
prev_cpu_read_nanos = IOSTATS(cpu_read_nanos);
|
||||
}
|
||||
|
||||
const MutableCFOptions* mutable_cf_options =
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
|
||||
// To build compression dictionary, we sample the first output file, assuming
|
||||
// it'll reach the maximum length. We optionally pass these samples through
|
||||
// zstd's dictionary trainer, or just use them directly. Then, the dictionary
|
||||
// is used for compressing subsequent output files in the same subcompaction.
|
||||
const bool kUseZstdTrainer =
|
||||
sub_compact->compaction->output_compression_opts().zstd_max_train_bytes >
|
||||
0;
|
||||
const size_t kSampleBytes =
|
||||
kUseZstdTrainer
|
||||
? sub_compact->compaction->output_compression_opts()
|
||||
.zstd_max_train_bytes
|
||||
: sub_compact->compaction->output_compression_opts().max_dict_bytes;
|
||||
const int kSampleLenShift = 6; // 2^6 = 64-byte samples
|
||||
std::set<size_t> sample_begin_offsets;
|
||||
if (bottommost_level_ && kSampleBytes > 0) {
|
||||
const size_t kMaxSamples = kSampleBytes >> kSampleLenShift;
|
||||
const size_t kOutFileLen =
|
||||
static_cast<size_t>(MaxFileSizeForLevel(*mutable_cf_options,
|
||||
compact_->compaction->output_level(),
|
||||
cfd->ioptions()->compaction_style,
|
||||
compact_->compaction->GetInputBaseLevel(),
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes));
|
||||
if (kOutFileLen != port::kMaxSizet) {
|
||||
const size_t kOutFileNumSamples = kOutFileLen >> kSampleLenShift;
|
||||
Random64 generator{versions_->NewFileNumber()};
|
||||
for (size_t i = 0; i < kMaxSamples; ++i) {
|
||||
sample_begin_offsets.insert(
|
||||
static_cast<size_t>(generator.Uniform(kOutFileNumSamples))
|
||||
<< kSampleLenShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto compaction_filter = cfd->ioptions()->compaction_filter;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_from_factory = nullptr;
|
||||
if (compaction_filter == nullptr) {
|
||||
compaction_filter_from_factory =
|
||||
sub_compact->compaction->CreateCompactionFilter();
|
||||
compaction_filter = compaction_filter_from_factory.get();
|
||||
}
|
||||
MergeHelper merge(
|
||||
env_, cfd->user_comparator(), cfd->ioptions()->merge_operator,
|
||||
compaction_filter, db_options_.info_log.get(),
|
||||
@@ -902,8 +887,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_), false,
|
||||
range_del_agg, sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
preserve_deletes_seqnum_));
|
||||
&range_del_agg, sub_compact->compaction, compaction_filter,
|
||||
shutting_down_, preserve_deletes_seqnum_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -914,12 +899,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->current_output_file_size);
|
||||
}
|
||||
const auto& c_iter_stats = c_iter->iter_stats();
|
||||
auto sample_begin_offset_iter = sample_begin_offsets.cbegin();
|
||||
// data_begin_offset and dict_sample_data are only valid while generating
|
||||
// dictionary from the first output file.
|
||||
size_t data_begin_offset = 0;
|
||||
std::string dict_sample_data;
|
||||
dict_sample_data.reserve(kSampleBytes);
|
||||
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
@@ -955,55 +934,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
key, c_iter->ikey().sequence);
|
||||
sub_compact->num_output_records++;
|
||||
|
||||
if (sub_compact->outputs.size() == 1) { // first output file
|
||||
// Check if this key/value overlaps any sample intervals; if so, appends
|
||||
// overlapping portions to the dictionary.
|
||||
for (const auto& data_elmt : {key, value}) {
|
||||
size_t data_end_offset = data_begin_offset + data_elmt.size();
|
||||
while (sample_begin_offset_iter != sample_begin_offsets.cend() &&
|
||||
*sample_begin_offset_iter < data_end_offset) {
|
||||
size_t sample_end_offset =
|
||||
*sample_begin_offset_iter + (1 << kSampleLenShift);
|
||||
// Invariant: Because we advance sample iterator while processing the
|
||||
// data_elmt containing the sample's last byte, the current sample
|
||||
// cannot end before the current data_elmt.
|
||||
assert(data_begin_offset < sample_end_offset);
|
||||
|
||||
size_t data_elmt_copy_offset, data_elmt_copy_len;
|
||||
if (*sample_begin_offset_iter <= data_begin_offset) {
|
||||
// The sample starts before data_elmt starts, so take bytes starting
|
||||
// at the beginning of data_elmt.
|
||||
data_elmt_copy_offset = 0;
|
||||
} else {
|
||||
// data_elmt starts before the sample starts, so take bytes starting
|
||||
// at the below offset into data_elmt.
|
||||
data_elmt_copy_offset =
|
||||
*sample_begin_offset_iter - data_begin_offset;
|
||||
}
|
||||
if (sample_end_offset <= data_end_offset) {
|
||||
// The sample ends before data_elmt ends, so take as many bytes as
|
||||
// needed.
|
||||
data_elmt_copy_len =
|
||||
sample_end_offset - (data_begin_offset + data_elmt_copy_offset);
|
||||
} else {
|
||||
// data_elmt ends before the sample ends, so take all remaining
|
||||
// bytes in data_elmt.
|
||||
data_elmt_copy_len =
|
||||
data_end_offset - (data_begin_offset + data_elmt_copy_offset);
|
||||
}
|
||||
dict_sample_data.append(&data_elmt.data()[data_elmt_copy_offset],
|
||||
data_elmt_copy_len);
|
||||
if (sample_end_offset > data_end_offset) {
|
||||
// Didn't finish sample. Try to finish it with the next data_elmt.
|
||||
break;
|
||||
}
|
||||
// Next sample may require bytes from same data_elmt.
|
||||
sample_begin_offset_iter++;
|
||||
}
|
||||
data_begin_offset = data_end_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Close output file if it is big enough. Two possibilities determine it's
|
||||
// time to close it: (1) the current key should be this file's last key, (2)
|
||||
// the next key should not be in this file.
|
||||
@@ -1041,22 +971,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
status =
|
||||
FinishCompactionOutputFile(input_status, sub_compact, range_del_agg,
|
||||
FinishCompactionOutputFile(input_status, sub_compact, &range_del_agg,
|
||||
&range_del_out_stats, next_key);
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// Use samples from first output file to create dictionary for
|
||||
// compression of subsequent files.
|
||||
if (kUseZstdTrainer) {
|
||||
sub_compact->compression_dict = ZSTD_TrainDictionary(
|
||||
dict_sample_data, kSampleLenShift,
|
||||
sub_compact->compaction->output_compression_opts()
|
||||
.max_dict_bytes);
|
||||
} else {
|
||||
sub_compact->compression_dict = std::move(dict_sample_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,8 +1010,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
if (status.ok() && sub_compact->builder == nullptr &&
|
||||
sub_compact->outputs.size() == 0 &&
|
||||
!range_del_agg->IsEmpty()) {
|
||||
sub_compact->outputs.size() == 0 && !range_del_agg.IsEmpty()) {
|
||||
// handle subcompaction containing only range deletions
|
||||
status = OpenCompactionOutputFile(sub_compact);
|
||||
}
|
||||
@@ -1102,7 +1019,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// close the output file.
|
||||
if (sub_compact->builder != nullptr) {
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
Status s = FinishCompactionOutputFile(status, sub_compact, range_del_agg,
|
||||
Status s = FinishCompactionOutputFile(status, sub_compact, &range_del_agg,
|
||||
&range_del_out_stats);
|
||||
if (status.ok()) {
|
||||
status = s;
|
||||
@@ -1110,6 +1027,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
env_->NowCPUNanos() / 1000 - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
IOSTATS(write_nanos) - prev_write_nanos;
|
||||
@@ -1119,7 +1039,11 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
IOSTATS(range_sync_nanos) - prev_range_sync_nanos;
|
||||
sub_compact->compaction_job_stats.file_prepare_write_nanos +=
|
||||
IOSTATS(prepare_write_nanos) - prev_prepare_write_nanos;
|
||||
if (prev_perf_level != PerfLevel::kEnableTime) {
|
||||
sub_compact->compaction_job_stats.cpu_micros -=
|
||||
(IOSTATS(cpu_write_nanos) - prev_cpu_write_nanos +
|
||||
IOSTATS(cpu_read_nanos) - prev_cpu_read_nanos) /
|
||||
1000;
|
||||
if (prev_perf_level != PerfLevel::kEnableTimeAndCPUTimeExceptForMutex) {
|
||||
SetPerfLevel(prev_perf_level);
|
||||
}
|
||||
}
|
||||
@@ -1168,7 +1092,7 @@ void CompactionJob::RecordDroppedKeys(
|
||||
|
||||
Status CompactionJob::FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
CompactionIterationStats* range_del_out_stats,
|
||||
const Slice* next_table_min_key /* = nullptr */) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -1192,10 +1116,12 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
Slice lower_bound_guard, upper_bound_guard;
|
||||
std::string smallest_user_key;
|
||||
const Slice *lower_bound, *upper_bound;
|
||||
bool lower_bound_from_sub_compact = false;
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// For the first output table, include range tombstones before the min key
|
||||
// but after the subcompaction boundary.
|
||||
lower_bound = sub_compact->start;
|
||||
lower_bound_from_sub_compact = true;
|
||||
} else if (meta->smallest.size() > 0) {
|
||||
// For subsequent output tables, only include range tombstones from min
|
||||
// key onwards since the previous file was extended to contain range
|
||||
@@ -1207,10 +1133,19 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
lower_bound = nullptr;
|
||||
}
|
||||
if (next_table_min_key != nullptr) {
|
||||
// This isn't the last file in the subcompaction, so extend until the next
|
||||
// file starts.
|
||||
// This may be the last file in the subcompaction in some cases, so we
|
||||
// need to compare the end key of subcompaction with the next file start
|
||||
// key. When the end key is chosen by the subcompaction, we know that
|
||||
// it must be the biggest key in output file. Therefore, it is safe to
|
||||
// use the smaller key as the upper bound of the output file, to ensure
|
||||
// that there is no overlapping between different output files.
|
||||
upper_bound_guard = ExtractUserKey(*next_table_min_key);
|
||||
upper_bound = &upper_bound_guard;
|
||||
if (sub_compact->end != nullptr &&
|
||||
ucmp->Compare(upper_bound_guard, *sub_compact->end) >= 0) {
|
||||
upper_bound = sub_compact->end;
|
||||
} else {
|
||||
upper_bound = &upper_bound_guard;
|
||||
}
|
||||
} else {
|
||||
// This is the last file in the subcompaction, so extend until the
|
||||
// subcompaction ends.
|
||||
@@ -1220,11 +1155,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
if (existing_snapshots_.size() > 0) {
|
||||
earliest_snapshot = existing_snapshots_[0];
|
||||
}
|
||||
auto it = range_del_agg->NewIterator();
|
||||
if (lower_bound != nullptr) {
|
||||
it->Seek(*lower_bound);
|
||||
}
|
||||
|
||||
bool has_overlapping_endpoints;
|
||||
if (upper_bound != nullptr && meta->largest.size() > 0) {
|
||||
has_overlapping_endpoints =
|
||||
@@ -1232,6 +1162,24 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
} else {
|
||||
has_overlapping_endpoints = false;
|
||||
}
|
||||
|
||||
// The end key of the subcompaction must be bigger or equal to the upper
|
||||
// bound. If the end of subcompaction is null or the upper bound is null,
|
||||
// it means that this file is the last file in the compaction. So there
|
||||
// will be no overlapping between this file and others.
|
||||
assert(sub_compact->end == nullptr ||
|
||||
upper_bound == nullptr ||
|
||||
ucmp->Compare(*upper_bound , *sub_compact->end) <= 0);
|
||||
auto it = range_del_agg->NewIterator(lower_bound, upper_bound,
|
||||
has_overlapping_endpoints);
|
||||
// Position the range tombstone output iterator. There may be tombstone
|
||||
// fragments that are entirely out of range, so make sure that we do not
|
||||
// include those.
|
||||
if (lower_bound != nullptr) {
|
||||
it->Seek(*lower_bound);
|
||||
} else {
|
||||
it->SeekToFirst();
|
||||
}
|
||||
for (; it->Valid(); it->Next()) {
|
||||
auto tombstone = it->Tombstone();
|
||||
if (upper_bound != nullptr) {
|
||||
@@ -1257,6 +1205,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
|
||||
auto kv = tombstone.Serialize();
|
||||
assert(lower_bound == nullptr ||
|
||||
ucmp->Compare(*lower_bound, kv.second) < 0);
|
||||
sub_compact->builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey smallest_candidate = std::move(kv.first);
|
||||
if (lower_bound != nullptr &&
|
||||
@@ -1265,11 +1215,24 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// (the max key in the previous table or subcompaction) in order for
|
||||
// files to appear key-space partitioned.
|
||||
//
|
||||
// Choose lowest seqnum so this file's smallest internal key comes
|
||||
// after the previous file's/subcompaction's largest. The fake seqnum
|
||||
// is OK because the read path's file-picking code only considers user
|
||||
// key.
|
||||
smallest_candidate = InternalKey(*lower_bound, 0, kTypeRangeDeletion);
|
||||
// When lower_bound is chosen by a subcompaction, we know that
|
||||
// subcompactions over smaller keys cannot contain any keys at
|
||||
// lower_bound. We also know that smaller subcompactions exist, because
|
||||
// otherwise the subcompaction woud be unbounded on the left. As a
|
||||
// result, we know that no other files on the output level will contain
|
||||
// actual keys at lower_bound (an output file may have a largest key of
|
||||
// lower_bound@kMaxSequenceNumber, but this only indicates a large range
|
||||
// tombstone was truncated). Therefore, it is safe to use the
|
||||
// tombstone's sequence number, to ensure that keys at lower_bound at
|
||||
// lower levels are covered by truncated tombstones.
|
||||
//
|
||||
// If lower_bound was chosen by the smallest data key in the file,
|
||||
// choose lowest seqnum so this file's smallest internal key comes after
|
||||
// the previous file's largest. The fake seqnum is OK because the read
|
||||
// path's file-picking code only considers user key.
|
||||
smallest_candidate = InternalKey(
|
||||
*lower_bound, lower_bound_from_sub_compact ? tombstone.seq_ : 0,
|
||||
kTypeRangeDeletion);
|
||||
}
|
||||
InternalKey largest_candidate = tombstone.SerializeEndKey();
|
||||
if (upper_bound != nullptr &&
|
||||
@@ -1291,9 +1254,23 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
largest_candidate =
|
||||
InternalKey(*upper_bound, kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
SequenceNumber smallest_ikey_seqnum = kMaxSequenceNumber;
|
||||
if (meta->smallest.size() > 0) {
|
||||
smallest_ikey_seqnum = GetInternalKeySeqno(meta->smallest.Encode());
|
||||
}
|
||||
#endif
|
||||
meta->UpdateBoundariesForRange(smallest_candidate, largest_candidate,
|
||||
tombstone.seq_,
|
||||
cfd->internal_comparator());
|
||||
|
||||
// The smallest key in a file is used for range tombstone truncation, so
|
||||
// it cannot have a seqnum of 0 (unless the smallest data key in a file
|
||||
// has a seqnum of 0). Otherwise, the truncated tombstone may expose
|
||||
// deleted keys at lower levels.
|
||||
assert(smallest_ikey_seqnum == 0 ||
|
||||
ExtractInternalKeyFooter(meta->smallest.Encode()) !=
|
||||
PackSequenceAndType(0, kTypeRangeDeletion));
|
||||
}
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
}
|
||||
@@ -1371,10 +1348,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
auto sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm && meta != nullptr && meta->fd.GetPathId() == 0) {
|
||||
auto fn =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
sfm->OnAddFile(fn);
|
||||
sfm->OnAddFile(fname);
|
||||
if (sfm->IsMaxAllowedSpaceReached()) {
|
||||
// TODO(ajkr): should we return OK() if max space was reached by the final
|
||||
// compaction output file (similarly to how flush works when full)?
|
||||
@@ -1496,7 +1470,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->immutable_cf_options()->listeners;
|
||||
sub_compact->outfile.reset(
|
||||
new WritableFileWriter(std::move(writable_file), fname, env_options_,
|
||||
db_options_.statistics.get(), listeners));
|
||||
env_, db_options_.statistics.get(), listeners));
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
@@ -1526,8 +1500,9 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
cfd->GetID(), cfd->GetName(), sub_compact->outfile.get(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
sub_compact->compaction->output_compression_opts(),
|
||||
sub_compact->compaction->output_level(), &sub_compact->compression_dict,
|
||||
skip_filters, output_file_creation_time));
|
||||
sub_compact->compaction->output_level(), skip_filters,
|
||||
output_file_creation_time, 0 /* oldest_key_time */,
|
||||
sub_compact->compaction->max_output_file_size()));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "options/db_options.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/compaction_job_stats.h"
|
||||
@@ -104,7 +104,7 @@ class CompactionJob {
|
||||
|
||||
Status FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
CompactionIterationStats* range_del_out_stats,
|
||||
const Slice* next_table_min_key = nullptr);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
@@ -113,7 +113,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
~CompactionJobStatsTest() {
|
||||
~CompactionJobStatsTest() override {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({});
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -426,7 +426,7 @@ class CompactionJobStatsChecker : public EventListener {
|
||||
// Once a compaction completed, this function will verify the returned
|
||||
// CompactionJobInfo with the oldest CompactionJobInfo added earlier
|
||||
// in "expected_stats_" which has not yet being used for verification.
|
||||
virtual void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) {
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
if (verify_next_comp_io_stats_) {
|
||||
ASSERT_GT(ci.stats.file_write_nanos, 0);
|
||||
ASSERT_GT(ci.stats.file_range_sync_nanos, 0);
|
||||
@@ -523,7 +523,7 @@ class CompactionJobDeletionStatsChecker : public CompactionJobStatsChecker {
|
||||
public:
|
||||
// Verifies whether two CompactionJobStats match.
|
||||
void Verify(const CompactionJobStats& current_stats,
|
||||
const CompactionJobStats& stats) {
|
||||
const CompactionJobStats& stats) override {
|
||||
ASSERT_EQ(
|
||||
current_stats.num_input_deletion_records,
|
||||
stats.num_input_deletion_records);
|
||||
|
||||
@@ -172,7 +172,7 @@ class CompactionJobTest : public testing::Test {
|
||||
// This is how the key will look like once it's written in bottommost
|
||||
// file
|
||||
InternalKey bottommost_internal_key(
|
||||
key, (key == "9999") ? sequence_number : 0, kTypeValue);
|
||||
key, 0, kTypeValue);
|
||||
|
||||
if (corrupt_id(k)) {
|
||||
test::CorruptKeyType(&internal_key);
|
||||
@@ -379,7 +379,7 @@ TEST_F(CompactionJobTest, SimpleOverwrite) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "val2"},
|
||||
{KeyStr("b", 4U, kTypeValue), "val3"}});
|
||||
{KeyStr("b", 0U, kTypeValue), "val3"}});
|
||||
|
||||
SetLastSequence(4U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -432,7 +432,7 @@ TEST_F(CompactionJobTest, SimpleMerge) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "3,4,5"},
|
||||
{KeyStr("b", 2U, kTypeValue), "1,2"}});
|
||||
{KeyStr("b", 0U, kTypeValue), "1,2"}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -456,8 +456,7 @@ TEST_F(CompactionJobTest, NonAssocMerge) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "3,4,5"},
|
||||
{KeyStr("b", 2U, kTypeMerge), "2"},
|
||||
{KeyStr("b", 1U, kTypeMerge), "1"}});
|
||||
{KeyStr("b", 0U, kTypeValue), "1,2"}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -484,7 +483,7 @@ TEST_F(CompactionJobTest, MergeOperandFilter) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), test::EncodeInt(8U)},
|
||||
{KeyStr("b", 2U, kTypeMerge), test::EncodeInt(2U)}});
|
||||
{KeyStr("b", 0U, kTypeValue), test::EncodeInt(2U)}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -747,7 +746,7 @@ TEST_F(CompactionJobTest, SingleDeleteZeroSeq) {
|
||||
AddMockFile(file2);
|
||||
|
||||
auto expected_results = mock::MakeMockFile({
|
||||
{KeyStr("dummy", 5U, kTypeValue), "val2"},
|
||||
{KeyStr("dummy", 0U, kTypeValue), "val2"},
|
||||
});
|
||||
|
||||
SetLastSequence(22U);
|
||||
@@ -931,7 +930,7 @@ TEST_F(CompactionJobTest, CorruptionAfterDeletion) {
|
||||
mock::MakeMockFile({{test::KeyStr("A", 0U, kTypeValue), "val3"},
|
||||
{test::KeyStr("a", 0U, kTypeValue, true), "val"},
|
||||
{test::KeyStr("b", 0U, kTypeValue, true), "val"},
|
||||
{test::KeyStr("c", 1U, kTypeValue), "val2"}});
|
||||
{test::KeyStr("c", 0U, kTypeValue), "val2"}});
|
||||
|
||||
SetLastSequence(6U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
|
||||
@@ -41,7 +41,7 @@ bool FIFOCompactionPicker::NeedsCompaction(
|
||||
Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(mutable_cf_options.compaction_options_fifo.ttl > 0);
|
||||
assert(mutable_cf_options.ttl > 0);
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
@@ -63,7 +63,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.ttl) {
|
||||
if (current_time > mutable_cf_options.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
@@ -71,8 +71,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time -
|
||||
mutable_cf_options.compaction_options_fifo.ttl)) {
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
@@ -201,7 +200,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
assert(vstorage->num_levels() == 1);
|
||||
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.compaction_options_fifo.ttl > 0) {
|
||||
if (mutable_cf_options.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
|
||||
@@ -22,9 +22,7 @@ namespace rocksdb {
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* /*format*/, va_list /*ap*/) override {
|
||||
log_count++;
|
||||
}
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override { log_count++; }
|
||||
size_t log_count;
|
||||
};
|
||||
|
||||
@@ -59,14 +57,16 @@ class CompactionPickerTest : public testing::Test {
|
||||
log_buffer_(InfoLogLevel::INFO_LEVEL, &logger_),
|
||||
file_num_(1),
|
||||
vstorage_(nullptr) {
|
||||
// ioptions_.compaction_pri = kMinOverlappingRatio has its own set of
|
||||
// tests to cover.
|
||||
ioptions_.compaction_pri = kByCompensatedSize;
|
||||
fifo_options_.max_table_files_size = 1;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
ioptions_.cf_paths.emplace_back("dummy",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
~CompactionPickerTest() {
|
||||
}
|
||||
~CompactionPickerTest() override {}
|
||||
|
||||
void NewVersionStorage(int num_levels, CompactionStyle style) {
|
||||
DeleteVersionStorage();
|
||||
@@ -85,8 +85,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100) {
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
@@ -94,7 +94,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
f->largest = InternalKey(largest, largest_seq, kTypeValue);
|
||||
f->fd.smallest_seqno = smallest_seq;
|
||||
f->fd.largest_seqno = largest_seq;
|
||||
f->compensated_file_size = file_size;
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->refs = 0;
|
||||
vstorage_->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
@@ -617,6 +618,35 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping3) {
|
||||
ASSERT_EQ(8U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
|
||||
// file 7 and 8 over lap with the same file, but file 8 is smaller so
|
||||
// it will be picked.
|
||||
// Overlaps with file 26, 27. And the file is compensated so will be
|
||||
// picked up.
|
||||
Add(2, 6U, "150", "167", 60000000U, 0, 100, 100, 180000000U);
|
||||
Add(2, 7U, "168", "169", 60000000U); // Overlaps with file 27
|
||||
Add(2, 8U, "201", "300", 61000000U); // Overlaps with file 28
|
||||
|
||||
Add(3, 26U, "160", "165", 60000000U);
|
||||
// Boosted file size in output level is not considered.
|
||||
Add(3, 27U, "166", "170", 60000000U, 0, 100, 100, 260000000U);
|
||||
Add(3, 28U, "180", "400", 60000000U);
|
||||
Add(3, 29U, "401", "500", 60000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Picking file 8 because overlapping ratio is the biggest.
|
||||
ASSERT_EQ(6U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
// This test exhibits the bug where we don't properly reset parent_index in
|
||||
// PickCompaction()
|
||||
TEST_F(CompactionPickerTest, ParentIndexResetBug) {
|
||||
|
||||
+26
-26
@@ -27,24 +27,24 @@ class KVIter : public Iterator {
|
||||
public:
|
||||
explicit KVIter(const stl_wrappers::KVMap* map)
|
||||
: map_(map), iter_(map_->end()) {}
|
||||
virtual bool Valid() const override { return iter_ != map_->end(); }
|
||||
virtual void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
virtual void SeekToLast() override {
|
||||
bool Valid() const override { return iter_ != map_->end(); }
|
||||
void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
void SeekToLast() override {
|
||||
if (map_->empty()) {
|
||||
iter_ = map_->end();
|
||||
} else {
|
||||
iter_ = map_->find(map_->rbegin()->first);
|
||||
}
|
||||
}
|
||||
virtual void Seek(const Slice& k) override {
|
||||
void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
void Next() override { ++iter_; }
|
||||
void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
iter_ = map_->end();
|
||||
return;
|
||||
@@ -52,9 +52,9 @@ class KVIter : public Iterator {
|
||||
--iter_;
|
||||
}
|
||||
|
||||
virtual Slice key() const override { return iter_->first; }
|
||||
virtual Slice value() const override { return iter_->second; }
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
Slice key() const override { return iter_->first; }
|
||||
Slice value() const override { return iter_->second; }
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const stl_wrappers::KVMap* const map_;
|
||||
@@ -171,9 +171,9 @@ class DoubleComparator : public Comparator {
|
||||
public:
|
||||
DoubleComparator() {}
|
||||
|
||||
virtual const char* Name() const override { return "DoubleComparator"; }
|
||||
const char* Name() const override { return "DoubleComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
#ifndef CYGWIN
|
||||
double da = std::stod(a.ToString());
|
||||
double db = std::stod(b.ToString());
|
||||
@@ -189,19 +189,19 @@ class DoubleComparator : public Comparator {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
public:
|
||||
HashComparator() {}
|
||||
|
||||
virtual const char* Name() const override { return "HashComparator"; }
|
||||
const char* Name() const override { return "HashComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
uint32_t ha = Hash(a.data(), a.size(), 66);
|
||||
uint32_t hb = Hash(b.data(), b.size(), 66);
|
||||
if (ha == hb) {
|
||||
@@ -212,19 +212,19 @@ class HashComparator : public Comparator {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
public:
|
||||
TwoStrComparator() {}
|
||||
|
||||
virtual const char* Name() const override { return "TwoStrComparator"; }
|
||||
const char* Name() const override { return "TwoStrComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
assert(a.size() >= 2);
|
||||
assert(b.size() >= 2);
|
||||
size_t size_a1 = static_cast<size_t>(a[0]);
|
||||
@@ -244,10 +244,10 @@ class TwoStrComparator : public Comparator {
|
||||
}
|
||||
return a2.compare(b2);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -272,7 +272,7 @@ class ComparatorDBTest
|
||||
EXPECT_OK(DestroyDB(dbname_, last_options_));
|
||||
}
|
||||
|
||||
~ComparatorDBTest() {
|
||||
~ComparatorDBTest() override {
|
||||
delete db_;
|
||||
EXPECT_OK(DestroyDB(dbname_, last_options_));
|
||||
comparator = BytewiseComparator();
|
||||
|
||||
+38
-3
@@ -24,6 +24,8 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
@@ -60,9 +62,9 @@ class CorruptionTest : public testing::Test {
|
||||
options_.create_if_missing = false;
|
||||
}
|
||||
|
||||
~CorruptionTest() {
|
||||
delete db_;
|
||||
DestroyDB(dbname_, Options());
|
||||
~CorruptionTest() override {
|
||||
delete db_;
|
||||
DestroyDB(dbname_, Options());
|
||||
}
|
||||
|
||||
void CloseDb() {
|
||||
@@ -467,6 +469,39 @@ TEST_F(CorruptionTest, UnrelatedKeys) {
|
||||
ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "b"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_EQ(static_cast<size_t>(1), metadata.size());
|
||||
std::string filename = dbname_ + metadata[0].name;
|
||||
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
ASSERT_OK(options_.env->NewRandomAccessFile(filename, &file, EnvOptions()));
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), filename));
|
||||
|
||||
uint64_t file_size;
|
||||
ASSERT_OK(options_.env->GetFileSize(filename, &file_size));
|
||||
|
||||
BlockHandle range_del_handle;
|
||||
ASSERT_OK(FindMetaBlock(
|
||||
file_reader.get(), file_size, kBlockBasedTableMagicNumber,
|
||||
ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
// The test case does not fail on TryReopen because failure to preload table
|
||||
// handlers is not considered critical.
|
||||
ASSERT_OK(TryReopen());
|
||||
std::string val;
|
||||
// However, it does fail on any read involving that file since that file
|
||||
// cannot be opened with a corrupt range deletion meta-block.
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "a", &val).IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Options options;
|
||||
|
||||
@@ -31,7 +31,7 @@ class CuckooTableDBTest : public testing::Test {
|
||||
Reopen();
|
||||
}
|
||||
|
||||
~CuckooTableDBTest() {
|
||||
~CuckooTableDBTest() override {
|
||||
delete db_;
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
}
|
||||
|
||||
+198
-26
@@ -6,6 +6,7 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
// #include <iostream>
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
@@ -214,11 +215,11 @@ TEST_F(DBBasicTest, PutSingleDeleteGet) {
|
||||
ASSERT_EQ("v2", Get(1, "foo2"));
|
||||
ASSERT_OK(SingleDelete(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because single delete does not get removed when it encounters a merge.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Ski FIFO and universal compaction because they do not apply to the test
|
||||
// case. Skip MergePut because single delete does not get removed when it
|
||||
// encounters a merge.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, EmptyFlush) {
|
||||
@@ -236,11 +237,11 @@ TEST_F(DBBasicTest, EmptyFlush) {
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
ASSERT_EQ("[ ]", AllEntriesFor("a", 1));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because merges cannot be combined with single deletions.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Skip FIFO and universal compaction as they do not apply to the test
|
||||
// case. Skip MergePut because merges cannot be combined with single
|
||||
// deletions.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, GetFromVersions) {
|
||||
@@ -264,11 +265,6 @@ TEST_F(DBBasicTest, GetSnapshot) {
|
||||
std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
|
||||
ASSERT_OK(Put(1, key, "v1"));
|
||||
const Snapshot* s1 = db_->GetSnapshot();
|
||||
if (option_config_ == kHashCuckoo) {
|
||||
// Unsupported case.
|
||||
ASSERT_TRUE(s1 == nullptr);
|
||||
break;
|
||||
}
|
||||
ASSERT_OK(Put(1, key, "v2"));
|
||||
ASSERT_EQ("v2", Get(1, key));
|
||||
ASSERT_EQ("v1", Get(1, key, s1));
|
||||
@@ -509,7 +505,7 @@ TEST_F(DBBasicTest, Snapshot) {
|
||||
ASSERT_EQ(0U, GetNumSnapshots());
|
||||
ASSERT_EQ("0v4", Get(0, "foo"));
|
||||
ASSERT_EQ("1v4", Get(1, "foo"));
|
||||
} while (ChangeOptions(kSkipHashCuckoo));
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -565,8 +561,7 @@ TEST_F(DBBasicTest, CompactBetweenSnapshots) {
|
||||
nullptr);
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth ]");
|
||||
// skip HashCuckooRep as it does not support snapshot
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
@@ -857,18 +852,17 @@ class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
TestLogger(TestEnv *env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() {
|
||||
~TestLogger() override {
|
||||
if (!closed_) {
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
virtual void Logv(const char* /*format*/, va_list /*ap*/) override{};
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override{};
|
||||
|
||||
protected:
|
||||
virtual Status CloseImpl() override {
|
||||
return CloseHelper();
|
||||
}
|
||||
private:
|
||||
Status CloseImpl() override { return CloseHelper(); }
|
||||
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();;
|
||||
return Status::IOError();
|
||||
@@ -880,8 +874,8 @@ class TestEnv : public EnvWrapper {
|
||||
|
||||
int GetCloseCount() { return close_count; }
|
||||
|
||||
virtual Status NewLogger(const std::string& /*fname*/,
|
||||
std::shared_ptr<Logger>* result) {
|
||||
Status NewLogger(const std::string& /*fname*/,
|
||||
std::shared_ptr<Logger>* result) override {
|
||||
result->reset(new TestLogger(this));
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -956,6 +950,184 @@ TEST_F(DBBasicTest, DBCloseFlushError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetMultiCF) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
options);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val"));
|
||||
}
|
||||
|
||||
int get_sv_count = 0;
|
||||
rocksdb::DBImpl* db = reinterpret_cast<DBImpl*>(db_);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (++get_sv_count == 2) {
|
||||
// After MultiGet refs a couple of CFs, flush all CFs so MultiGet
|
||||
// is forced to repeat the process
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val2"));
|
||||
}
|
||||
}
|
||||
if (get_sv_count == 11) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
db->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_EQ(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<int> cfs;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
cfs.push_back(i);
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
values = MultiGet(cfs, keys);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j], "cf" + std::to_string(j) + "_val2");
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
reinterpret_cast<DBImpl*>(db_)->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVObsolete);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetMultiCFMutex) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
options);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val"));
|
||||
}
|
||||
|
||||
int get_sv_count = 0;
|
||||
int retries = 0;
|
||||
bool last_try = false;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::LastTry", [&](void* /*arg*/) {
|
||||
last_try = true;
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (last_try) {
|
||||
return;
|
||||
}
|
||||
if (++get_sv_count == 2) {
|
||||
++retries;
|
||||
get_sv_count = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(Put(
|
||||
i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val" + std::to_string(retries)));
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<int> cfs;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
cfs.push_back(i);
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
values = MultiGet(cfs, keys);
|
||||
ASSERT_TRUE(last_try);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j],
|
||||
"cf" + std::to_string(j) + "_val" + std::to_string(retries));
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
reinterpret_cast<DBImpl*>(db_)->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetMultiCFSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
options);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val"));
|
||||
}
|
||||
|
||||
int get_sv_count = 0;
|
||||
rocksdb::DBImpl* db = reinterpret_cast<DBImpl*>(db_);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (++get_sv_count == 2) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val2"));
|
||||
}
|
||||
}
|
||||
if (get_sv_count == 8) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
db->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_TRUE(
|
||||
(cfd->TEST_GetLocalSV()->Get() == SuperVersion::kSVInUse) ||
|
||||
(cfd->TEST_GetLocalSV()->Get() == SuperVersion::kSVObsolete));
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<int> cfs;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
cfs.push_back(i);
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
values = MultiGet(cfs, keys, snapshot);
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j], "cf" + std::to_string(j) + "_val");
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
reinterpret_cast<DBImpl*>(db_)->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -173,7 +173,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
|
||||
// Release interators and access cache again.
|
||||
// Release iterators and access cache again.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
iterators[i].reset();
|
||||
CheckCacheCounters(options, 0, 0, 0, 0);
|
||||
@@ -395,9 +395,9 @@ class MockCache : public LRUCache {
|
||||
false /*strict_capacity_limit*/, 0.0 /*high_pri_pool_ratio*/) {
|
||||
}
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle, Priority priority) override {
|
||||
Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value), Handle** handle,
|
||||
Priority priority) override {
|
||||
if (priority == Priority::LOW) {
|
||||
low_pri_insert_count++;
|
||||
} else {
|
||||
@@ -631,6 +631,74 @@ TEST_F(DBBlockCacheTest, CompressedCache) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
const int kNumFiles = 4;
|
||||
const int kNumEntriesPerFile = 128;
|
||||
const int kNumBytesPerEntry = 1024;
|
||||
|
||||
// Try all the available libraries that support dictionary compression
|
||||
std::vector<CompressionType> compression_types;
|
||||
#ifdef ZLIB
|
||||
compression_types.push_back(kZlibCompression);
|
||||
#endif // ZLIB
|
||||
#if LZ4_VERSION_NUMBER >= 10400
|
||||
compression_types.push_back(kLZ4Compression);
|
||||
compression_types.push_back(kLZ4HCCompression);
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
#if ZSTD_VERSION_NUMBER >= 500
|
||||
compression_types.push_back(kZSTD);
|
||||
#endif // ZSTD_VERSION_NUMBER >= 500
|
||||
Random rnd(301);
|
||||
for (auto compression_type : compression_types) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = compression_type;
|
||||
options.compression_opts.max_dict_bytes = 4096;
|
||||
options.create_if_missing = true;
|
||||
options.num_levels = 2;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache.reset(new MockCache());
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
std::string value = RandomString(&rnd, kNumBytesPerEntry);
|
||||
ASSERT_OK(Put(Key(j * kNumFiles + i), value.c_str()));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
// Seek to a key in a file. It should cause the SST's dictionary meta-block
|
||||
// to be read.
|
||||
RecordCacheCounters(options);
|
||||
ASSERT_EQ(0,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD));
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
ReadOptions read_options;
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
|
||||
// Two blocks missed/added: dictionary and data block
|
||||
// One block hit: index since it's prefetched
|
||||
CheckCacheCounters(options, 2 /* expected_misses */, 1 /* expected_hits */,
|
||||
2 /* expected_inserts */, 0 /* expected_failures */);
|
||||
ASSERT_EQ(1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD));
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -32,7 +32,7 @@ class DBBloomFilterTestWithParam
|
||||
public:
|
||||
DBBloomFilterTestWithParam() : DBTestBase("/db_bloom_filter_tests") {}
|
||||
|
||||
~DBBloomFilterTestWithParam() {}
|
||||
~DBBloomFilterTestWithParam() override {}
|
||||
|
||||
void SetUp() override {
|
||||
use_block_based_filter_ = std::get<0>(GetParam());
|
||||
@@ -642,7 +642,7 @@ class WrappedBloom : public FilterPolicy {
|
||||
explicit WrappedBloom(int bits_per_key)
|
||||
: filter_(NewBloomFilterPolicy(bits_per_key)), counter_(0) {}
|
||||
|
||||
~WrappedBloom() { delete filter_; }
|
||||
~WrappedBloom() override { delete filter_; }
|
||||
|
||||
const char* Name() const override { return "WrappedRocksDbFilterPolicy"; }
|
||||
|
||||
@@ -786,6 +786,56 @@ TEST_F(DBBloomFilterTest, PrefixExtractorBlockFilter) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, MemtableWholeKeyBloomFilter) {
|
||||
// regression test for #2743. the range delete tombstones in memtable should
|
||||
// be added even when Get() skips searching due to its prefix bloom filter
|
||||
const int kMemtableSize = 1 << 20; // 1MB
|
||||
const int kMemtablePrefixFilterSize = 1 << 13; // 8KB
|
||||
const int kPrefixLen = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.memtable_prefix_bloom_size_ratio =
|
||||
static_cast<double>(kMemtablePrefixFilterSize) / kMemtableSize;
|
||||
options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(kPrefixLen));
|
||||
options.write_buffer_size = kMemtableSize;
|
||||
options.memtable_whole_key_filtering = false;
|
||||
Reopen(options);
|
||||
std::string key1("AAAABBBB");
|
||||
std::string key2("AAAACCCC"); // not in DB
|
||||
std::string key3("AAAADDDD");
|
||||
std::string key4("AAAAEEEE");
|
||||
std::string value1("Value1");
|
||||
std::string value3("Value3");
|
||||
std::string value4("Value4");
|
||||
|
||||
ASSERT_OK(Put(key1, value1, WriteOptions()));
|
||||
|
||||
// check memtable bloom stats
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
ASSERT_EQ(0, get_perf_context()->bloom_memtable_miss_count);
|
||||
// same prefix, bloom filter false positive
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
|
||||
// enable whole key bloom filter
|
||||
options.memtable_whole_key_filtering = true;
|
||||
Reopen(options);
|
||||
// check memtable bloom stats
|
||||
ASSERT_OK(Put(key3, value3, WriteOptions()));
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
// whole key bloom filter kicks in and determines it's a miss
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_miss_count);
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
|
||||
// verify whole key filtering does not depend on prefix_extractor
|
||||
options.prefix_extractor.reset();
|
||||
Reopen(options);
|
||||
// check memtable bloom stats
|
||||
ASSERT_OK(Put(key4, value4, WriteOptions()));
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
// whole key bloom filter kicks in and determines it's a miss
|
||||
ASSERT_EQ(2, get_perf_context()->bloom_memtable_miss_count);
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class BloomStatsTestWithParam
|
||||
: public DBBloomFilterTest,
|
||||
@@ -823,7 +873,7 @@ class BloomStatsTestWithParam
|
||||
DestroyAndReopen(options_);
|
||||
}
|
||||
|
||||
~BloomStatsTestWithParam() {
|
||||
~BloomStatsTestWithParam() override {
|
||||
get_perf_context()->Reset();
|
||||
Destroy(options_);
|
||||
}
|
||||
|
||||
@@ -63,33 +63,33 @@ INSTANTIATE_TEST_CASE_P(DBTestCompactionFilterWithCompactOption,
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilter"; }
|
||||
const char* Name() const override { return "KeepFilter"; }
|
||||
};
|
||||
|
||||
class DeleteFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
class DeleteISFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& key, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& key, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
if (i > 5 && i <= 105) {
|
||||
@@ -98,20 +98,18 @@ class DeleteISFilter : public CompactionFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return true; }
|
||||
bool IgnoreSnapshots() const override { return true; }
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
// Skip x if floor(x/10) is even, use range skips. Requires that keys are
|
||||
// zero-padded to length 10.
|
||||
class SkipEvenFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key,
|
||||
ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/,
|
||||
std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/, std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
if (i / 10 % 2 == 0) {
|
||||
@@ -124,22 +122,22 @@ class SkipEvenFilter : public CompactionFilter {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return true; }
|
||||
bool IgnoreSnapshots() const override { return true; }
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilter"; }
|
||||
const char* Name() const override { return "DelayFilter"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -149,13 +147,13 @@ class ConditionalFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ConditionalFilter(const std::string* filtered_value)
|
||||
: filtered_value_(filtered_value) {}
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/, const Slice& value,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& value,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return value.ToString() == *filtered_value_;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "ConditionalFilter"; }
|
||||
const char* Name() const override { return "ConditionalFilter"; }
|
||||
|
||||
private:
|
||||
const std::string* filtered_value_;
|
||||
@@ -165,16 +163,15 @@ class ChangeFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ChangeFilter() {}
|
||||
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* new_value, bool* value_changed) const override {
|
||||
assert(new_value != nullptr);
|
||||
*new_value = NEW_VALUE;
|
||||
*value_changed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "ChangeFilter"; }
|
||||
const char* Name() const override { return "ChangeFilter"; }
|
||||
};
|
||||
|
||||
class KeepFilterFactory : public CompactionFilterFactory {
|
||||
@@ -185,7 +182,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
check_context_cf_id_(check_context_cf_id),
|
||||
compaction_filter_created_(false) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (check_context_) {
|
||||
EXPECT_EQ(expect_full_compaction_.load(), context.is_full_compaction);
|
||||
@@ -200,7 +197,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
|
||||
bool compaction_filter_created() const { return compaction_filter_created_; }
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilterFactory"; }
|
||||
const char* Name() const override { return "KeepFilterFactory"; }
|
||||
bool check_context_;
|
||||
bool check_context_cf_id_;
|
||||
std::atomic_bool expect_full_compaction_;
|
||||
@@ -211,7 +208,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
|
||||
class DeleteFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new DeleteFilter());
|
||||
@@ -220,13 +217,13 @@ class DeleteFilterFactory : public CompactionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
};
|
||||
|
||||
// Delete Filter Factory which ignores snapshots
|
||||
class DeleteISFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new DeleteISFilter());
|
||||
@@ -235,12 +232,12 @@ class DeleteISFilterFactory : public CompactionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
};
|
||||
|
||||
class SkipEvenFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new SkipEvenFilter());
|
||||
@@ -249,18 +246,18 @@ class SkipEvenFilterFactory : public CompactionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "SkipEvenFilterFactory"; }
|
||||
const char* Name() const override { return "SkipEvenFilterFactory"; }
|
||||
};
|
||||
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilterFactory"; }
|
||||
const char* Name() const override { return "DelayFilterFactory"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -271,15 +268,13 @@ class ConditionalFilterFactory : public CompactionFilterFactory {
|
||||
explicit ConditionalFilterFactory(const Slice& filtered_value)
|
||||
: filtered_value_(filtered_value.ToString()) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(
|
||||
new ConditionalFilter(&filtered_value_));
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "ConditionalFilterFactory";
|
||||
}
|
||||
const char* Name() const override { return "ConditionalFilterFactory"; }
|
||||
|
||||
private:
|
||||
std::string filtered_value_;
|
||||
@@ -289,12 +284,12 @@ class ChangeFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit ChangeFilterFactory() {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new ChangeFilter());
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "ChangeFilterFactory"; }
|
||||
const char* Name() const override { return "ChangeFilterFactory"; }
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -340,8 +335,8 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
Arena arena;
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregatorV2 range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
@@ -357,7 +352,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(total, 100000);
|
||||
ASSERT_EQ(count, 1);
|
||||
ASSERT_EQ(count, 0);
|
||||
|
||||
// overwrite all the 100K keys once again.
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
@@ -430,8 +425,8 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
count = 0;
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregatorV2 range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
@@ -648,8 +643,8 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
int total = 0;
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregatorV2 range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* snapshots */);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* snapshots */);
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber));
|
||||
iter->SeekToFirst();
|
||||
@@ -664,7 +659,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(total, 700);
|
||||
ASSERT_EQ(count, 1);
|
||||
ASSERT_EQ(count, 0);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -699,44 +694,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextCfId) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Compaction filters should only be applied to records that are newer than the
|
||||
// latest snapshot. This test inserts records and applies a delete filter.
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter_factory = std::make_shared<DeleteFilterFactory>();
|
||||
options.disable_auto_compactions = true;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Put some data.
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Put(ToString(table * 100 + i), "val");
|
||||
}
|
||||
Flush();
|
||||
|
||||
if (table == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
}
|
||||
}
|
||||
assert(snapshot != nullptr);
|
||||
|
||||
cfilter_count = 0;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// The filter should delete 10 records.
|
||||
ASSERT_EQ(30U, cfilter_count);
|
||||
|
||||
// Release the snapshot and compact again -> now all records should be
|
||||
// removed.
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(0U, CountLiveFiles());
|
||||
}
|
||||
|
||||
// Compaction filters should only be applied to records that are newer than the
|
||||
// latest snapshot. However, if the compaction filter asks to ignore snapshots
|
||||
// records newer than the snapshot will also be processed
|
||||
// Compaction filters aplies to all records, regardless snapshots.
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
std::string five = ToString(5);
|
||||
Options options = CurrentOptions();
|
||||
@@ -874,6 +832,38 @@ TEST_F(DBTestCompactionFilter, SkipUntilWithBloomFilter) {
|
||||
EXPECT_EQ("v50", val);
|
||||
}
|
||||
|
||||
class TestNotSupportedFilter : public CompactionFilter {
|
||||
public:
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "NotSupported"; }
|
||||
bool IgnoreSnapshots() const override { return false; }
|
||||
};
|
||||
|
||||
TEST_F(DBTestCompactionFilter, IgnoreSnapshotsFalse) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter = new TestNotSupportedFilter();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put("a", "v10");
|
||||
Put("z", "v20");
|
||||
Flush();
|
||||
|
||||
Put("a", "v10");
|
||||
Put("z", "v20");
|
||||
Flush();
|
||||
|
||||
// Comapction should fail because IgnoreSnapshots() = false
|
||||
EXPECT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsNotSupported());
|
||||
|
||||
delete options.compaction_filter;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+216
-21
@@ -10,8 +10,10 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/utilities/convenience.h"
|
||||
#include "util/concurrent_task_limiter_impl.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
@@ -53,9 +55,9 @@ namespace {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -85,24 +87,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~CompactionStatsCollector() {}
|
||||
~CompactionStatsCollector() override {}
|
||||
|
||||
virtual void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
int k = static_cast<int>(info.compaction_reason);
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
assert(k >= 0 && k < num_of_reasons);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
virtual void OnExternalFileIngested(DB* /* db */,
|
||||
const ExternalFileIngestionInfo& /* info */) override {
|
||||
void OnExternalFileIngested(
|
||||
DB* /* db */, const ExternalFileIngestionInfo& /* info */) override {
|
||||
int k = static_cast<int>(CompactionReason::kExternalSstIngestion);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /* db */,
|
||||
const FlushJobInfo& /* info */) override {
|
||||
void OnFlushCompleted(DB* /* db */, const FlushJobInfo& /* info */) override {
|
||||
int k = static_cast<int>(CompactionReason::kFlush);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
@@ -412,6 +413,7 @@ TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
|
||||
|
||||
// Reopen the DB with stats-update disabled
|
||||
options.skip_stats_update_on_db_open = true;
|
||||
options.max_open_files = 20;
|
||||
env_->random_file_open_counter_.store(0);
|
||||
Reopen(options);
|
||||
|
||||
@@ -437,7 +439,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.new_table_reader_for_compaction_inputs = true;
|
||||
options.max_open_files = 100;
|
||||
options.max_open_files = 20;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
@@ -466,15 +468,16 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// preloading iterator issues one table cache lookup and create
|
||||
// a new table reader.
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
// a new table reader, if not preloaded.
|
||||
int old_num_table_cache_lookup = num_table_cache_lookup;
|
||||
ASSERT_GE(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(num_new_table_reader, 1);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(k), Get(Key(k)));
|
||||
// lookup iterator from table cache and no need to create a new one.
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(old_num_table_cache_lookup + num_table_cache_lookup, 2);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
}
|
||||
}
|
||||
@@ -487,7 +490,10 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
// a new table reader. One file is created for flush and one for compaction.
|
||||
// Compaction inputs make no table cache look-up for data/range deletion
|
||||
// iterators
|
||||
ASSERT_EQ(num_table_cache_lookup, 2);
|
||||
// May preload table cache too.
|
||||
ASSERT_GE(num_table_cache_lookup, 2);
|
||||
int old_num_table_cache_lookup2 = num_table_cache_lookup;
|
||||
|
||||
// Create new iterator for:
|
||||
// (1) 1 for verifying flush results
|
||||
// (2) 3 for compaction input files
|
||||
@@ -497,7 +503,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
@@ -509,14 +515,16 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
// Only verifying compaction outputs issues one table cache lookup
|
||||
// for both data block and range deletion block).
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
// May preload table cache too.
|
||||
ASSERT_GE(num_table_cache_lookup, 1);
|
||||
old_num_table_cache_lookup2 = num_table_cache_lookup;
|
||||
// One for compaction input, one for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 2);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -1594,7 +1602,8 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
// Note that we don't delete level 0 files
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 1;
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_OK(
|
||||
DeleteFilesInRange(db_, db_->DefaultColumnFamily(), nullptr, nullptr));
|
||||
@@ -3890,6 +3899,192 @@ TEST_F(DBCompactionTest, CompactionHasEmptyOutput) {
|
||||
ASSERT_EQ(2, collector->num_ssts_creation_started());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
const int kNumKeysPerFile = 10;
|
||||
const int kMaxBackgroundThreads = 64;
|
||||
|
||||
struct CompactionLimiter {
|
||||
std::string name;
|
||||
int limit_tasks;
|
||||
int max_tasks;
|
||||
int tasks;
|
||||
std::shared_ptr<ConcurrentTaskLimiter> limiter;
|
||||
};
|
||||
|
||||
std::vector<CompactionLimiter> limiter_settings;
|
||||
limiter_settings.push_back({"limiter_1", 1, 0, 0, nullptr});
|
||||
limiter_settings.push_back({"limiter_2", 2, 0, 0, nullptr});
|
||||
limiter_settings.push_back({"limiter_3", 3, 0, 0, nullptr});
|
||||
|
||||
for (auto& ls : limiter_settings) {
|
||||
ls.limiter.reset(NewConcurrentTaskLimiter(ls.name, ls.limit_tasks));
|
||||
}
|
||||
|
||||
std::shared_ptr<ConcurrentTaskLimiter> unique_limiter(
|
||||
NewConcurrentTaskLimiter("unique_limiter", -1));
|
||||
|
||||
const char* cf_names[] = {"default", "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
||||
const int cf_count = sizeof cf_names / sizeof cf_names[0];
|
||||
|
||||
std::unordered_map<std::string, CompactionLimiter*> cf_to_limiter;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 110 * 1024; // 110KB
|
||||
options.arena_block_size = 4096;
|
||||
options.num_levels = 3;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.level0_slowdown_writes_trigger = 64;
|
||||
options.level0_stop_writes_trigger = 64;
|
||||
options.max_background_jobs = kMaxBackgroundThreads; // Enough threads
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
options.max_write_buffer_number = 10; // Enough memtables
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::vector<Options> option_vector;
|
||||
option_vector.reserve(cf_count);
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
ColumnFamilyOptions cf_opt(options);
|
||||
if (cf == 0) {
|
||||
// "Default" CF does't use compaction limiter
|
||||
cf_opt.compaction_thread_limiter = nullptr;
|
||||
} else if (cf == 1) {
|
||||
// "1" CF uses bypass compaction limiter
|
||||
unique_limiter->SetMaxOutstandingTask(-1);
|
||||
cf_opt.compaction_thread_limiter = unique_limiter;
|
||||
} else {
|
||||
// Assign limiter by mod
|
||||
auto& ls = limiter_settings[cf % 3];
|
||||
cf_opt.compaction_thread_limiter = ls.limiter;
|
||||
cf_to_limiter[cf_names[cf]] = &ls;
|
||||
}
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt);
|
||||
}
|
||||
|
||||
for (int cf = 1; cf < cf_count; cf++) {
|
||||
CreateColumnFamilies({cf_names[cf]}, option_vector[cf]);
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies(std::vector<std::string>(cf_names,
|
||||
cf_names + cf_count),
|
||||
option_vector);
|
||||
|
||||
port::Mutex mutex;
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction", [&](void* arg) {
|
||||
const auto& cf_name = static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
auto iter = cf_to_limiter.find(cf_name);
|
||||
if (iter != cf_to_limiter.end()) {
|
||||
MutexLock l(&mutex);
|
||||
ASSERT_GE(iter->second->limit_tasks, ++iter->second->tasks);
|
||||
iter->second->max_tasks = std::max(iter->second->max_tasks,
|
||||
iter->second->limit_tasks);
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:AfterCompaction", [&](void* arg) {
|
||||
const auto& cf_name = static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
auto iter = cf_to_limiter.find(cf_name);
|
||||
if (iter != cf_to_limiter.end()) {
|
||||
MutexLock l(&mutex);
|
||||
ASSERT_GE(--iter->second->tasks, 0);
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Block all compact threads in thread pool.
|
||||
const size_t kTotalFlushTasks = kMaxBackgroundThreads / 4;
|
||||
const size_t kTotalCompactTasks = kMaxBackgroundThreads - kTotalFlushTasks;
|
||||
env_->SetBackgroundThreads((int)kTotalFlushTasks, Env::HIGH);
|
||||
env_->SetBackgroundThreads((int)kTotalCompactTasks, Env::LOW);
|
||||
|
||||
test::SleepingBackgroundTask sleeping_compact_tasks[kTotalCompactTasks];
|
||||
|
||||
// Block all compaction threads in thread pool.
|
||||
for (size_t i = 0; i < kTotalCompactTasks; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_compact_tasks[i], Env::LOW);
|
||||
sleeping_compact_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
int keyIndex = 0;
|
||||
|
||||
for (int n = 0; n < options.level0_file_num_compaction_trigger; n++) {
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf, Key(keyIndex++), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(cf, "", ""));
|
||||
}
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
}
|
||||
}
|
||||
|
||||
// Enough L0 files to trigger compaction
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, cf),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
}
|
||||
|
||||
// Create more files for one column family, which triggers speed up
|
||||
// condition, all compactions will be scheduled.
|
||||
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(0, Key(i), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(0, "", ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
ASSERT_EQ(options.level0_file_num_compaction_trigger + num + 1,
|
||||
NumTableFilesAtLevel(0, 0));
|
||||
}
|
||||
|
||||
// All CFs are pending compaction
|
||||
ASSERT_EQ(cf_count, env_->GetThreadPoolQueueLen(Env::LOW));
|
||||
|
||||
// Unblock all compaction threads
|
||||
for (size_t i = 0; i < kTotalCompactTasks; i++) {
|
||||
sleeping_compact_tasks[i].WakeUp();
|
||||
sleeping_compact_tasks[i].WaitUntilDone();
|
||||
}
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Max outstanding compact tasks reached limit
|
||||
for (auto& ls : limiter_settings) {
|
||||
ASSERT_EQ(ls.limit_tasks, ls.max_tasks);
|
||||
ASSERT_EQ(0, ls.limiter->GetOutstandingTask());
|
||||
}
|
||||
|
||||
// test manual compaction under a fully throttled limiter
|
||||
int cf_test = 1;
|
||||
unique_limiter->SetMaxOutstandingTask(0);
|
||||
|
||||
// flush one more file to cf 1
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf_test, Key(keyIndex++), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(cf_test, "", ""));
|
||||
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf_test]);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0, cf_test));
|
||||
|
||||
Compact(cf_test, Key(0), Key(keyIndex));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
@@ -3991,14 +4186,14 @@ class NoopMergeOperator : public MergeOperator {
|
||||
public:
|
||||
NoopMergeOperator() {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
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"; }
|
||||
const char* Name() const override { return "Noop"; }
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, PartialManualCompaction) {
|
||||
|
||||
@@ -407,6 +407,87 @@ TEST_P(DBAtomicFlushTest, AtomicFlushRollbackSomeJobs) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, FlushMultipleCFs_DropSomeBeforeRequestFlush) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
std::vector<int> cf_ids;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_OK(Put(cf_id, "key", "value", wopts));
|
||||
cf_ids.push_back(cf_id);
|
||||
}
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_TRUE(Flush(cf_ids).IsShutdownInProgress());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest,
|
||||
FlushMultipleCFs_DropSomeAfterScheduleFlushBeforeFlushJobRun) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::AtomicFlushMemTables:AfterScheduleFlush",
|
||||
"DBAtomicFlushTest::BeforeDropCF"},
|
||||
{"DBAtomicFlushTest::AfterDropCF",
|
||||
"DBImpl::BackgroundCallFlush:start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_OK(Put(cf_id, "key", "value", wopts));
|
||||
}
|
||||
port::Thread user_thread([&]() {
|
||||
TEST_SYNC_POINT("DBAtomicFlushTest::BeforeDropCF");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
TEST_SYNC_POINT("DBAtomicFlushTest::AfterDropCF");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = true;
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts, handles_));
|
||||
user_thread.join();
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_EQ("value", Get(cf_id, "key"));
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "eevee"}, options);
|
||||
num_cfs = handles_.size();
|
||||
ASSERT_EQ(2, num_cfs);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_EQ("value", Get(cf_id, "key"));
|
||||
}
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
|
||||
+529
-169
@@ -37,6 +37,7 @@
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
#include "db/flush_job.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "db/in_memory_stats_history.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
@@ -45,7 +46,6 @@
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/stats_history.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
@@ -221,7 +222,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
preserve_deletes_(options.preserve_deletes),
|
||||
closed_(false),
|
||||
error_handler_(this, immutable_db_options_, &mutex_),
|
||||
atomic_flush_commit_in_progress_(false) {
|
||||
atomic_flush_install_cv_(&mutex_) {
|
||||
// !batch_per_trx_ implies seq_per_batch_ because it is only unset for
|
||||
// WriteUnprepared, which should use seq_per_batch_.
|
||||
assert(batch_per_txn_ || seq_per_batch_);
|
||||
@@ -388,16 +389,15 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Shutdown: canceling all background work");
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// To avoid deadlock, `thread_dump_stats_->cancel()` needs to be called
|
||||
// before grabbing db mutex because the actual worker function
|
||||
// `DBImpl::DumpStats()` also holds db mutex
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
mutex_.Unlock();
|
||||
thread_dump_stats_->cancel();
|
||||
mutex_.Lock();
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
if (thread_persist_stats_ != nullptr) {
|
||||
thread_persist_stats_->cancel();
|
||||
thread_persist_stats_.reset();
|
||||
}
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (!shutting_down_.load(std::memory_order_acquire) &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed) &&
|
||||
!mutable_db_options_.avoid_flush_during_shutdown) {
|
||||
@@ -614,13 +614,14 @@ const Status DBImpl::CreateArchivalDirectory() {
|
||||
void DBImpl::PrintStatistics() {
|
||||
auto dbstats = immutable_db_options_.statistics.get();
|
||||
if (dbstats) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "STATISTICS:\n %s",
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "STATISTICS:\n %s",
|
||||
dbstats->ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::StartTimedTasks() {
|
||||
unsigned int stats_dump_period_sec = 0;
|
||||
unsigned int stats_persist_period_sec = 0;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
stats_dump_period_sec = mutable_db_options_.stats_dump_period_sec;
|
||||
@@ -631,9 +632,114 @@ void DBImpl::StartTimedTasks() {
|
||||
stats_dump_period_sec * 1000000));
|
||||
}
|
||||
}
|
||||
stats_persist_period_sec = mutable_db_options_.stats_persist_period_sec;
|
||||
if (stats_persist_period_sec > 0) {
|
||||
if (!thread_persist_stats_) {
|
||||
thread_persist_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::PersistStats(); }, "pst_st", env_,
|
||||
stats_persist_period_sec * 1000000));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
size_t DBImpl::EstiamteStatsHistorySize() const {
|
||||
size_t size_total =
|
||||
sizeof(std::map<uint64_t, std::map<std::string, uint64_t>>);
|
||||
if (stats_history_.size() == 0) return size_total;
|
||||
size_t size_per_slice =
|
||||
sizeof(uint64_t) + sizeof(std::map<std::string, uint64_t>);
|
||||
// non-empty map, stats_history_.begin() guaranteed to exist
|
||||
std::map<std::string, uint64_t> sample_slice(stats_history_.begin()->second);
|
||||
for (const auto& pairs : sample_slice) {
|
||||
size_per_slice +=
|
||||
pairs.first.capacity() + sizeof(pairs.first) + sizeof(pairs.second);
|
||||
}
|
||||
size_total = size_per_slice * stats_history_.size();
|
||||
return size_total;
|
||||
}
|
||||
|
||||
void DBImpl::PersistStats() {
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:Entry");
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (shutdown_initiated_) {
|
||||
return;
|
||||
}
|
||||
uint64_t now_micros = env_->NowMicros();
|
||||
Statistics* statistics = immutable_db_options_.statistics.get();
|
||||
if (!statistics) {
|
||||
return;
|
||||
}
|
||||
size_t stats_history_size_limit = 0;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
stats_history_size_limit = mutable_db_options_.stats_history_buffer_size;
|
||||
}
|
||||
|
||||
// TODO(Zhongyi): also persist immutable_db_options_.statistics
|
||||
{
|
||||
std::map<std::string, uint64_t> stats_map;
|
||||
if (!statistics->getTickerMap(&stats_map)) {
|
||||
return;
|
||||
}
|
||||
InstrumentedMutexLock l(&stats_history_mutex_);
|
||||
// calculate the delta from last time
|
||||
if (stats_slice_initialized_) {
|
||||
std::map<std::string, uint64_t> stats_delta;
|
||||
for (const auto& stat : stats_map) {
|
||||
if (stats_slice_.find(stat.first) != stats_slice_.end()) {
|
||||
stats_delta[stat.first] = stat.second - stats_slice_[stat.first];
|
||||
}
|
||||
}
|
||||
stats_history_[now_micros] = stats_delta;
|
||||
}
|
||||
stats_slice_initialized_ = true;
|
||||
std::swap(stats_slice_, stats_map);
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:StatsCopied");
|
||||
|
||||
// delete older stats snapshots to control memory consumption
|
||||
bool purge_needed = EstiamteStatsHistorySize() > stats_history_size_limit;
|
||||
while (purge_needed && !stats_history_.empty()) {
|
||||
stats_history_.erase(stats_history_.begin());
|
||||
purge_needed = EstiamteStatsHistorySize() > stats_history_size_limit;
|
||||
}
|
||||
}
|
||||
// TODO: persist stats to disk
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
bool DBImpl::FindStatsByTime(uint64_t start_time, uint64_t end_time,
|
||||
uint64_t* new_time,
|
||||
std::map<std::string, uint64_t>* stats_map) {
|
||||
assert(new_time);
|
||||
assert(stats_map);
|
||||
if (!new_time || !stats_map) return false;
|
||||
// lock when search for start_time
|
||||
{
|
||||
InstrumentedMutexLock l(&stats_history_mutex_);
|
||||
auto it = stats_history_.lower_bound(start_time);
|
||||
if (it != stats_history_.end() && it->first < end_time) {
|
||||
// make a copy for timestamp and stats_map
|
||||
*new_time = it->first;
|
||||
*stats_map = it->second;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::GetStatsHistory(uint64_t start_time, uint64_t end_time,
|
||||
std::unique_ptr<StatsHistoryIterator>* stats_iterator) {
|
||||
if (!stats_iterator) {
|
||||
return Status::InvalidArgument("stats_iterator not preallocated.");
|
||||
}
|
||||
stats_iterator->reset(
|
||||
new InMemoryStatsHistoryIterator(start_time, end_time, this));
|
||||
return (*stats_iterator)->status();
|
||||
}
|
||||
|
||||
void DBImpl::DumpStats() {
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:1");
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -666,16 +772,16 @@ void DBImpl::DumpStats() {
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:2");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"------- DUMPING STATS -------");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
if (immutable_db_options_.dump_malloc_stats) {
|
||||
stats.clear();
|
||||
DumpMallocStats(&stats);
|
||||
if (!stats.empty()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"------- Malloc STATS -------");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -820,6 +926,21 @@ Status DBImpl::SetDBOptions(
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
}
|
||||
if (new_options.stats_persist_period_sec !=
|
||||
mutable_db_options_.stats_persist_period_sec) {
|
||||
if (thread_persist_stats_) {
|
||||
mutex_.Unlock();
|
||||
thread_persist_stats_->cancel();
|
||||
mutex_.Lock();
|
||||
}
|
||||
if (new_options.stats_persist_period_sec > 0) {
|
||||
thread_persist_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::PersistStats(); }, "pst_st", env_,
|
||||
new_options.stats_persist_period_sec * 1000000));
|
||||
} else {
|
||||
thread_persist_stats_.reset();
|
||||
}
|
||||
}
|
||||
write_controller_.set_max_delayed_write_rate(
|
||||
new_options.delayed_write_rate);
|
||||
table_cache_.get()->SetCapacity(new_options.max_open_files == -1
|
||||
@@ -1033,7 +1154,7 @@ bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
}
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregatorV2* range_del_agg, SequenceNumber sequence,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
@@ -1150,10 +1271,12 @@ static void CleanupIteratorState(void* arg1, void* /*arg2*/) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
const ReadOptions& read_options, ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version, Arena* arena,
|
||||
RangeDelAggregatorV2* range_del_agg, SequenceNumber sequence) {
|
||||
InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber sequence) {
|
||||
InternalIterator* internal_iter;
|
||||
assert(arena != nullptr);
|
||||
assert(range_del_agg != nullptr);
|
||||
@@ -1215,6 +1338,7 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
PinnableSlice* pinnable_val, bool* value_found,
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
assert(pinnable_val != nullptr);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
@@ -1317,10 +1441,13 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
size_t size = pinnable_val->size();
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
size_t size = 0;
|
||||
if (s.ok()) {
|
||||
size = pinnable_val->size();
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
PERF_COUNTER_ADD(get_read_bytes, size);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_READ, size);
|
||||
PERF_COUNTER_ADD(get_read_bytes, size);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -1329,6 +1456,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
@@ -1337,33 +1465,96 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
struct MultiGetColumnFamilyData {
|
||||
ColumnFamilyData* cfd;
|
||||
SuperVersion* super_version;
|
||||
MultiGetColumnFamilyData(ColumnFamilyData* cf, SuperVersion* sv)
|
||||
: cfd(cf), super_version(sv) {}
|
||||
};
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData*> multiget_cf_data;
|
||||
// fill up and allocate outside of mutex
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
column_family.size());
|
||||
for (auto cf : column_family) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(cf);
|
||||
auto cfd = cfh->cfd();
|
||||
if (multiget_cf_data.find(cfd->GetID()) == multiget_cf_data.end()) {
|
||||
auto mgcfd = new MultiGetColumnFamilyData();
|
||||
mgcfd->cfd = cfd;
|
||||
multiget_cf_data.insert({cfd->GetID(), mgcfd});
|
||||
multiget_cf_data.emplace(cfd->GetID(),
|
||||
MultiGetColumnFamilyData(cfd, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
if (read_options.snapshot != nullptr) {
|
||||
snapshot =
|
||||
reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
} else {
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
bool last_try = false;
|
||||
{
|
||||
// If we end up with the same issue of memtable geting sealed during 2
|
||||
// consecutive retries, it means the write rate is very high. In that case
|
||||
// its probably ok to take the mutex on the 3rd try so we can succeed for
|
||||
// sure
|
||||
static const int num_retries = 3;
|
||||
for (auto i = 0; i < num_retries; ++i) {
|
||||
last_try = (i == num_retries - 1);
|
||||
bool retry = false;
|
||||
|
||||
if (i > 0) {
|
||||
for (auto mgd_iter = multiget_cf_data.begin();
|
||||
mgd_iter != multiget_cf_data.end(); ++mgd_iter) {
|
||||
auto super_version = mgd_iter->second.super_version;
|
||||
auto cfd = mgd_iter->second.cfd;
|
||||
if (super_version != nullptr) {
|
||||
ReturnAndCleanupSuperVersion(cfd, super_version);
|
||||
}
|
||||
mgd_iter->second.super_version = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options.snapshot == nullptr) {
|
||||
if (last_try) {
|
||||
TEST_SYNC_POINT("DBImpl::MultiGet::LastTry");
|
||||
// We're close to max number of retries. For the last retry,
|
||||
// acquire the lock so we're sure to succeed
|
||||
mutex_.Lock();
|
||||
}
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
} else {
|
||||
snapshot = reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_;
|
||||
}
|
||||
|
||||
for (auto mgd_iter = multiget_cf_data.begin();
|
||||
mgd_iter != multiget_cf_data.end(); ++mgd_iter) {
|
||||
if (!last_try) {
|
||||
mgd_iter->second.super_version =
|
||||
GetAndRefSuperVersion(mgd_iter->second.cfd);
|
||||
} else {
|
||||
mgd_iter->second.super_version =
|
||||
mgd_iter->second.cfd->GetSuperVersion()->Ref();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::MultiGet::AfterRefSV");
|
||||
if (read_options.snapshot != nullptr || last_try) {
|
||||
// If user passed a snapshot, then we don't care if a memtable is
|
||||
// sealed or compaction happens because the snapshot would ensure
|
||||
// that older key versions are kept around. If this is the last
|
||||
// retry, then we have the lock so nothing bad can happen
|
||||
continue;
|
||||
}
|
||||
// We could get the earliest sequence number for the whole list of
|
||||
// memtables, which will include immutable memtables as well, but that
|
||||
// might be tricky to maintain in case we decide, in future, to do
|
||||
// memtable compaction.
|
||||
if (!last_try) {
|
||||
auto seq =
|
||||
mgd_iter->second.super_version->mem->GetEarliestSequenceNumber();
|
||||
if (seq > snapshot) {
|
||||
retry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!retry) {
|
||||
if (last_try) {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto mgd_iter : multiget_cf_data) {
|
||||
mgd_iter.second->super_version =
|
||||
mgd_iter.second->cfd->GetSuperVersion()->Ref();
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
// Contain a list of merge operations if merge occurs.
|
||||
MergeContext merge_context;
|
||||
@@ -1393,7 +1584,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
auto mgd_iter = multiget_cf_data.find(cfh->cfd()->GetID());
|
||||
assert(mgd_iter != multiget_cf_data.end());
|
||||
auto mgd = mgd_iter->second;
|
||||
auto super_version = mgd->super_version;
|
||||
auto super_version = mgd.super_version;
|
||||
bool skip_memtable =
|
||||
(read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
@@ -1429,24 +1620,14 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
autovector<SuperVersion*> superversions_to_delete;
|
||||
|
||||
// TODO(icanadi) do we need lock here or just around Cleanup()?
|
||||
mutex_.Lock();
|
||||
for (auto mgd_iter : multiget_cf_data) {
|
||||
auto mgd = mgd_iter.second;
|
||||
if (mgd->super_version->Unref()) {
|
||||
mgd->super_version->Cleanup();
|
||||
superversions_to_delete.push_back(mgd->super_version);
|
||||
if (!last_try) {
|
||||
ReturnAndCleanupSuperVersion(mgd.cfd, mgd.super_version);
|
||||
} else {
|
||||
mgd.cfd->GetSuperVersion()->Unref();
|
||||
}
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
for (auto td : superversions_to_delete) {
|
||||
delete td;
|
||||
}
|
||||
for (auto mgd : multiget_cf_data) {
|
||||
delete mgd.second;
|
||||
}
|
||||
|
||||
RecordTick(stats_, NUMBER_MULTIGET_CALLS);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_READ, num_keys);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_FOUND, num_found);
|
||||
@@ -1865,7 +2046,7 @@ Status DBImpl::NewIterators(
|
||||
if (read_options.tailing) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
return Status::InvalidArgument(
|
||||
"Tailing interator not supported in RocksDB lite");
|
||||
"Tailing iterator not supported in RocksDB lite");
|
||||
#else
|
||||
for (auto cfh : column_families) {
|
||||
auto cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
@@ -1904,21 +2085,32 @@ const Snapshot* DBImpl::GetSnapshotForWriteConflictBoundary() {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary) {
|
||||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
bool lock) {
|
||||
int64_t unix_time = 0;
|
||||
env_->GetCurrentTime(&unix_time); // Ignore error
|
||||
SnapshotImpl* s = new SnapshotImpl;
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (lock) {
|
||||
mutex_.Lock();
|
||||
}
|
||||
// returns null if the underlying memtable does not support snapshot.
|
||||
if (!is_snapshot_supported_) {
|
||||
if (lock) {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
auto snapshot_seq = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
return snapshots_.New(s, snapshot_seq, unix_time, is_write_conflict_boundary);
|
||||
SnapshotImpl* snapshot =
|
||||
snapshots_.New(s, snapshot_seq, unix_time, is_write_conflict_boundary);
|
||||
if (lock) {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
@@ -2386,9 +2578,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_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions(), FlushReason::kDeleteFiles);
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
FindObsoleteFiles(&job_context, false);
|
||||
} // lock released here
|
||||
@@ -2471,9 +2663,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_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions(), FlushReason::kDeleteFiles);
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
for (auto* deleted_file : deleted_files) {
|
||||
deleted_file->being_compacted = false;
|
||||
@@ -2863,11 +3055,15 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status s;
|
||||
|
||||
versions_->options_file_number_ = versions_->NewFileNumber();
|
||||
uint64_t options_file_number = versions_->NewFileNumber();
|
||||
std::string options_file_name =
|
||||
OptionsFileName(GetName(), versions_->options_file_number_);
|
||||
OptionsFileName(GetName(), options_file_number);
|
||||
// Retry if the file name happen to conflict with an existing one.
|
||||
s = GetEnv()->RenameFile(file_name, options_file_name);
|
||||
if (s.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
versions_->options_file_number_ = options_file_number;
|
||||
}
|
||||
|
||||
if (0 == disable_delete_obsolete_files_) {
|
||||
DeleteObsoleteOptionsFiles();
|
||||
@@ -2918,6 +3114,8 @@ void DumpRocksDBBuildVersion(Logger* log) {
|
||||
ROCKSDB_MINOR, ROCKSDB_PATCH);
|
||||
ROCKS_LOG_HEADER(log, "Git sha %s", rocksdb_build_git_sha);
|
||||
ROCKS_LOG_HEADER(log, "Compile date %s", rocksdb_build_compile_date);
|
||||
#else
|
||||
(void)log; // ignore "-Wunused-parameter"
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3036,74 +3234,124 @@ 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");
|
||||
IngestExternalFileArg arg;
|
||||
arg.column_family = column_family;
|
||||
arg.external_files = external_files;
|
||||
arg.options = ingestion_options;
|
||||
return IngestExternalFiles({arg});
|
||||
}
|
||||
|
||||
Status DBImpl::IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) {
|
||||
if (args.empty()) {
|
||||
return Status::InvalidArgument("ingestion arg list is empty");
|
||||
}
|
||||
|
||||
Status status;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
// Ingest should immediately fail if ingest_behind is requested,
|
||||
// but the DB doesn't support it.
|
||||
if (ingestion_options.ingest_behind) {
|
||||
if (!immutable_db_options_.allow_ingest_behind) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
}
|
||||
}
|
||||
|
||||
ExternalSstFileIngestionJob ingestion_job(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
&snapshots_, ingestion_options);
|
||||
|
||||
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_);
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
// Don't ingest files when there is a bg_error
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
// 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);
|
||||
std::unordered_set<ColumnFamilyHandle*> unique_cfhs;
|
||||
for (const auto& arg : args) {
|
||||
if (arg.column_family == nullptr) {
|
||||
return Status::InvalidArgument("column family handle is null");
|
||||
} else if (unique_cfhs.count(arg.column_family) > 0) {
|
||||
return Status::InvalidArgument(
|
||||
"ingestion args have duplicate column families");
|
||||
}
|
||||
unique_cfhs.insert(arg.column_family);
|
||||
}
|
||||
}
|
||||
dummy_sv_ctx.Clean();
|
||||
// Ingest multiple external SST files atomically.
|
||||
size_t num_cfs = args.size();
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
if (args[i].external_files.empty()) {
|
||||
char err_msg[128] = {0};
|
||||
snprintf(err_msg, 128, "external_files[%zu] is empty", i);
|
||||
return Status::InvalidArgument(err_msg);
|
||||
}
|
||||
}
|
||||
for (const auto& arg : args) {
|
||||
const IngestExternalFileOptions& ingest_opts = arg.options;
|
||||
if (ingest_opts.ingest_behind &&
|
||||
!immutable_db_options_.allow_ingest_behind) {
|
||||
return Status::InvalidArgument(
|
||||
"can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (yanqin) maybe handle the case in which column_families have
|
||||
// duplicates
|
||||
std::list<uint64_t>::iterator pending_output_elem;
|
||||
size_t total = 0;
|
||||
for (const auto& arg : args) {
|
||||
total += arg.external_files.size();
|
||||
}
|
||||
uint64_t next_file_number = 0;
|
||||
Status status = ReserveFileNumbersBeforeIngestion(
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[0].column_family)->cfd(), total,
|
||||
&pending_output_elem, &next_file_number);
|
||||
if (!status.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
return status;
|
||||
}
|
||||
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
status =
|
||||
ingestion_job.Prepare(external_files, next_file_number, super_version);
|
||||
CleanupSuperVersion(super_version);
|
||||
std::vector<ExternalSstFileIngestionJob> ingestion_jobs;
|
||||
for (const auto& arg : args) {
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(arg.column_family)->cfd();
|
||||
ingestion_jobs.emplace_back(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
&snapshots_, arg.options);
|
||||
}
|
||||
std::vector<std::pair<bool, Status>> exec_results;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
exec_results.emplace_back(false, Status::OK());
|
||||
}
|
||||
// TODO(yanqin) maybe make jobs run in parallel
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
uint64_t start_file_number =
|
||||
next_file_number + args[i - 1].external_files.size();
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
exec_results[i].second = ingestion_jobs[i].Prepare(
|
||||
args[i].external_files, start_file_number, super_version);
|
||||
exec_results[i].first = true;
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:BeforeLastJobPrepare:0");
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:BeforeLastJobPrepare:1");
|
||||
{
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[0].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
exec_results[0].second = ingestion_jobs[0].Prepare(
|
||||
args[0].external_files, next_file_number, super_version);
|
||||
exec_results[0].first = true;
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
for (const auto& exec_result : exec_results) {
|
||||
if (!exec_result.second.ok()) {
|
||||
status = exec_result.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!status.ok()) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
if (exec_results[i].first) {
|
||||
ingestion_jobs[i].Cleanup(status);
|
||||
}
|
||||
}
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
return status;
|
||||
}
|
||||
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
std::vector<SuperVersionContext> sv_ctxs;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
sv_ctxs.emplace_back(true /* create_superversion */);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:BeforeJobsRun:0");
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:BeforeJobsRun:1");
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:Start");
|
||||
{
|
||||
// Lock db mutex
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:MutexLock");
|
||||
|
||||
@@ -3115,56 +3363,133 @@ Status DBImpl::IngestExternalFile(
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
num_running_ingest_file_++;
|
||||
num_running_ingest_file_ += static_cast<int>(num_cfs);
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFile:AfterIncIngestFileCounter");
|
||||
|
||||
// We cannot ingest a file into a dropped CF
|
||||
if (cfd->IsDropped()) {
|
||||
status = Status::InvalidArgument(
|
||||
"Cannot ingest an external file into a dropped CF");
|
||||
}
|
||||
|
||||
// Figure out if we need to flush the memtable first
|
||||
if (status.ok()) {
|
||||
bool need_flush = false;
|
||||
status = ingestion_job.NeedsFlush(&need_flush, cfd->GetSuperVersion());
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IngestExternalFile:NeedFlush",
|
||||
&need_flush);
|
||||
if (status.ok() && need_flush) {
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
} else {
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
}
|
||||
mutex_.Lock();
|
||||
bool at_least_one_cf_need_flush = false;
|
||||
std::vector<bool> need_flush(num_cfs, false);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (cfd->IsDropped()) {
|
||||
// TODO (yanqin) investigate whether we should abort ingestion or
|
||||
// proceed with other non-dropped column families.
|
||||
status = Status::InvalidArgument(
|
||||
"cannot ingest an external file into a dropped CF");
|
||||
break;
|
||||
}
|
||||
bool tmp = false;
|
||||
status = ingestion_jobs[i].NeedsFlush(&tmp, cfd->GetSuperVersion());
|
||||
need_flush[i] = tmp;
|
||||
at_least_one_cf_need_flush = (at_least_one_cf_need_flush || tmp);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IngestExternalFile:NeedFlush",
|
||||
&at_least_one_cf_need_flush);
|
||||
|
||||
// Run the ingestion job
|
||||
if (status.ok()) {
|
||||
status = ingestion_job.Run();
|
||||
if (status.ok() && at_least_one_cf_need_flush) {
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds_to_flush;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds_to_flush);
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds_to_flush, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
if (need_flush[i]) {
|
||||
mutex_.Unlock();
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)
|
||||
->cfd();
|
||||
status = FlushMemTable(cfd, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
mutex_.Lock();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Install job edit [Mutex will be unlocked here]
|
||||
auto mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
// Run ingestion jobs.
|
||||
if (status.ok()) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
status = ingestion_jobs[i].Run();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
bool should_increment_last_seqno =
|
||||
ingestion_jobs[0].ShouldIncrementLastSequence();
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
assert(should_increment_last_seqno ==
|
||||
ingestion_jobs[i].ShouldIncrementLastSequence());
|
||||
}
|
||||
#endif
|
||||
if (should_increment_last_seqno) {
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
versions_->SetLastAllocatedSequence(last_seqno + 1);
|
||||
versions_->SetLastPublishedSequence(last_seqno + 1);
|
||||
versions_->SetLastSequence(last_seqno + 1);
|
||||
}
|
||||
autovector<ColumnFamilyData*> cfds_to_commit;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
uint32_t num_entries = 0;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfds_to_commit.push_back(cfd);
|
||||
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
|
||||
autovector<VersionEdit*> edit_list;
|
||||
edit_list.push_back(ingestion_jobs[i].edit());
|
||||
edit_lists.push_back(edit_list);
|
||||
++num_entries;
|
||||
}
|
||||
// Mark the version edits as an atomic group if the number of version
|
||||
// edits exceeds 1.
|
||||
if (cfds_to_commit.size() > 1) {
|
||||
for (auto& edits : edit_lists) {
|
||||
assert(edits.size() == 1);
|
||||
edits[0]->MarkAtomicGroup(--num_entries);
|
||||
}
|
||||
assert(0 == num_entries);
|
||||
}
|
||||
status =
|
||||
versions_->LogAndApply(cfd, *mutable_cf_options, ingestion_job.edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
versions_->LogAndApply(cfds_to_commit, mutable_cf_options_list,
|
||||
edit_lists, &mutex_, directories_.GetDbDir());
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *mutable_cf_options,
|
||||
FlushReason::kExternalFileIngestion);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (!cfd->IsDropped()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_ctxs[i],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
#ifndef NDEBUG
|
||||
if (0 == i && num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::IngestExternalFiles:InstallSVForFirstCF:0");
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::IngestExternalFiles:InstallSVForFirstCF:1");
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
@@ -3173,30 +3498,36 @@ Status DBImpl::IngestExternalFile(
|
||||
}
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
|
||||
// Update stats
|
||||
if (status.ok()) {
|
||||
ingestion_job.UpdateStats();
|
||||
for (auto& job : ingestion_jobs) {
|
||||
job.UpdateStats();
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
|
||||
num_running_ingest_file_--;
|
||||
if (num_running_ingest_file_ == 0) {
|
||||
num_running_ingest_file_ -= static_cast<int>(num_cfs);
|
||||
if (0 == num_running_ingest_file_) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:MutexUnlock");
|
||||
}
|
||||
// mutex_ is unlocked here
|
||||
|
||||
// Cleanup
|
||||
sv_context.Clean();
|
||||
ingestion_job.Cleanup(status);
|
||||
|
||||
if (status.ok()) {
|
||||
NotifyOnExternalFileIngested(cfd, ingestion_job);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
sv_ctxs[i].Clean();
|
||||
// This may rollback jobs that have completed successfully. This is
|
||||
// intended for atomicity.
|
||||
ingestion_jobs[i].Cleanup(status);
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (!cfd->IsDropped()) {
|
||||
NotifyOnExternalFileIngested(cfd, ingestion_jobs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3316,6 +3647,35 @@ Status DBImpl::TraceIteratorSeekForPrev(const uint32_t& cf_id,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::ReserveFileNumbersBeforeIngestion(
|
||||
ColumnFamilyData* cfd, uint64_t num,
|
||||
std::list<uint64_t>::iterator* pending_output_elem,
|
||||
uint64_t* next_file_number) {
|
||||
Status s;
|
||||
SuperVersionContext dummy_sv_ctx(true /* create_superversion */);
|
||||
assert(nullptr != pending_output_elem);
|
||||
assert(nullptr != next_file_number);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
// Do not ingest files when there is a bg_error
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
*pending_output_elem = CaptureCurrentFileNumberInPendingOutputs();
|
||||
*next_file_number = versions_->FetchAddFileNumber(static_cast<uint64_t>(num));
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
VersionEdit dummy_edit;
|
||||
// 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.
|
||||
s = versions_->LogAndApply(cfd, *cf_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
|
||||
}
|
||||
dummy_sv_ctx.Clean();
|
||||
return s;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
+115
-38
@@ -31,7 +31,7 @@
|
||||
#include "db/log_writer.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/pre_release_callback.h"
|
||||
#include "db/range_del_aggregator_v2.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
@@ -63,8 +63,10 @@ namespace rocksdb {
|
||||
|
||||
class Arena;
|
||||
class ArenaWrappedDBIter;
|
||||
class InMemoryStatsHistoryIterator;
|
||||
class MemTable;
|
||||
class TableCache;
|
||||
class TaskLimiterToken;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
@@ -191,13 +193,13 @@ class DBImpl : public DB {
|
||||
const Slice* begin, const Slice* end) override;
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual Status CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level,
|
||||
const int output_path_id = -1,
|
||||
std::vector<std::string>* const output_file_names
|
||||
= nullptr) override;
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names, const int output_level,
|
||||
const int output_path_id = -1,
|
||||
std::vector<std::string>* const output_file_names = nullptr,
|
||||
CompactionJobInfo* compaction_job_info = nullptr) override;
|
||||
|
||||
virtual Status PauseBackgroundWork() override;
|
||||
virtual Status ContinueBackgroundWork() override;
|
||||
@@ -237,6 +239,13 @@ class DBImpl : public DB {
|
||||
virtual Status SyncWAL() override;
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
virtual SequenceNumber GetLastPublishedSequence() const {
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
return versions_->LastSequence();
|
||||
} else {
|
||||
return versions_->LastPublishedSequence();
|
||||
}
|
||||
}
|
||||
// REQUIRES: joined the main write queue if two_write_queues is disabled, and
|
||||
// the second write queue otherwise.
|
||||
virtual void SetLastPublishedSequence(SequenceNumber seq);
|
||||
@@ -338,6 +347,10 @@ class DBImpl : public DB {
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
using DB::IngestExternalFiles;
|
||||
virtual Status IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) override;
|
||||
|
||||
virtual Status VerifyChecksum() override;
|
||||
|
||||
using DB::StartTrace;
|
||||
@@ -374,8 +387,8 @@ class DBImpl : public DB {
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
// The returned iterator should be deleted when no longer needed.
|
||||
InternalIterator* NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregatorV2* range_del_agg,
|
||||
SequenceNumber sequence, ColumnFamilyHandle* column_family = nullptr);
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
LogsWithPrepTracker* logs_with_prep_tracker() {
|
||||
return &logs_with_prep_tracker_;
|
||||
@@ -470,7 +483,10 @@ class DBImpl : public DB {
|
||||
int TEST_BGCompactionsAllowed() const;
|
||||
int TEST_BGFlushesAllowed() const;
|
||||
size_t TEST_GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
void TEST_WaitForTimedTaskRun(std::function<void()> callback) const;
|
||||
void TEST_WaitForDumpStatsRun(std::function<void()> callback) const;
|
||||
void TEST_WaitForPersistStatsRun(std::function<void()> callback) const;
|
||||
bool TEST_IsPersistentStatsEnabled() const;
|
||||
size_t TEST_EstiamteStatsHistorySize() const;
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
@@ -578,12 +594,9 @@ class DBImpl : public DB {
|
||||
|
||||
const WriteController& write_controller() { return write_controller_; }
|
||||
|
||||
InternalIterator* NewInternalIterator(const ReadOptions&,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregatorV2* range_del_agg,
|
||||
SequenceNumber sequence);
|
||||
InternalIterator* NewInternalIterator(
|
||||
const ReadOptions&, ColumnFamilyData* cfd, SuperVersion* super_version,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence);
|
||||
|
||||
// hollow transactions shell used for recovery.
|
||||
// these will then be passed to TransactionDB so that
|
||||
@@ -695,6 +708,12 @@ class DBImpl : public DB {
|
||||
|
||||
void SetSnapshotChecker(SnapshotChecker* snapshot_checker);
|
||||
|
||||
// Fill JobContext with snapshot information needed by flush and compaction.
|
||||
void GetSnapshotContext(JobContext* job_context,
|
||||
std::vector<SequenceNumber>* snapshot_seqs,
|
||||
SequenceNumber* earliest_write_conflict_snapshot,
|
||||
SnapshotChecker** snapshot_checker);
|
||||
|
||||
// Not thread-safe.
|
||||
void SetRecoverableStatePreReleaseCallback(PreReleaseCallback* callback);
|
||||
|
||||
@@ -713,6 +732,17 @@ class DBImpl : public DB {
|
||||
static Status CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory);
|
||||
|
||||
// Given a time window, return an iterator for accessing stats history
|
||||
Status GetStatsHistory(
|
||||
uint64_t start_time, uint64_t end_time,
|
||||
std::unique_ptr<StatsHistoryIterator>* stats_iterator) override;
|
||||
|
||||
// find stats map from stats_history_ with smallest timestamp in
|
||||
// the range of [start_time, end_time)
|
||||
bool FindStatsByTime(uint64_t start_time, uint64_t end_time,
|
||||
uint64_t* new_time,
|
||||
std::map<std::string, uint64_t>* stats_map);
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
@@ -912,11 +942,13 @@ class DBImpl : public DB {
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful. Then
|
||||
// installs a new super version for the column family.
|
||||
Status FlushMemTableToOutputFile(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, JobContext* job_context,
|
||||
SuperVersionContext* superversion_context,
|
||||
LogBuffer* log_buffer);
|
||||
Status FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, JobContext* job_context,
|
||||
SuperVersionContext* superversion_context,
|
||||
std::vector<SequenceNumber>& snapshot_seqs,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, LogBuffer* log_buffer);
|
||||
|
||||
// Argument required by background flush thread.
|
||||
struct BGFlushArg {
|
||||
@@ -1053,7 +1085,8 @@ class DBImpl : public DB {
|
||||
const std::vector<std::string>& input_file_names,
|
||||
std::vector<std::string>* const output_file_names,
|
||||
const int output_level, int output_path_id,
|
||||
JobContext* job_context, LogBuffer* log_buffer);
|
||||
JobContext* job_context, LogBuffer* log_buffer,
|
||||
CompactionJobInfo* compaction_job_info);
|
||||
|
||||
// Wait for current IngestExternalFile() calls to finish.
|
||||
// REQUIRES: mutex_ held
|
||||
@@ -1106,11 +1139,22 @@ class DBImpl : public DB {
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
bool* sfm_bookkeeping, LogBuffer* log_buffer);
|
||||
|
||||
// Request compaction tasks token from compaction thread limiter.
|
||||
// It always succeeds if force = true or limiter is disable.
|
||||
bool RequestCompactionToken(ColumnFamilyData* cfd, bool force,
|
||||
std::unique_ptr<TaskLimiterToken>* token,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Schedule background tasks
|
||||
void StartTimedTasks();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
size_t EstiamteStatsHistorySize() const;
|
||||
|
||||
// persist stats to column family "_persistent_stats"
|
||||
void PersistStats();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
void DumpStats();
|
||||
|
||||
@@ -1129,10 +1173,15 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* PopFirstFromCompactionQueue();
|
||||
FlushRequest PopFirstFromFlushQueue();
|
||||
|
||||
// Pick the first unthrottled compaction with task token from queue.
|
||||
ColumnFamilyData* PickCompactionFromQueue(
|
||||
std::unique_ptr<TaskLimiterToken>* token, LogBuffer* log_buffer);
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
void MarkLogsSynced(uint64_t up_to, bool synced_dir, const Status& status);
|
||||
|
||||
SnapshotImpl* GetSnapshotImpl(bool is_write_conflict_boundary);
|
||||
SnapshotImpl* GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
bool lock = true);
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
@@ -1148,6 +1197,8 @@ class DBImpl : public DB {
|
||||
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
|
||||
FileLock* db_lock_;
|
||||
|
||||
// In addition to mutex_, log_write_mutex_ protected writes to stats_history_
|
||||
InstrumentedMutex stats_history_mutex_;
|
||||
// In addition to mutex_, log_write_mutex_ protected writes to logs_ and
|
||||
// logfile_number_. With two_write_queues it also protects alive_log_files_,
|
||||
// and log_empty_. Refer to the definition of each variable below for more
|
||||
@@ -1270,6 +1321,12 @@ class DBImpl : public DB {
|
||||
|
||||
bool is_snapshot_supported_;
|
||||
|
||||
std::map<uint64_t, std::map<std::string, uint64_t>> stats_history_;
|
||||
|
||||
std::map<std::string, uint64_t> stats_slice_;
|
||||
|
||||
bool stats_slice_initialized_ = false;
|
||||
|
||||
// Class to maintain directories for all database paths other than main one.
|
||||
class Directories {
|
||||
public:
|
||||
@@ -1355,7 +1412,7 @@ class DBImpl : public DB {
|
||||
// compacted. Consumers of these queues are flush and compaction threads. When
|
||||
// column family is put on this queue, we increase unscheduled_flushes_ and
|
||||
// unscheduled_compactions_. When these variables are bigger than zero, that
|
||||
// means we need to schedule background threads for compaction and thread.
|
||||
// means we need to schedule background threads for flush and compaction.
|
||||
// Once the background threads are scheduled, we decrease unscheduled_flushes_
|
||||
// and unscheduled_compactions_. That way we keep track of number of
|
||||
// compaction and flush threads we need to schedule. This scheduling is done
|
||||
@@ -1422,6 +1479,8 @@ class DBImpl : public DB {
|
||||
// caller retains ownership of `manual_compaction_state` as it is reused
|
||||
// across background compactions.
|
||||
ManualCompactionState* manual_compaction_state; // nullptr if non-manual
|
||||
// task limiter token is requested during compaction picking.
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
};
|
||||
std::deque<ManualCompactionState*> manual_compaction_dequeue_;
|
||||
|
||||
@@ -1461,7 +1520,7 @@ class DBImpl : public DB {
|
||||
std::atomic<bool> has_unpersisted_data_;
|
||||
|
||||
// if an attempt was made to flush all column families that
|
||||
// the oldest log depends on but uncommited data in the oldest
|
||||
// the oldest log depends on but uncommitted data in the oldest
|
||||
// log prevents the log from being released.
|
||||
// We must attempt to free the dependent memtables again
|
||||
// at a later time after the transaction in the oldest
|
||||
@@ -1513,10 +1572,14 @@ class DBImpl : public DB {
|
||||
// Only to be set during initialization
|
||||
std::unique_ptr<PreReleaseCallback> recoverable_state_pre_release_callback_;
|
||||
|
||||
// handle for scheduling jobs at fixed intervals
|
||||
// handle for scheduling stats dumping at fixed intervals
|
||||
// REQUIRES: mutex locked
|
||||
std::unique_ptr<rocksdb::RepeatableThread> thread_dump_stats_;
|
||||
|
||||
// handle for scheduling stats snapshoting at fixed intervals
|
||||
// REQUIRES: mutex locked
|
||||
std::unique_ptr<rocksdb::RepeatableThread> thread_persist_stats_;
|
||||
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
@@ -1530,8 +1593,7 @@ class DBImpl : public DB {
|
||||
// state needs flush or compaction.
|
||||
void InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
FlushReason flush_reason = FlushReason::kOthers);
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
@@ -1556,6 +1618,20 @@ class DBImpl : public DB {
|
||||
bool ShouldntRunManualCompaction(ManualCompactionState* m);
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
#ifndef ROCKSDB_LITE
|
||||
void BuildCompactionJobInfo(const ColumnFamilyData* cfd, Compaction* c,
|
||||
const Status& st,
|
||||
const CompactionJobStats& compaction_job_stats,
|
||||
const int job_id, const Version* current,
|
||||
CompactionJobInfo* compaction_job_info) const;
|
||||
// Reserve the next 'num' file numbers for to-be-ingested external SST files,
|
||||
// and return the current file_number in 'next_file_number'.
|
||||
// Write a version edit to the MANIFEST.
|
||||
Status ReserveFileNumbersBeforeIngestion(
|
||||
ColumnFamilyData* cfd, uint64_t num,
|
||||
std::list<uint64_t>::iterator* pending_output_elem,
|
||||
uint64_t* next_file_number);
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
bool ShouldPurge(uint64_t file_number) const;
|
||||
void MarkAsGrabbedForPurge(uint64_t file_number);
|
||||
@@ -1613,15 +1689,16 @@ class DBImpl : public DB {
|
||||
|
||||
ErrorHandler error_handler_;
|
||||
|
||||
// True if the DB is committing atomic flush.
|
||||
// TODO (yanqin) the current impl assumes that the entire DB belongs to
|
||||
// a single atomic flush group. In the future we need to add a new class
|
||||
// (struct) similar to the following to make it more general.
|
||||
// struct AtomicFlushGroup {
|
||||
// bool commit_in_progress_;
|
||||
// std::vector<MemTableList*> imm_lists;
|
||||
// };
|
||||
bool atomic_flush_commit_in_progress_;
|
||||
// Conditional variable to coordinate installation of atomic flush results.
|
||||
// With atomic flush, each bg thread installs the result of flushing multiple
|
||||
// column families, and different threads can flush different column
|
||||
// families. It's difficult to rely on one thread to perform batch
|
||||
// installation for all threads. This is different from the non-atomic flush
|
||||
// case.
|
||||
// atomic_flush_install_cv_ makes sure that threads install atomic flush
|
||||
// results sequentially. Flush results of memtables with lower IDs get
|
||||
// installed to MANIFEST first.
|
||||
InstrumentedCondVar atomic_flush_install_cv_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
+343
-165
@@ -20,6 +20,7 @@
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "util/concurrent_task_limiter_impl.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
@@ -59,6 +60,27 @@ bool DBImpl::EnoughRoomForCompaction(
|
||||
return enough_room;
|
||||
}
|
||||
|
||||
bool DBImpl::RequestCompactionToken(ColumnFamilyData* cfd, bool force,
|
||||
std::unique_ptr<TaskLimiterToken>* token,
|
||||
LogBuffer* log_buffer) {
|
||||
assert(*token == nullptr);
|
||||
auto limiter = static_cast<ConcurrentTaskLimiterImpl*>(
|
||||
cfd->ioptions()->compaction_thread_limiter.get());
|
||||
if (limiter == nullptr) {
|
||||
return true;
|
||||
}
|
||||
*token = limiter->GetToken(force);
|
||||
if (*token != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"Thread limiter [%s] increase [%s] compaction task, "
|
||||
"force: %s, tasks after: %d",
|
||||
limiter->GetName().c_str(), cfd->GetName().c_str(),
|
||||
force ? "true" : "false", limiter->GetOutstandingTask());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
@@ -110,19 +132,15 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
Status DBImpl::FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* made_progress, JobContext* job_context,
|
||||
SuperVersionContext* superversion_context, LogBuffer* log_buffer) {
|
||||
SuperVersionContext* superversion_context,
|
||||
std::vector<SequenceNumber>& snapshot_seqs,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, LogBuffer* log_buffer) {
|
||||
mutex_.AssertHeld();
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
nullptr /* memtable_id */, env_options_for_compaction_, versions_.get(),
|
||||
@@ -219,20 +237,30 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
return AtomicFlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
||||
job_context, log_buffer);
|
||||
}
|
||||
Status s;
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
Status status;
|
||||
for (auto& arg : bg_flush_args) {
|
||||
ColumnFamilyData* cfd = arg.cfd_;
|
||||
const MutableCFOptions& mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context = arg.superversion_context_;
|
||||
s = FlushMemTableToOutputFile(cfd, mutable_cf_options, made_progress,
|
||||
job_context, superversion_context,
|
||||
log_buffer);
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options, made_progress, job_context,
|
||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, log_buffer);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
status = s;
|
||||
if (!s.IsShutdownInProgress()) {
|
||||
// At this point, DB is not shutting down, nor is cfd dropped.
|
||||
// Something is wrong, thus we break out of the loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -261,40 +289,43 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
autovector<Directory*> distinct_output_dirs;
|
||||
autovector<std::string> distinct_output_dir_paths;
|
||||
std::vector<FlushJob> jobs;
|
||||
std::vector<MutableCFOptions> all_mutable_cf_options;
|
||||
int num_cfs = static_cast<int>(cfds.size());
|
||||
all_mutable_cf_options.reserve(num_cfs);
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
auto cfd = cfds[i];
|
||||
Directory* data_dir = GetDataDir(cfd, 0U);
|
||||
const std::string& curr_path = cfd->ioptions()->cf_paths[0].path;
|
||||
|
||||
// Add to distinct output directories if eligible. Use linear search. Since
|
||||
// the number of elements in the vector is not large, performance should be
|
||||
// tolerable.
|
||||
bool found = false;
|
||||
for (const auto dir : distinct_output_dirs) {
|
||||
if (dir == data_dir) {
|
||||
for (const auto& path : distinct_output_dir_paths) {
|
||||
if (path == curr_path) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
distinct_output_dir_paths.emplace_back(curr_path);
|
||||
distinct_output_dirs.emplace_back(data_dir);
|
||||
}
|
||||
|
||||
const MutableCFOptions& mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||
const uint64_t* max_memtable_id = &(bg_flush_args[i].max_memtable_id_);
|
||||
jobs.emplace_back(
|
||||
dbname_, cfds[i], immutable_db_options_, mutable_cf_options,
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
max_memtable_id, env_options_for_compaction_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, log_buffer, directories_.GetDbDir(),
|
||||
@@ -304,21 +335,18 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs.back().PickMemTable();
|
||||
}
|
||||
|
||||
autovector<FileMetaData> file_meta;
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
Status s;
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
file_meta.emplace_back();
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const MutableCFOptions& mutable_cf_options =
|
||||
*cfds[i]->GetLatestMutableCFOptions();
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfds[i], &file_meta[i], mutable_cf_options,
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
#endif /* !ROCKSDB_LITE */
|
||||
}
|
||||
#endif /* !ROCKSDB_LITE */
|
||||
|
||||
if (logfile_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
@@ -331,8 +359,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
std::pair<bool, Status> elem(false, Status::OK());
|
||||
exec_status.emplace_back(elem);
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -341,10 +368,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
exec_status[i].second =
|
||||
jobs[i].Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
exec_status[i].first = true;
|
||||
if (!exec_status[i].second.ok()) {
|
||||
s = exec_status[i].second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
@@ -352,17 +375,27 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::AtomicFlushMemTablesToOutputFiles:SomeFlushJobsComplete:2");
|
||||
}
|
||||
if (s.ok()) {
|
||||
exec_status[0].second =
|
||||
jobs[0].Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].first = true;
|
||||
if (!exec_status[0].second.ok()) {
|
||||
s = exec_status[0].second;
|
||||
exec_status[0].second =
|
||||
jobs[0].Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].first = true;
|
||||
|
||||
Status error_status;
|
||||
for (const auto& e : exec_status) {
|
||||
if (!e.second.ok()) {
|
||||
s = e.second;
|
||||
if (!e.second.IsShutdownInProgress()) {
|
||||
// If a flush job did not return OK, and the CF is not dropped, and
|
||||
// the DB is not shutting down, then we have to return this result to
|
||||
// caller later.
|
||||
error_status = e.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
// Sync on all distinct output directories.
|
||||
for (auto dir : distinct_output_dirs) {
|
||||
if (dir != nullptr) {
|
||||
@@ -372,37 +405,78 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
mems_list.emplace_back(&mems);
|
||||
}
|
||||
autovector<ColumnFamilyData*> all_cfds;
|
||||
autovector<MemTableList*> imm_lists;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
all_cfds.emplace_back(cfd);
|
||||
imm_lists.emplace_back(cfd->imm());
|
||||
mutable_cf_options_list.emplace_back(cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
|
||||
s = MemTableList::TryInstallMemtableFlushResults(
|
||||
imm_lists, all_cfds, mutable_cf_options_list, mems_list,
|
||||
&atomic_flush_commit_in_progress_, &logs_with_prep_tracker_,
|
||||
versions_.get(), &mutex_, file_meta, &job_context->memtables_to_free,
|
||||
directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
auto wait_to_install_func = [&]() {
|
||||
bool ready = true;
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
if (cfds[i]->IsDropped()) {
|
||||
// If the column family is dropped, then do not wait.
|
||||
continue;
|
||||
} else if (!mems.empty() &&
|
||||
cfds[i]->imm()->GetEarliestMemTableID() < mems[0]->GetID()) {
|
||||
// If a flush job needs to install the flush result for mems and
|
||||
// mems[0] is not the earliest memtable, it means another thread must
|
||||
// be installing flush results for the same column family, then the
|
||||
// current thread needs to wait.
|
||||
ready = false;
|
||||
break;
|
||||
} else if (mems.empty() && cfds[i]->imm()->GetEarliestMemTableID() <=
|
||||
bg_flush_args[i].max_memtable_id_) {
|
||||
// If a flush job does not need to install flush results, then it has
|
||||
// to wait until all memtables up to max_memtable_id_ (inclusive) are
|
||||
// installed.
|
||||
ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ready;
|
||||
};
|
||||
|
||||
bool resuming_from_bg_err = error_handler_.IsDBStopped();
|
||||
while ((!error_handler_.IsDBStopped() ||
|
||||
error_handler_.GetRecoveryError().ok()) &&
|
||||
!wait_to_install_func()) {
|
||||
atomic_flush_install_cv_.Wait();
|
||||
}
|
||||
|
||||
s = resuming_from_bg_err ? error_handler_.GetRecoveryError()
|
||||
: error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
autovector<ColumnFamilyData*> tmp_cfds;
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
if (!cfds[i]->IsDropped() && !mems.empty()) {
|
||||
tmp_cfds.emplace_back(cfds[i]);
|
||||
mems_list.emplace_back(&mems);
|
||||
mutable_cf_options_list.emplace_back(&all_mutable_cf_options[i]);
|
||||
tmp_file_meta.emplace_back(&file_meta[i]);
|
||||
}
|
||||
}
|
||||
|
||||
s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &mutex_, tmp_file_meta,
|
||||
&job_context->memtables_to_free, directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
assert(num_cfs ==
|
||||
static_cast<int>(job_context->superversion_contexts.size()));
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
InstallSuperVersionAndScheduleWork(cfds[i],
|
||||
&job_context->superversion_contexts[i],
|
||||
*cfds[i]->GetLatestMutableCFOptions());
|
||||
all_mutable_cf_options[i]);
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Level summary: %s\n",
|
||||
cfds[i]->GetName().c_str(),
|
||||
@@ -415,8 +489,10 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
NotifyOnFlushCompleted(cfds[i], &file_meta[i],
|
||||
*cfds[i]->GetLatestMutableCFOptions(),
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
NotifyOnFlushCompleted(cfds[i], &file_meta[i], all_mutable_cf_options[i],
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
if (sfm) {
|
||||
std::string file_path = MakeTableFileName(
|
||||
@@ -434,7 +510,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
// Have to cancel the flush jobs that have NOT executed because we need to
|
||||
// unref the versions.
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
@@ -442,17 +520,15 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs[i].Cancel();
|
||||
}
|
||||
}
|
||||
if (!s.IsShutdownInProgress()) {
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (exec_status[i].first && exec_status[i].second.ok()) {
|
||||
auto& mems = jobs[i].GetMemTables();
|
||||
cfds[i]->imm()->RollbackMemtableFlush(mems,
|
||||
file_meta[i].fd.GetNumber());
|
||||
}
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (exec_status[i].first && exec_status[i].second.ok()) {
|
||||
auto& mems = jobs[i].GetMemTables();
|
||||
cfds[i]->imm()->RollbackMemtableFlush(mems,
|
||||
file_meta[i].fd.GetNumber());
|
||||
}
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -479,6 +555,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
mutex_.Unlock();
|
||||
{
|
||||
FlushJobInfo info;
|
||||
info.cf_id = cfd->GetID();
|
||||
info.cf_name = cfd->GetName();
|
||||
// TODO(yhchiang): make db_paths dynamic in case flush does not
|
||||
// go to L0 in the future.
|
||||
@@ -530,6 +607,7 @@ void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
mutex_.Unlock();
|
||||
{
|
||||
FlushJobInfo info;
|
||||
info.cf_id = cfd->GetID();
|
||||
info.cf_name = cfd->GetName();
|
||||
// TODO(yhchiang): make db_paths dynamic in case flush does not
|
||||
// go to L0 in the future.
|
||||
@@ -703,7 +781,8 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level, const int output_path_id,
|
||||
std::vector<std::string>* const output_file_names) {
|
||||
std::vector<std::string>* const output_file_names,
|
||||
CompactionJobInfo* compaction_job_info) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
(void)compact_options;
|
||||
(void)column_family;
|
||||
@@ -711,6 +790,7 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
(void)output_level;
|
||||
(void)output_path_id;
|
||||
(void)output_file_names;
|
||||
(void)compaction_job_info;
|
||||
// not supported in lite version
|
||||
return Status::NotSupported("Not supported in ROCKSDB LITE");
|
||||
#else
|
||||
@@ -742,7 +822,7 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
|
||||
s = CompactFilesImpl(compact_options, cfd, current, input_file_names,
|
||||
output_file_names, output_level, output_path_id,
|
||||
&job_context, &log_buffer);
|
||||
&job_context, &log_buffer, compaction_job_info);
|
||||
|
||||
current->Unref();
|
||||
}
|
||||
@@ -782,7 +862,8 @@ Status DBImpl::CompactFilesImpl(
|
||||
const CompactionOptions& compact_options, ColumnFamilyData* cfd,
|
||||
Version* version, const std::vector<std::string>& input_file_names,
|
||||
std::vector<std::string>* const output_file_names, const int output_level,
|
||||
int output_path_id, JobContext* job_context, LogBuffer* log_buffer) {
|
||||
int output_path_id, JobContext* job_context, LogBuffer* log_buffer,
|
||||
CompactionJobInfo* compaction_job_info) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
@@ -856,18 +937,17 @@ Status DBImpl::CompactFilesImpl(
|
||||
// deletion compaction currently not allowed in CompactFiles.
|
||||
assert(!c->deletion_compaction());
|
||||
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJobStats compaction_job_stats;
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
@@ -877,19 +957,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
nullptr); // Here we pass a nullptr for CompactionJobStats because
|
||||
// CompactFiles does not trigger OnCompactionCompleted(),
|
||||
// which is the only place where CompactionJobStats is
|
||||
// returned. The idea of not triggering OnCompationCompleted()
|
||||
// is that CompactFiles runs in the caller thread, so the user
|
||||
// should always know when it completes. As a result, it makes
|
||||
// less sense to notify the users something they should already
|
||||
// know.
|
||||
//
|
||||
// In the future, if we would like to add CompactionJobStats
|
||||
// support for CompactFiles, we should have CompactFiles API
|
||||
// pass a pointer of CompactionJobStats as the out-value
|
||||
// instead of using EventListener.
|
||||
&compaction_job_stats);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -910,9 +978,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
Status status = compaction_job.Install(*c->mutable_cf_options());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kManualCompaction);
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
c->ReleaseCompactionFiles(s);
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -926,6 +994,11 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
if (compaction_job_info != nullptr) {
|
||||
BuildCompactionJobInfo(cfd, c.get(), s, compaction_job_stats,
|
||||
job_context->job_id, version, compaction_job_info);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
// Done
|
||||
} else if (status.IsShutdownInProgress()) {
|
||||
@@ -953,6 +1026,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (bg_compaction_scheduled_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
TEST_SYNC_POINT("CompactFilesImpl:End");
|
||||
|
||||
return status;
|
||||
@@ -1068,36 +1142,8 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionCompleted::UnlockMutex");
|
||||
{
|
||||
CompactionJobInfo info;
|
||||
info.cf_id = cfd->GetID();
|
||||
info.cf_name = cfd->GetName();
|
||||
info.status = st;
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
info.base_input_level = c->start_level();
|
||||
info.output_level = c->output_level();
|
||||
info.stats = compaction_job_stats;
|
||||
info.table_properties = c->GetOutputTableProperties();
|
||||
info.compaction_reason = c->compaction_reason();
|
||||
info.compression = c->output_compression();
|
||||
for (size_t i = 0; i < c->num_input_levels(); ++i) {
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
auto fn = TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
fmd->fd.GetNumber(), fmd->fd.GetPathId());
|
||||
info.input_files.push_back(fn);
|
||||
if (info.table_properties.count(fn) == 0) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
auto s = current->GetTableProperties(&tp, fmd, &fn);
|
||||
if (s.ok()) {
|
||||
info.table_properties[fn] = tp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
info.output_files.push_back(TableFileName(
|
||||
c->immutable_cf_options()->cf_paths, newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
}
|
||||
BuildCompactionJobInfo(cfd, c, st, compaction_job_stats, job_id, current,
|
||||
&info);
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionCompleted(this, info);
|
||||
}
|
||||
@@ -1352,6 +1398,8 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] Manual compaction starting", cfd->GetName().c_str());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
// We don't check bg_error_ here, because if we get the error in compaction,
|
||||
// the compaction will set manual.status to bg_error_ and set manual.done to
|
||||
// true.
|
||||
@@ -1389,6 +1437,11 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->manual_compaction_state = &manual;
|
||||
ca->prepicked_compaction->compaction = compaction;
|
||||
if (!RequestCompactionToken(
|
||||
cfd, true, &ca->prepicked_compaction->task_token, &log_buffer)) {
|
||||
// Don't throttle manual compaction, only count outstanding tasks.
|
||||
assert(false);
|
||||
}
|
||||
manual.incomplete = false;
|
||||
bg_compaction_scheduled_++;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
@@ -1397,6 +1450,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
}
|
||||
}
|
||||
|
||||
log_buffer.FlushBufferToLog();
|
||||
assert(!manual.in_progress);
|
||||
assert(HasPendingManualCompaction());
|
||||
RemoveManualCompaction(&manual);
|
||||
@@ -1407,6 +1461,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushRequest* req) {
|
||||
assert(req != nullptr);
|
||||
req->reserve(cfds.size());
|
||||
for (const auto cfd : cfds) {
|
||||
if (nullptr == cfd) {
|
||||
// cfd may be null, see DBImpl::ScheduleFlushes
|
||||
@@ -1440,11 +1495,16 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
}
|
||||
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
@@ -1518,6 +1578,9 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
for (auto cfd : cfds) {
|
||||
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
cfd->Unref();
|
||||
@@ -1539,6 +1602,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
@@ -1822,6 +1886,32 @@ DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
||||
return flush_req;
|
||||
}
|
||||
|
||||
ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
||||
std::unique_ptr<TaskLimiterToken>* token, LogBuffer* log_buffer) {
|
||||
assert(!compaction_queue_.empty());
|
||||
assert(*token == nullptr);
|
||||
autovector<ColumnFamilyData*> throttled_candidates;
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
while (!compaction_queue_.empty()) {
|
||||
auto first_cfd = *compaction_queue_.begin();
|
||||
compaction_queue_.pop_front();
|
||||
assert(first_cfd->queued_for_compaction());
|
||||
if (!RequestCompactionToken(first_cfd, false, token, log_buffer)) {
|
||||
throttled_candidates.push_back(first_cfd);
|
||||
continue;
|
||||
}
|
||||
cfd = first_cfd;
|
||||
cfd->set_queued_for_compaction(false);
|
||||
break;
|
||||
}
|
||||
// Add throttled compaction candidates back to queue in the original order.
|
||||
for (auto iter = throttled_candidates.rbegin();
|
||||
iter != throttled_candidates.rend(); ++iter) {
|
||||
compaction_queue_.push_front(*iter);
|
||||
}
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req,
|
||||
FlushReason flush_reason) {
|
||||
if (flush_req.empty()) {
|
||||
@@ -2046,6 +2136,7 @@ void DBImpl::BackgroundCallFlush() {
|
||||
bg_flush_scheduled_--;
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
atomic_flush_install_cv_.SignalAll();
|
||||
bg_cv_.SignalAll();
|
||||
// IMPORTANT: there should be no code after calling SignalAll. This call may
|
||||
// signal the DB destructor that it's OK to proceed with destruction. In
|
||||
@@ -2079,7 +2170,12 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
Status s = BackgroundCompaction(&made_progress, &job_context, &log_buffer,
|
||||
prepicked_compaction);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:1");
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
if (s.IsBusy()) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
env_->SleepForMicroseconds(10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
@@ -2214,6 +2310,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->in_progress = true;
|
||||
}
|
||||
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
|
||||
// InternalKey manual_end_storage;
|
||||
// InternalKey* manual_end = &manual_end_storage;
|
||||
bool sfm_reserved_compact_space = false;
|
||||
@@ -2265,17 +2363,23 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// cfd is referenced here
|
||||
auto cfd = PopFirstFromCompactionQueue();
|
||||
auto cfd = PickCompactionFromQueue(&task_token, log_buffer);
|
||||
if (cfd == nullptr) {
|
||||
// Can't find any executable task from the compaction queue.
|
||||
// All tasks have been throttled by compaction thread limiter.
|
||||
++unscheduled_compactions_;
|
||||
return Status::Busy();
|
||||
}
|
||||
|
||||
// We unreference here because the following code will take a Ref() on
|
||||
// this cfd if it is going to use it (Compaction class holds a
|
||||
// reference).
|
||||
// This will all happen under a mutex so we don't have to be afraid of
|
||||
// somebody else deleting it.
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
// This was the last reference of the column family, so no need to
|
||||
// compact.
|
||||
delete cfd;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -2345,6 +2449,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
} else if (c->deletion_compaction()) {
|
||||
// TODO(icanadi) Do we want to honor snapshots here? i.e. not delete old
|
||||
// file if there is alive snapshot pointing to it
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
c->column_family_data());
|
||||
assert(c->num_input_files(1) == 0);
|
||||
assert(c->level() == 0);
|
||||
assert(c->column_family_data()->ioptions()->compaction_style ==
|
||||
@@ -2361,15 +2467,19 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (!trivial_move_disallowed && c->IsTrivialMove()) {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:TrivialMove");
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
c->column_family_data());
|
||||
// Instrument for event update
|
||||
// TODO(yhchiang): add op details for showing trivial-move.
|
||||
ThreadStatusUtil::SetColumnFamily(
|
||||
@@ -2411,9 +2521,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
|
||||
@@ -2435,6 +2545,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
// Clear Instrument
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (!is_prepicked && c->output_level() > 0 &&
|
||||
c->output_level() ==
|
||||
c->column_family_data()
|
||||
@@ -2452,22 +2564,23 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->compaction = c.release();
|
||||
ca->prepicked_compaction->manual_compaction_state = nullptr;
|
||||
// Transfer requested token, so it doesn't need to do it again.
|
||||
ca->prepicked_compaction->task_token = std::move(task_token);
|
||||
++bg_bottom_compaction_scheduled_;
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca, Env::Priority::BOTTOM,
|
||||
this, &DBImpl::UnscheduleCallback);
|
||||
} else {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
c->column_family_data());
|
||||
int output_level __attribute__((__unused__));
|
||||
output_level = c->output_level();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:NonTrivial",
|
||||
&output_level);
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
@@ -2491,11 +2604,13 @@ 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_contexts[0],
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
}
|
||||
if (c != nullptr) {
|
||||
c->ReleaseCompactionFiles(status);
|
||||
@@ -2676,6 +2791,45 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBImpl::BuildCompactionJobInfo(
|
||||
const ColumnFamilyData* cfd, Compaction* c, const Status& st,
|
||||
const CompactionJobStats& compaction_job_stats, const int job_id,
|
||||
const Version* current, CompactionJobInfo* compaction_job_info) const {
|
||||
assert(compaction_job_info != nullptr);
|
||||
compaction_job_info->cf_id = cfd->GetID();
|
||||
compaction_job_info->cf_name = cfd->GetName();
|
||||
compaction_job_info->status = st;
|
||||
compaction_job_info->thread_id = env_->GetThreadID();
|
||||
compaction_job_info->job_id = job_id;
|
||||
compaction_job_info->base_input_level = c->start_level();
|
||||
compaction_job_info->output_level = c->output_level();
|
||||
compaction_job_info->stats = compaction_job_stats;
|
||||
compaction_job_info->table_properties = c->GetOutputTableProperties();
|
||||
compaction_job_info->compaction_reason = c->compaction_reason();
|
||||
compaction_job_info->compression = c->output_compression();
|
||||
for (size_t i = 0; i < c->num_input_levels(); ++i) {
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
auto fn = TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
fmd->fd.GetNumber(), fmd->fd.GetPathId());
|
||||
compaction_job_info->input_files.push_back(fn);
|
||||
if (compaction_job_info->table_properties.count(fn) == 0) {
|
||||
shared_ptr<const TableProperties> tp;
|
||||
auto s = current->GetTableProperties(&tp, fmd, &fn);
|
||||
if (s.ok()) {
|
||||
compaction_job_info->table_properties[fn] = tp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& newf : c->edit()->GetNewFiles()) {
|
||||
compaction_job_info->output_files.push_back(
|
||||
TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
newf.second.fd.GetNumber(), newf.second.fd.GetPathId()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// SuperVersionContext gets created and destructed outside of the lock --
|
||||
// we use this conveniently to:
|
||||
// * malloc one SuperVersion() outside of the lock -- new_superversion
|
||||
@@ -2690,10 +2844,7 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
|
||||
void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
FlushReason /* flush_reason */) {
|
||||
// TODO(yanqin) investigate if 'flush_reason' can be removed since it's not
|
||||
// used.
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// Update max_total_in_memory_state_
|
||||
@@ -2760,4 +2911,31 @@ void DBImpl::SetSnapshotChecker(SnapshotChecker* snapshot_checker) {
|
||||
assert(!snapshot_checker_);
|
||||
snapshot_checker_.reset(snapshot_checker);
|
||||
}
|
||||
|
||||
void DBImpl::GetSnapshotContext(
|
||||
JobContext* job_context, std::vector<SequenceNumber>* snapshot_seqs,
|
||||
SequenceNumber* earliest_write_conflict_snapshot,
|
||||
SnapshotChecker** snapshot_checker_ptr) {
|
||||
mutex_.AssertHeld();
|
||||
assert(job_context != nullptr);
|
||||
assert(snapshot_seqs != nullptr);
|
||||
assert(earliest_write_conflict_snapshot != nullptr);
|
||||
assert(snapshot_checker_ptr != nullptr);
|
||||
|
||||
*snapshot_checker_ptr = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && *snapshot_checker_ptr == nullptr) {
|
||||
*snapshot_checker_ptr = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
if (*snapshot_checker_ptr != nullptr) {
|
||||
// If snapshot_checker is used, that means the flush/compaction may
|
||||
// contain values not visible to snapshot taken after
|
||||
// flush/compaction job starts. Take a snapshot and it will appear
|
||||
// in snapshot_seqs and force compaction iterator to consider such
|
||||
// snapshots.
|
||||
const Snapshot* job_snapshot =
|
||||
GetSnapshotImpl(false /*write_conflict_boundary*/, false /*lock*/);
|
||||
job_context->job_snapshot.reset(new ManagedSnapshot(this, job_snapshot));
|
||||
}
|
||||
*snapshot_seqs = snapshots_.GetAll(earliest_write_conflict_snapshot);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
+15
-1
@@ -243,10 +243,24 @@ size_t DBImpl::TEST_GetWalPreallocateBlockSize(
|
||||
return GetWalPreallocateBlockSize(write_buffer_size);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForTimedTaskRun(std::function<void()> callback) const {
|
||||
void DBImpl::TEST_WaitForDumpStatsRun(std::function<void()> callback) const {
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
thread_dump_stats_->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForPersistStatsRun(std::function<void()> callback) const {
|
||||
if (thread_persist_stats_ != nullptr) {
|
||||
thread_persist_stats_->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_IsPersistentStatsEnabled() const {
|
||||
return thread_persist_stats_ && thread_persist_stats_->IsRunning();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_EstiamteStatsHistorySize() const {
|
||||
return EstiamteStatsHistorySize();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
+61
-33
@@ -23,8 +23,7 @@
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
Options SanitizeOptions(const std::string& dbname,
|
||||
const Options& src) {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src) {
|
||||
auto db_options = SanitizeOptions(dbname, DBOptions(src));
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
auto cf_options =
|
||||
@@ -42,6 +41,8 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
max_max_open_files = 0x400000;
|
||||
}
|
||||
ClipToRange(&result.max_open_files, 20, max_max_open_files);
|
||||
TEST_SYNC_POINT_CALLBACK("SanitizeOptions::AfterChangeMaxOpenFiles",
|
||||
&result.max_open_files);
|
||||
}
|
||||
|
||||
if (result.info_log == nullptr) {
|
||||
@@ -56,10 +57,9 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
result.write_buffer_manager.reset(
|
||||
new WriteBufferManager(result.db_write_buffer_size));
|
||||
}
|
||||
auto bg_job_limits = DBImpl::GetBGJobLimits(result.max_background_flushes,
|
||||
result.max_background_compactions,
|
||||
result.max_background_jobs,
|
||||
true /* parallelize_compactions */);
|
||||
auto bg_job_limits = DBImpl::GetBGJobLimits(
|
||||
result.max_background_flushes, result.max_background_compactions,
|
||||
result.max_background_jobs, true /* parallelize_compactions */);
|
||||
result.env->IncBackgroundThreadsIfNeeded(bg_job_limits.max_compactions,
|
||||
Env::Priority::LOW);
|
||||
result.env->IncBackgroundThreadsIfNeeded(bg_job_limits.max_flushes,
|
||||
@@ -107,14 +107,12 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
result.db_paths.emplace_back(dbname, std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
if (result.use_direct_reads &&
|
||||
result.compaction_readahead_size == 0) {
|
||||
if (result.use_direct_reads && result.compaction_readahead_size == 0) {
|
||||
TEST_SYNC_POINT_CALLBACK("SanitizeOptions:direct_io", nullptr);
|
||||
result.compaction_readahead_size = 1024 * 1024 * 2;
|
||||
}
|
||||
|
||||
if (result.compaction_readahead_size > 0 ||
|
||||
result.use_direct_reads) {
|
||||
if (result.compaction_readahead_size > 0 || result.use_direct_reads) {
|
||||
result.new_table_reader_for_compaction_inputs = true;
|
||||
}
|
||||
|
||||
@@ -178,7 +176,7 @@ static Status ValidateOptions(
|
||||
return s;
|
||||
}
|
||||
|
||||
if (cfd.options.ttl > 0 || cfd.options.compaction_options_fifo.ttl > 0) {
|
||||
if (cfd.options.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported when files are always "
|
||||
@@ -218,7 +216,7 @@ static Status ValidateOptions(
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
Status DBImpl::NewDB() {
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -239,7 +237,7 @@ Status DBImpl::NewDB() {
|
||||
file->SetPreallocationBlockSize(
|
||||
immutable_db_options_.manifest_preallocation_size);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, env_options, nullptr /* stats */,
|
||||
std::move(file), manifest, env_options, env_, nullptr /* stats */,
|
||||
immutable_db_options_.listeners));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
@@ -258,9 +256,8 @@ Status DBImpl::NewDB() {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::CreateAndNewDirectory(
|
||||
Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) {
|
||||
Status DBImpl::CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) {
|
||||
// We call CreateDirIfMissing() as the directory may already exist (if we
|
||||
// are reopening a DB), when this happens we don't want creating the
|
||||
// directory to cause an error. However, we need to check if creating the
|
||||
@@ -341,8 +338,8 @@ Status DBImpl::Recover(
|
||||
}
|
||||
} else if (s.ok()) {
|
||||
if (immutable_db_options_.error_if_exists) {
|
||||
return Status::InvalidArgument(
|
||||
dbname_, "exists (error_if_exists is true)");
|
||||
return Status::InvalidArgument(dbname_,
|
||||
"exists (error_if_exists is true)");
|
||||
}
|
||||
} else {
|
||||
// Unexpected error reading file
|
||||
@@ -424,7 +421,10 @@ Status DBImpl::Recover(
|
||||
// produced by an older version of rocksdb.
|
||||
std::vector<std::string> filenames;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
if (!s.ok()) {
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("wal_dir not found",
|
||||
immutable_db_options_.wal_dir);
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -479,6 +479,28 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
|
||||
if (read_only) {
|
||||
// If we are opening as read-only, we need to update options_file_number_
|
||||
// to reflect the most recent OPTIONS file. It does not matter for regular
|
||||
// read-write db instance because options_file_number_ will later be
|
||||
// updated to versions_->NewFileNumber() in RenameTempFileToOptionsFile.
|
||||
std::vector<std::string> file_names;
|
||||
if (s.ok()) {
|
||||
s = env_->GetChildren(GetName(), &file_names);
|
||||
}
|
||||
if (s.ok()) {
|
||||
uint64_t number = 0;
|
||||
uint64_t options_file_number = 0;
|
||||
FileType type;
|
||||
for (const auto& fname : file_names) {
|
||||
if (ParseFileName(fname, &number, &type) && type == kOptionsFile) {
|
||||
options_file_number = std::max(number, options_file_number);
|
||||
}
|
||||
}
|
||||
versions_->options_file_number_ = options_file_number;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -490,7 +512,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Logger* info_log;
|
||||
const char* fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(this->status == nullptr ? "(ignoring error) " : ""),
|
||||
fname, static_cast<int>(bytes), s.ToString().c_str());
|
||||
@@ -527,10 +549,9 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
std::map<std::string, uint32_t> cf_name_id_map;
|
||||
std::map<uint32_t, uint64_t> cf_lognumber_map;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cf_name_id_map.insert(
|
||||
std::make_pair(cfd->GetName(), cfd->GetID()));
|
||||
cf_name_id_map.insert(std::make_pair(cfd->GetName(), cfd->GetID()));
|
||||
cf_lognumber_map.insert(
|
||||
std::make_pair(cfd->GetID(), cfd->GetLogNumber()));
|
||||
std::make_pair(cfd->GetID(), cfd->GetLogNumber()));
|
||||
}
|
||||
|
||||
immutable_db_options_.wal_filter->ColumnFamilyLogNumberMap(cf_lognumber_map,
|
||||
@@ -880,8 +901,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// VersionSet::next_file_number_ always to be strictly greater than any
|
||||
// log number
|
||||
versions_->MarkFileNumberUsed(max_log_number + 1);
|
||||
status = versions_->LogAndApply(
|
||||
cfd, *cfd->GetLatestMutableCFOptions(), edit, &mutex_);
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
edit, &mutex_);
|
||||
if (!status.ok()) {
|
||||
// Recovery failed
|
||||
break;
|
||||
@@ -994,12 +1015,17 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
auto range_del_iter =
|
||||
mem->NewRangeTombstoneIterator(ro, kMaxSequenceNumber);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
s = BuildTable(
|
||||
dbname_, env_, *cfd->ioptions(), mutable_cf_options,
|
||||
env_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::unique_ptr<InternalIterator>(
|
||||
mem->NewRangeTombstoneIterator(ro, versions_->LastSequence())),
|
||||
&meta, cfd->internal_comparator(),
|
||||
std::move(range_del_iters), &meta, cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
@@ -1033,8 +1059,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
cfd->internal_stats()->AddCompactionStats(level, stats);
|
||||
cfd->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED, meta.fd.GetFileSize());
|
||||
cfd->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta.fd.GetFileSize());
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
return s;
|
||||
}
|
||||
@@ -1150,7 +1176,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
const auto& listeners = impl->immutable_db_options_.listeners;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), log_fname, opt_env_options,
|
||||
nullptr /* stats */, listeners));
|
||||
impl->env_, nullptr /* stats */, listeners));
|
||||
impl->logs_.emplace_back(
|
||||
new_log_number,
|
||||
new log::Writer(
|
||||
@@ -1227,7 +1253,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
!cfd->mem()->IsMergeOperatorSupported()) {
|
||||
s = Status::InvalidArgument(
|
||||
"The memtable of column family %s does not support merge operator "
|
||||
"its options.merge_operator is non-null", cfd->GetName().c_str());
|
||||
"its options.merge_operator is non-null",
|
||||
cfd->GetName().c_str());
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
@@ -1289,7 +1316,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(impl->immutable_db_options_.info_log, "DB pointer %p", impl);
|
||||
ROCKS_LOG_HEADER(impl->immutable_db_options_.info_log, "DB pointer %p",
|
||||
impl);
|
||||
LogFlush(impl->immutable_db_options_.info_log);
|
||||
assert(impl->TEST_WALBufferIsEmpty());
|
||||
// If the assert above fails then we need to FlushWAL before returning
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -77,8 +77,8 @@ class DBImplReadOnly : public DBImpl {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1,
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr
|
||||
) override {
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr,
|
||||
CompactionJobInfo* /*compaction_job_info*/ = nullptr) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
@@ -89,10 +89,11 @@ class DBImplReadOnly : public DBImpl {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
}
|
||||
|
||||
using DBImpl::Flush;
|
||||
|
||||
+10
-10
@@ -475,7 +475,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
if (w.ShouldWriteToWAL()) {
|
||||
if (w.status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF, 1);
|
||||
@@ -504,7 +504,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
WriteThread::WriteGroup memtable_write_group;
|
||||
if (w.state == WriteThread::STATE_MEMTABLE_WRITER_LEADER) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
assert(w.status.ok());
|
||||
assert(w.ShouldWriteToMemtable());
|
||||
write_thread_.EnterAsMemTableWriter(&w, &memtable_write_group);
|
||||
if (memtable_write_group.size > 1 &&
|
||||
immutable_db_options_.allow_concurrent_memtable_write) {
|
||||
@@ -1048,22 +1048,22 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
auto oldest_alive_log = alive_log_files_.begin()->number;
|
||||
bool flush_wont_release_oldest_log = false;
|
||||
if (allow_2pc()) {
|
||||
auto oldest_log_with_uncommited_prep =
|
||||
auto oldest_log_with_uncommitted_prep =
|
||||
logs_with_prep_tracker_.FindMinLogContainingOutstandingPrep();
|
||||
|
||||
assert(oldest_log_with_uncommited_prep == 0 ||
|
||||
oldest_log_with_uncommited_prep >= oldest_alive_log);
|
||||
if (oldest_log_with_uncommited_prep > 0 &&
|
||||
oldest_log_with_uncommited_prep == oldest_alive_log) {
|
||||
assert(oldest_log_with_uncommitted_prep == 0 ||
|
||||
oldest_log_with_uncommitted_prep >= oldest_alive_log);
|
||||
if (oldest_log_with_uncommitted_prep > 0 &&
|
||||
oldest_log_with_uncommitted_prep == oldest_alive_log) {
|
||||
if (unable_to_release_oldest_log_) {
|
||||
// we already attempted to flush all column families dependent on
|
||||
// the oldest alive log but the log still contained uncommited
|
||||
// the oldest alive log but the log still contained uncommitted
|
||||
// transactions so there is still nothing that we can do.
|
||||
return status;
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to release oldest log due to uncommited transaction");
|
||||
"Unable to release oldest log due to uncommitted transaction");
|
||||
unable_to_release_oldest_log_ = true;
|
||||
flush_wont_release_oldest_log = true;
|
||||
}
|
||||
@@ -1456,7 +1456,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
lfile->SetWriteLifeTimeHint(write_hint);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_env_opt, nullptr /* stats */,
|
||||
std::move(lfile), log_fname, opt_env_opt, env_, nullptr /* stats */,
|
||||
immutable_db_options_.listeners));
|
||||
new_log = new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
|
||||
+15
-17
@@ -152,7 +152,7 @@ class DBIter final: public Iterator {
|
||||
iter_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
}
|
||||
virtual ~DBIter() {
|
||||
~DBIter() override {
|
||||
// Release pinned data if any
|
||||
if (pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
@@ -171,21 +171,20 @@ class DBIter final: public Iterator {
|
||||
iter_ = iter;
|
||||
iter_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
virtual RangeDelAggregatorV2* GetRangeDelAggregator() {
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator() {
|
||||
return &range_del_agg_;
|
||||
}
|
||||
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual Slice key() const override {
|
||||
bool Valid() const override { return valid_; }
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
if(start_seqnum_ > 0) {
|
||||
return saved_key_.GetInternalKey();
|
||||
} else {
|
||||
return saved_key_.GetUserKey();
|
||||
}
|
||||
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
Slice value() const override {
|
||||
assert(valid_);
|
||||
if (current_entry_is_merged_) {
|
||||
// If pinned_value_ is set then the result of merge operator is one of
|
||||
@@ -197,7 +196,7 @@ class DBIter final: public Iterator {
|
||||
return iter_->value();
|
||||
}
|
||||
}
|
||||
virtual Status status() const override {
|
||||
Status status() const override {
|
||||
if (status_.ok()) {
|
||||
return iter_->status();
|
||||
} else {
|
||||
@@ -210,8 +209,7 @@ class DBIter final: public Iterator {
|
||||
return is_blob_;
|
||||
}
|
||||
|
||||
virtual Status GetProperty(std::string prop_name,
|
||||
std::string* prop) override {
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override {
|
||||
if (prop == nullptr) {
|
||||
return Status::InvalidArgument("prop is nullptr");
|
||||
}
|
||||
@@ -232,12 +230,12 @@ class DBIter final: public Iterator {
|
||||
return Status::InvalidArgument("Unidentified property.");
|
||||
}
|
||||
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
void Next() override;
|
||||
void Prev() override;
|
||||
void Seek(const Slice& target) override;
|
||||
void SeekForPrev(const Slice& target) override;
|
||||
void SeekToFirst() override;
|
||||
void SeekToLast() override;
|
||||
Env* env() { return env_; }
|
||||
void set_sequence(uint64_t s) { sequence_ = s; }
|
||||
void set_valid(bool v) { valid_ = v; }
|
||||
@@ -341,7 +339,7 @@ class DBIter final: public Iterator {
|
||||
const bool total_order_seek_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
RangeDelAggregatorV2 range_del_agg_;
|
||||
ReadRangeDelAggregator range_del_agg_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
ReadCallback* read_callback_;
|
||||
@@ -1479,7 +1477,7 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
|
||||
ArenaWrappedDBIter::~ArenaWrappedDBIter() { db_iter_->~DBIter(); }
|
||||
|
||||
RangeDelAggregatorV2* ArenaWrappedDBIter::GetRangeDelAggregator() {
|
||||
ReadRangeDelAggregator* ArenaWrappedDBIter::GetRangeDelAggregator() {
|
||||
return db_iter_->GetRangeDelAggregator();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator_v2.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -48,7 +48,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// Get the arena to be used to allocate memory for DBIter to be wrapped,
|
||||
// as well as child iterators in it.
|
||||
virtual Arena* GetArena() { return &arena_; }
|
||||
virtual RangeDelAggregatorV2* GetRangeDelAggregator();
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator();
|
||||
|
||||
// Set the internal iterator wrapped inside the DB Iterator. Usually it is
|
||||
// a merging iterator.
|
||||
|
||||
+12
-12
@@ -116,12 +116,12 @@ class TestIterator : public InternalIterator {
|
||||
// Number of operations done on this iterator since construction.
|
||||
size_t steps() const { return steps_; }
|
||||
|
||||
virtual bool Valid() const override {
|
||||
bool Valid() const override {
|
||||
assert(initialized_);
|
||||
return valid_;
|
||||
}
|
||||
|
||||
virtual void SeekToFirst() override {
|
||||
void SeekToFirst() override {
|
||||
assert(initialized_);
|
||||
++steps_;
|
||||
DeleteCurrentIfNeeded();
|
||||
@@ -129,7 +129,7 @@ class TestIterator : public InternalIterator {
|
||||
iter_ = 0;
|
||||
}
|
||||
|
||||
virtual void SeekToLast() override {
|
||||
void SeekToLast() override {
|
||||
assert(initialized_);
|
||||
++steps_;
|
||||
DeleteCurrentIfNeeded();
|
||||
@@ -137,7 +137,7 @@ class TestIterator : public InternalIterator {
|
||||
iter_ = data_.size() - 1;
|
||||
}
|
||||
|
||||
virtual void Seek(const Slice& target) override {
|
||||
void Seek(const Slice& target) override {
|
||||
assert(initialized_);
|
||||
SeekToFirst();
|
||||
++steps_;
|
||||
@@ -154,13 +154,13 @@ class TestIterator : public InternalIterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& target) override {
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
assert(initialized_);
|
||||
DeleteCurrentIfNeeded();
|
||||
SeekForPrevImpl(target, &cmp);
|
||||
}
|
||||
|
||||
virtual void Next() override {
|
||||
void Next() override {
|
||||
assert(initialized_);
|
||||
assert(valid_);
|
||||
assert(iter_ < data_.size());
|
||||
@@ -174,7 +174,7 @@ class TestIterator : public InternalIterator {
|
||||
valid_ = iter_ < data_.size();
|
||||
}
|
||||
|
||||
virtual void Prev() override {
|
||||
void Prev() override {
|
||||
assert(initialized_);
|
||||
assert(valid_);
|
||||
assert(iter_ < data_.size());
|
||||
@@ -188,23 +188,23 @@ class TestIterator : public InternalIterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual Slice key() const override {
|
||||
Slice key() const override {
|
||||
assert(initialized_);
|
||||
return data_[iter_].first;
|
||||
}
|
||||
|
||||
virtual Slice value() const override {
|
||||
Slice value() const override {
|
||||
assert(initialized_);
|
||||
return data_[iter_].second;
|
||||
}
|
||||
|
||||
virtual Status status() const override {
|
||||
Status status() const override {
|
||||
assert(initialized_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual bool IsKeyPinned() const override { return true; }
|
||||
virtual bool IsValuePinned() const override { return true; }
|
||||
bool IsKeyPinned() const override { return true; }
|
||||
bool IsValuePinned() const override { return true; }
|
||||
|
||||
private:
|
||||
bool initialized_;
|
||||
|
||||
@@ -50,13 +50,14 @@ class DBIteratorTest : public DBTestBase,
|
||||
|
||||
class FlushBlockEveryKeyPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
virtual bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (!start_) {
|
||||
start_ = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool start_ = false;
|
||||
};
|
||||
@@ -179,9 +180,7 @@ TEST_P(DBIteratorTest, NonBlockingIteration) {
|
||||
|
||||
// This test verifies block cache behaviors, which is not used by plain
|
||||
// table format.
|
||||
// Exclude kHashCuckoo as it does not support iteration currently
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipNoSeekToLast | kSkipHashCuckoo |
|
||||
kSkipMmapReads));
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipNoSeekToLast | kSkipMmapReads));
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IterSeekBeforePrev) {
|
||||
@@ -765,8 +764,7 @@ TEST_P(DBIteratorTest, IterWithSnapshot) {
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
delete iter;
|
||||
// skip as HashCuckooRep does not support snapshot
|
||||
} while (ChangeOptions(kSkipHashCuckoo));
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IteratorPinsRef) {
|
||||
|
||||
@@ -249,22 +249,20 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorBlobs) {
|
||||
auto res = OpenTransactionLogIter(0)->GetBatch();
|
||||
struct Handler : public WriteBatch::Handler {
|
||||
std::string seen;
|
||||
virtual Status PutCF(uint32_t cf, const Slice& key,
|
||||
const Slice& value) override {
|
||||
Status PutCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
||||
seen += "Put(" + ToString(cf) + ", " + key.ToString() + ", " +
|
||||
ToString(value.size()) + ")";
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status MergeCF(uint32_t cf, const Slice& key,
|
||||
const Slice& value) override {
|
||||
Status MergeCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
||||
seen += "Merge(" + ToString(cf) + ", " + key.ToString() + ", " +
|
||||
ToString(value.size()) + ")";
|
||||
return Status::OK();
|
||||
}
|
||||
virtual void LogData(const Slice& blob) override {
|
||||
void LogData(const Slice& blob) override {
|
||||
seen += "LogData(" + blob.ToString() + ")";
|
||||
}
|
||||
virtual Status DeleteCF(uint32_t cf, const Slice& key) override {
|
||||
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
||||
seen += "Delete(" + ToString(cf) + ", " + key.ToString() + ")";
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
+25
-26
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
@@ -24,13 +25,13 @@ class MockMemTableRep : public MemTableRep {
|
||||
explicit MockMemTableRep(Allocator* allocator, MemTableRep* rep)
|
||||
: MemTableRep(allocator), rep_(rep), num_insert_with_hint_(0) {}
|
||||
|
||||
virtual KeyHandle Allocate(const size_t len, char** buf) override {
|
||||
KeyHandle Allocate(const size_t len, char** buf) override {
|
||||
return rep_->Allocate(len, buf);
|
||||
}
|
||||
|
||||
virtual void Insert(KeyHandle handle) override { rep_->Insert(handle); }
|
||||
void Insert(KeyHandle handle) override { rep_->Insert(handle); }
|
||||
|
||||
virtual void InsertWithHint(KeyHandle handle, void** hint) override {
|
||||
void InsertWithHint(KeyHandle handle, void** hint) override {
|
||||
num_insert_with_hint_++;
|
||||
EXPECT_NE(nullptr, hint);
|
||||
last_hint_in_ = *hint;
|
||||
@@ -38,21 +39,18 @@ class MockMemTableRep : public MemTableRep {
|
||||
last_hint_out_ = *hint;
|
||||
}
|
||||
|
||||
virtual bool Contains(const char* key) const override {
|
||||
return rep_->Contains(key);
|
||||
}
|
||||
bool Contains(const char* key) const override { return rep_->Contains(key); }
|
||||
|
||||
virtual void Get(const LookupKey& k, void* callback_args,
|
||||
bool (*callback_func)(void* arg,
|
||||
const char* entry)) override {
|
||||
void Get(const LookupKey& k, void* callback_args,
|
||||
bool (*callback_func)(void* arg, const char* entry)) override {
|
||||
rep_->Get(k, callback_args, callback_func);
|
||||
}
|
||||
|
||||
virtual size_t ApproximateMemoryUsage() override {
|
||||
size_t ApproximateMemoryUsage() override {
|
||||
return rep_->ApproximateMemoryUsage();
|
||||
}
|
||||
|
||||
virtual Iterator* GetIterator(Arena* arena) override {
|
||||
Iterator* GetIterator(Arena* arena) override {
|
||||
return rep_->GetIterator(arena);
|
||||
}
|
||||
|
||||
@@ -69,10 +67,10 @@ class MockMemTableRep : public MemTableRep {
|
||||
|
||||
class MockMemTableRepFactory : public MemTableRepFactory {
|
||||
public:
|
||||
virtual MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
|
||||
Allocator* allocator,
|
||||
const SliceTransform* transform,
|
||||
Logger* logger) override {
|
||||
MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
|
||||
Allocator* allocator,
|
||||
const SliceTransform* transform,
|
||||
Logger* logger) override {
|
||||
SkipListFactory factory;
|
||||
MemTableRep* skiplist_rep =
|
||||
factory.CreateMemTableRep(cmp, allocator, transform, logger);
|
||||
@@ -80,16 +78,16 @@ class MockMemTableRepFactory : public MemTableRepFactory {
|
||||
return mock_rep_;
|
||||
}
|
||||
|
||||
virtual MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
|
||||
Allocator* allocator,
|
||||
const SliceTransform* transform,
|
||||
Logger* logger,
|
||||
uint32_t column_family_id) override {
|
||||
MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
|
||||
Allocator* allocator,
|
||||
const SliceTransform* transform,
|
||||
Logger* logger,
|
||||
uint32_t column_family_id) override {
|
||||
last_column_family_id_ = column_family_id;
|
||||
return CreateMemTableRep(cmp, allocator, transform, logger);
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "MockMemTableRepFactory"; }
|
||||
const char* Name() const override { return "MockMemTableRepFactory"; }
|
||||
|
||||
MockMemTableRep* rep() { return mock_rep_; }
|
||||
|
||||
@@ -105,9 +103,9 @@ class MockMemTableRepFactory : public MemTableRepFactory {
|
||||
|
||||
class TestPrefixExtractor : public SliceTransform {
|
||||
public:
|
||||
virtual const char* Name() const override { return "TestPrefixExtractor"; }
|
||||
const char* Name() const override { return "TestPrefixExtractor"; }
|
||||
|
||||
virtual Slice Transform(const Slice& key) const override {
|
||||
Slice Transform(const Slice& key) const override {
|
||||
const char* p = separator(key);
|
||||
if (p == nullptr) {
|
||||
return Slice();
|
||||
@@ -115,11 +113,11 @@ class TestPrefixExtractor : public SliceTransform {
|
||||
return Slice(key.data(), p - key.data() + 1);
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& key) const override {
|
||||
bool InDomain(const Slice& key) const override {
|
||||
return separator(key) != nullptr;
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& /*key*/) const override { return false; }
|
||||
bool InRange(const Slice& /*key*/) const override { return false; }
|
||||
|
||||
private:
|
||||
const char* separator(const Slice& key) const {
|
||||
@@ -135,7 +133,8 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
MergeContext merge_context;
|
||||
Options options;
|
||||
InternalKeyComparator ikey_cmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(ikey_cmp, {} /* snapshots */);
|
||||
ReadRangeDelAggregator range_del_agg(&ikey_cmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
|
||||
// Create a MemTable
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
|
||||
@@ -21,7 +21,8 @@ class TestReadCallback : public ReadCallback {
|
||||
: snapshot_checker_(snapshot_checker), snapshot_seq_(snapshot_seq) {}
|
||||
|
||||
bool IsVisible(SequenceNumber seq) override {
|
||||
return snapshot_checker_->IsInSnapshot(seq, snapshot_seq_);
|
||||
return snapshot_checker_->CheckInSnapshot(seq, snapshot_seq_) ==
|
||||
SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -331,8 +332,7 @@ TEST_P(MergeOperatorPinningTest, Randomized) {
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
// Skip HashCuckoo since it does not support merge operators
|
||||
} while (ChangeOptions(kSkipMergePut | kSkipHashCuckoo));
|
||||
} while (ChangeOptions(kSkipMergePut));
|
||||
}
|
||||
|
||||
class MergeOperatorHook : public MergeOperator {
|
||||
@@ -340,15 +340,15 @@ class MergeOperatorHook : public MergeOperator {
|
||||
explicit MergeOperatorHook(std::shared_ptr<MergeOperator> _merge_op)
|
||||
: merge_op_(_merge_op) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
before_merge_();
|
||||
bool res = merge_op_->FullMergeV2(merge_in, merge_out);
|
||||
after_merge_();
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return merge_op_->Name(); }
|
||||
const char* Name() const override { return merge_op_->Name(); }
|
||||
|
||||
std::shared_ptr<MergeOperator> merge_op_;
|
||||
std::function<void()> before_merge_ = []() {};
|
||||
@@ -547,8 +547,15 @@ TEST_F(DBMergeOperatorTest, SnapshotCheckerAndReadCallback) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
class TestSnapshotChecker : public SnapshotChecker {
|
||||
bool IsInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot_seq) const override {
|
||||
public:
|
||||
SnapshotCheckerResult CheckInSnapshot(
|
||||
SequenceNumber seq, SequenceNumber snapshot_seq) const override {
|
||||
return IsInSnapshot(seq, snapshot_seq)
|
||||
? SnapshotCheckerResult::kInSnapshot
|
||||
: SnapshotCheckerResult::kNotInSnapshot;
|
||||
}
|
||||
|
||||
bool IsInSnapshot(SequenceNumber seq, SequenceNumber snapshot_seq) const {
|
||||
switch (snapshot_seq) {
|
||||
case 0:
|
||||
return seq == 0;
|
||||
|
||||
+208
-11
@@ -18,12 +18,15 @@
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/stats_history.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const int kMicrosInSec = 1000000;
|
||||
|
||||
class DBOptionsTest : public DBTestBase {
|
||||
public:
|
||||
DBOptionsTest() : DBTestBase("/db_options_test") {}
|
||||
@@ -508,10 +511,11 @@ TEST_F(DBOptionsTest, SetStatsDumpPeriodSec) {
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int num = rand() % 5000 + 1;
|
||||
ASSERT_OK(dbfull()->SetDBOptions(
|
||||
{{"stats_dump_period_sec", std::to_string(num)}}));
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"stats_dump_period_sec", ToString(num)}}));
|
||||
ASSERT_EQ(num, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, RunStatsDumpPeriodSec) {
|
||||
@@ -530,17 +534,210 @@ TEST_F(DBOptionsTest, RunStatsDumpPeriodSec) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
dbfull()->TEST_WaitForTimedTaskRun([&] { mock_env->set_current_time(5); });
|
||||
dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
|
||||
// Test cacel job through SetOptions
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
|
||||
int old_val = counter;
|
||||
env_->SleepForMicroseconds(10000000);
|
||||
for (int i = 6; i < 20; ++i) {
|
||||
dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env->set_current_time(i); });
|
||||
}
|
||||
ASSERT_EQ(counter, old_val);
|
||||
Close();
|
||||
}
|
||||
|
||||
// Test persistent stats background thread scheduling and cancelling
|
||||
TEST_F(DBOptionsTest, StatsPersistScheduling) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 5;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
int counter = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
|
||||
// Test cacel job through SetOptions
|
||||
ASSERT_TRUE(dbfull()->TEST_IsPersistentStatsEnabled());
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
|
||||
ASSERT_FALSE(dbfull()->TEST_IsPersistentStatsEnabled());
|
||||
Close();
|
||||
}
|
||||
|
||||
// Test enabling persistent stats for the first time
|
||||
TEST_F(DBOptionsTest, PersistentStatsFreshInstall) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 0;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
int counter = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "5"}}));
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsStatsPersistPeriodSec) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 5;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "12345"}}));
|
||||
ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
ASSERT_NOK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "abcde"}}));
|
||||
ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, GetStatsHistory) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 5;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
CreateColumnFamilies({"pikachu"}, options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
int mock_time = 1;
|
||||
// Wait for stats persist to finish
|
||||
dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
|
||||
std::unique_ptr<StatsHistoryIterator> stats_iter;
|
||||
db_->GetStatsHistory(0, 6 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
// disabled stats snapshots
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
|
||||
size_t stats_count = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
auto stats_map = stats_iter->GetStatsMap();
|
||||
stats_count += stats_map.size();
|
||||
}
|
||||
ASSERT_GT(stats_count, 0);
|
||||
// Wait a bit and verify no more stats are found
|
||||
for (mock_time = 6; mock_time < 20; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
db_->GetStatsHistory(0, 20 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
size_t stats_count_new = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
stats_count_new += stats_iter->GetStatsMap().size();
|
||||
}
|
||||
ASSERT_EQ(stats_count_new, stats_count);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, InMemoryStatsHistoryPurging) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.stats_persist_period_sec = 1;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
CreateColumnFamilies({"pikachu"}, options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// some random operation to populate statistics
|
||||
ASSERT_OK(Delete("foo"));
|
||||
ASSERT_OK(Put("sol", "sol"));
|
||||
ASSERT_OK(Put("epic", "epic"));
|
||||
ASSERT_OK(Put("ltd", "ltd"));
|
||||
ASSERT_EQ("sol", Get("sol"));
|
||||
ASSERT_EQ("epic", Get("epic"));
|
||||
ASSERT_EQ("ltd", Get("ltd"));
|
||||
Iterator* iterator = db_->NewIterator(ReadOptions());
|
||||
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
|
||||
ASSERT_TRUE(iterator->key() == iterator->value());
|
||||
}
|
||||
delete iterator;
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Delete("sol"));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
int mock_time = 1;
|
||||
// Wait for stats persist to finish
|
||||
for (; mock_time < 5; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
|
||||
// second round of ops
|
||||
ASSERT_OK(Put("saigon", "saigon"));
|
||||
ASSERT_OK(Put("noodle talk", "noodle talk"));
|
||||
ASSERT_OK(Put("ping bistro", "ping bistro"));
|
||||
iterator = db_->NewIterator(ReadOptions());
|
||||
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
|
||||
ASSERT_TRUE(iterator->key() == iterator->value());
|
||||
}
|
||||
delete iterator;
|
||||
ASSERT_OK(Flush());
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
for (; mock_time < 10; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
std::unique_ptr<StatsHistoryIterator> stats_iter;
|
||||
db_->GetStatsHistory(0, 10 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
size_t stats_count = 0;
|
||||
int slice_count = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
slice_count++;
|
||||
auto stats_map = stats_iter->GetStatsMap();
|
||||
stats_count += stats_map.size();
|
||||
}
|
||||
size_t stats_history_size = dbfull()->TEST_EstiamteStatsHistorySize();
|
||||
ASSERT_GE(slice_count, 9);
|
||||
ASSERT_GE(stats_history_size, 12000);
|
||||
// capping memory cost at 12000 bytes since one slice is around 10000~12000
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "12000"}}));
|
||||
ASSERT_EQ(12000, dbfull()->GetDBOptions().stats_history_buffer_size);
|
||||
// Wait for stats persist to finish
|
||||
for (; mock_time < 20; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
db_->GetStatsHistory(0, 20 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
size_t stats_count_reopen = 0;
|
||||
slice_count = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
slice_count++;
|
||||
auto stats_map = stats_iter->GetStatsMap();
|
||||
stats_count_reopen += stats_map.size();
|
||||
}
|
||||
size_t stats_history_size_reopen = dbfull()->TEST_EstiamteStatsHistorySize();
|
||||
// only one slice can fit under the new stats_history_buffer_size
|
||||
ASSERT_LT(slice_count, 2);
|
||||
ASSERT_TRUE(stats_history_size_reopen < 12000 &&
|
||||
stats_history_size_reopen > 0);
|
||||
ASSERT_TRUE(stats_count_reopen < stats_count && stats_count_reopen > 0);
|
||||
Close();
|
||||
}
|
||||
|
||||
static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
dbfull->TEST_LockMutex();
|
||||
JobContext job_context(0);
|
||||
@@ -628,9 +825,9 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
// Test dynamically changing compaction_options_fifo.ttl
|
||||
// Test dynamically changing ttl.
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
Random rnd(301);
|
||||
@@ -648,13 +845,13 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
env_->addon_time_.fetch_add(61);
|
||||
|
||||
// No files should be compacted as ttl is set to 1 hour.
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 3600);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 3600);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Set ttl to 1 minute. So all files should get deleted.
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo", "{ttl=60;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 60);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "60"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 60);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
@@ -662,7 +859,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
// Test dynamically changing compaction_options_fifo.max_table_files_size
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.max_table_files_size = 500 << 10; // 00KB
|
||||
options.compaction_options_fifo.ttl = 0;
|
||||
options.ttl = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
@@ -692,7 +889,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
|
||||
// Test dynamically changing compaction_options_fifo.allow_compaction
|
||||
options.compaction_options_fifo.max_table_files_size = 500 << 10; // 500KB
|
||||
options.compaction_options_fifo.ttl = 0;
|
||||
options.ttl = 0;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -245,15 +245,14 @@ void GetExpectedTableProperties(
|
||||
const int kDeletionCount = kTableCount * kDeletionsPerTable;
|
||||
const int kMergeCount = kTableCount * kMergeOperandsPerTable;
|
||||
const int kRangeDeletionCount = kTableCount * kRangeDeletionsPerTable;
|
||||
const int kKeyCount = kPutCount + kDeletionCount + kMergeCount;
|
||||
const int kKeyCount = kPutCount + kDeletionCount + kMergeCount + kRangeDeletionCount;
|
||||
const int kAvgSuccessorSize = kKeySize / 5;
|
||||
const int kEncodingSavePerKey = kKeySize / 4;
|
||||
expected_tp->raw_key_size =
|
||||
(kKeyCount + kRangeDeletionCount) * (kKeySize + 8);
|
||||
expected_tp->raw_key_size = kKeyCount * (kKeySize + 8);
|
||||
expected_tp->raw_value_size =
|
||||
(kPutCount + kMergeCount + kRangeDeletionCount) * kValueSize;
|
||||
expected_tp->num_entries = kKeyCount;
|
||||
expected_tp->num_deletions = kDeletionCount;
|
||||
expected_tp->num_deletions = kDeletionCount + kRangeDeletionCount;
|
||||
expected_tp->num_merge_operands = kMergeCount;
|
||||
expected_tp->num_range_deletions = kRangeDeletionCount;
|
||||
expected_tp->num_data_blocks =
|
||||
@@ -393,7 +392,15 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
options.target_file_size_base = 98 << 10;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.max_open_files = 100;
|
||||
options.max_open_files = 11; // Make sure no proloading of table readers
|
||||
|
||||
// RocksDB sanitize max open files to at least 20. Modify it back.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = static_cast<int*>(arg);
|
||||
*max_open_files = 11;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
@@ -441,6 +448,7 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
|
||||
// Reopen and issue iterating. See thee latency tracked
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
@@ -1086,7 +1094,7 @@ class CountingUserTblPropCollector : public TablePropertiesCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
@@ -1102,7 +1110,7 @@ class CountingUserTblPropCollectorFactory
|
||||
uint32_t expected_column_family_id)
|
||||
: expected_column_family_id_(expected_column_family_id),
|
||||
num_created_(0) {}
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context context) override {
|
||||
EXPECT_EQ(expected_column_family_id_, context.column_family_id);
|
||||
num_created_++;
|
||||
@@ -1150,7 +1158,7 @@ class CountingDeleteTabPropCollector : public TablePropertiesCollector {
|
||||
class CountingDeleteTabPropCollectorFactory
|
||||
: public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
return new CountingDeleteTabPropCollector();
|
||||
}
|
||||
@@ -1424,7 +1432,7 @@ TEST_F(DBPropertiesTest, EstimateOldestKeyTime) {
|
||||
}
|
||||
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.compaction_options_fifo.ttl = 300;
|
||||
options.ttl = 300;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
+22
-9
@@ -72,8 +72,8 @@ TEST_F(DBRangeDelTest, CompactionOutputHasOnlyRangeTombstone) {
|
||||
// Skip cuckoo memtables, which do not support snapshots. Skip non-leveled
|
||||
// compactions as the above assertions about the number of files in a level
|
||||
// do not hold true.
|
||||
} while (ChangeOptions(kRangeDelSkipConfigs | kSkipHashCuckoo |
|
||||
kSkipUniversalCompaction | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kRangeDelSkipConfigs | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, CompactionOutputFilesExactlyFilled) {
|
||||
@@ -645,8 +645,7 @@ TEST_F(DBRangeDelTest, GetCoveredKeyFromSst) {
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(read_opts, "key", &value).IsNotFound());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
// Cuckoo memtables do not support snapshots.
|
||||
} while (ChangeOptions(kRangeDelSkipConfigs | kSkipHashCuckoo));
|
||||
} while (ChangeOptions(kRangeDelSkipConfigs));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredMergeOperandFromMemtable) {
|
||||
@@ -1041,11 +1040,16 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
// L2:
|
||||
// [key000000#1,1, key000000#1,1]
|
||||
// [key000002#6,1, key000004#72057594037927935,15]
|
||||
//
|
||||
// At the same time, verify the compaction does not cause the key at the
|
||||
// endpoint (key000002#6,1) to disappear.
|
||||
ASSERT_EQ(value, Get(Key(2)));
|
||||
auto begin_str = Key(3);
|
||||
const rocksdb::Slice begin = begin_str;
|
||||
dbfull()->TEST_CompactRange(1, &begin, nullptr);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(2));
|
||||
ASSERT_EQ(value, Get(Key(2)));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1107,14 +1111,14 @@ class MockMergeOperator : public MergeOperator {
|
||||
// Mock non-associative operator. Non-associativity is expressed by lack of
|
||||
// implementation for any `PartialMerge*` functions.
|
||||
public:
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
assert(merge_out != nullptr);
|
||||
merge_out->new_value = merge_in.operand_list.back().ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "MockMergeOperator"; }
|
||||
const char* Name() const override { return "MockMergeOperator"; }
|
||||
};
|
||||
|
||||
TEST_F(DBRangeDelTest, KeyAtOverlappingEndpointReappears) {
|
||||
@@ -1133,6 +1137,14 @@ TEST_F(DBRangeDelTest, KeyAtOverlappingEndpointReappears) {
|
||||
options.target_file_size_base = kFileBytes;
|
||||
Reopen(options);
|
||||
|
||||
// Push dummy data to L3 so that our actual test files on L0-L2
|
||||
// will not be considered "bottommost" level, otherwise compaction
|
||||
// may prevent us from creating overlapping user keys
|
||||
// as on the bottommost layer MergeHelper
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", "dummy"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
MoveFilesToLevel(3);
|
||||
|
||||
Random rnd(301);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
@@ -1154,8 +1166,9 @@ TEST_F(DBRangeDelTest, KeyAtOverlappingEndpointReappears) {
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "key", &value).IsNotFound());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr /* begin_key */,
|
||||
nullptr /* end_key */));
|
||||
dbfull()->TEST_CompactRange(0 /* level */, nullptr /* begin */,
|
||||
nullptr /* end */, nullptr /* column_family */,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
// Now we have multiple files at L1 all containing a single user key, thus
|
||||
// guaranteeing overlap in the file endpoints.
|
||||
|
||||
+3
-2
@@ -25,9 +25,9 @@ class DBSSTTest : public DBTestBase {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -385,6 +385,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
|
||||
// Compaction will move the 4 files in L0 to trash and create 1 L1 file
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
|
||||
uint64_t delete_start_time = env_->NowMicros();
|
||||
|
||||
+232
-200
@@ -487,11 +487,11 @@ TEST_F(DBTest, PutSingleDeleteGet) {
|
||||
ASSERT_EQ("v2", Get(1, "foo2"));
|
||||
ASSERT_OK(SingleDelete(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because single delete does not get removed when it encounters a merge.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Skip FIFO and universal compaction beccause they do not apply to the test
|
||||
// case. Skip MergePut because single delete does not get removed when it
|
||||
// encounters a merge.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ReadFromPersistedTier) {
|
||||
@@ -604,7 +604,7 @@ TEST_F(DBTest, ReadFromPersistedTier) {
|
||||
DestroyAndReopen(options);
|
||||
}
|
||||
}
|
||||
} while (ChangeOptions(kSkipHashCuckoo));
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, SingleDeleteFlush) {
|
||||
@@ -640,11 +640,11 @@ TEST_F(DBTest, SingleDeleteFlush) {
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "bar"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because merges cannot be combined with single deletions.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Skip FIFO and universal compaction beccause they do not apply to the test
|
||||
// case. Skip MergePut because single delete does not get removed when it
|
||||
// encounters a merge.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, SingleDeletePutFlush) {
|
||||
@@ -663,11 +663,11 @@ TEST_F(DBTest, SingleDeletePutFlush) {
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
ASSERT_EQ("[ ]", AllEntriesFor("a", 1));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because merges cannot be combined with single deletions.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Skip FIFO and universal compaction beccause they do not apply to the test
|
||||
// case. Skip MergePut because single delete does not get removed when it
|
||||
// encounters a merge.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
// Disable because not all platform can run it.
|
||||
@@ -940,13 +940,13 @@ TEST_F(DBTest, FlushSchedule) {
|
||||
namespace {
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilter"; }
|
||||
const char* Name() const override { return "KeepFilter"; }
|
||||
};
|
||||
|
||||
class KeepFilterFactory : public CompactionFilterFactory {
|
||||
@@ -954,7 +954,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
explicit KeepFilterFactory(bool check_context = false)
|
||||
: check_context_(check_context) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (check_context_) {
|
||||
EXPECT_EQ(expect_full_compaction_.load(), context.is_full_compaction);
|
||||
@@ -963,7 +963,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
return std::unique_ptr<CompactionFilter>(new KeepFilter());
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilterFactory"; }
|
||||
const char* Name() const override { return "KeepFilterFactory"; }
|
||||
bool check_context_;
|
||||
std::atomic_bool expect_full_compaction_;
|
||||
std::atomic_bool expect_manual_compaction_;
|
||||
@@ -972,14 +972,14 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilter"; }
|
||||
const char* Name() const override { return "DelayFilter"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -988,12 +988,12 @@ class DelayFilter : public CompactionFilter {
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilterFactory"; }
|
||||
const char* Name() const override { return "DelayFilterFactory"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -1569,7 +1569,7 @@ TEST_F(DBTest, Snapshot) {
|
||||
ASSERT_EQ(0U, GetNumSnapshots());
|
||||
ASSERT_EQ("0v4", Get(0, "foo"));
|
||||
ASSERT_EQ("1v4", Get(1, "foo"));
|
||||
} while (ChangeOptions(kSkipHashCuckoo));
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, HiddenValuesAreRemoved) {
|
||||
@@ -1606,9 +1606,8 @@ TEST_F(DBTest, HiddenValuesAreRemoved) {
|
||||
ASSERT_TRUE(Between(Size("", "pastfoo", 1), 0, 1000));
|
||||
// ApproximateOffsetOf() is not yet implemented in plain table format,
|
||||
// which is used by Size().
|
||||
// skip HashCuckooRep as it does not support snapshot
|
||||
} while (ChangeOptions(kSkipUniversalCompaction | kSkipFIFOCompaction |
|
||||
kSkipPlainTable | kSkipHashCuckoo));
|
||||
kSkipPlainTable));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -1654,11 +1653,11 @@ TEST_F(DBTest, UnremovableSingleDelete) {
|
||||
ASSERT_EQ("first", Get(1, "foo", snapshot));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because single delete does not get removed when it encounters a merge.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Skip FIFO and universal compaction beccause they do not apply to the test
|
||||
// case. Skip MergePut because single delete does not get removed when it
|
||||
// encounters a merge.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1776,17 +1775,14 @@ TEST_F(DBTest, OverlapInLevel0) {
|
||||
TEST_F(DBTest, ComparatorCheck) {
|
||||
class NewComparator : public Comparator {
|
||||
public:
|
||||
virtual const char* Name() const override {
|
||||
return "rocksdb.NewComparator";
|
||||
}
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
const char* Name() const override { return "rocksdb.NewComparator"; }
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
return BytewiseComparator()->Compare(a, b);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* s,
|
||||
const Slice& l) const override {
|
||||
void FindShortestSeparator(std::string* s, const Slice& l) const override {
|
||||
BytewiseComparator()->FindShortestSeparator(s, l);
|
||||
}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {
|
||||
void FindShortSuccessor(std::string* key) const override {
|
||||
BytewiseComparator()->FindShortSuccessor(key);
|
||||
}
|
||||
};
|
||||
@@ -1809,18 +1805,15 @@ TEST_F(DBTest, ComparatorCheck) {
|
||||
TEST_F(DBTest, CustomComparator) {
|
||||
class NumberComparator : public Comparator {
|
||||
public:
|
||||
virtual const char* Name() const override {
|
||||
return "test.NumberComparator";
|
||||
}
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
const char* Name() const override { return "test.NumberComparator"; }
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
return ToNumber(a) - ToNumber(b);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* s,
|
||||
const Slice& l) const override {
|
||||
void FindShortestSeparator(std::string* s, const Slice& l) const override {
|
||||
ToNumber(*s); // Check format
|
||||
ToNumber(l); // Check format
|
||||
}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {
|
||||
void FindShortSuccessor(std::string* key) const override {
|
||||
ToNumber(*key); // Check format
|
||||
}
|
||||
|
||||
@@ -2254,15 +2247,12 @@ static void MTThreadBody(void* arg) {
|
||||
class MultiThreadedDBTest : public DBTest,
|
||||
public ::testing::WithParamInterface<int> {
|
||||
public:
|
||||
virtual void SetUp() override { option_config_ = GetParam(); }
|
||||
void SetUp() override { option_config_ = GetParam(); }
|
||||
|
||||
static std::vector<int> GenerateOptionConfigs() {
|
||||
std::vector<int> optionConfigs;
|
||||
for (int optionConfig = kDefault; optionConfig < kEnd; ++optionConfig) {
|
||||
// skip as HashCuckooRep does not support snapshot
|
||||
if (optionConfig != kHashCuckoo) {
|
||||
optionConfigs.push_back(optionConfig);
|
||||
}
|
||||
optionConfigs.push_back(optionConfig);
|
||||
}
|
||||
return optionConfigs;
|
||||
}
|
||||
@@ -2402,7 +2392,7 @@ class ModelDB : public DB {
|
||||
public:
|
||||
KVMap map_;
|
||||
|
||||
virtual SequenceNumber GetSequenceNumber() const override {
|
||||
SequenceNumber GetSequenceNumber() const override {
|
||||
// no need to call this
|
||||
assert(false);
|
||||
return 0;
|
||||
@@ -2411,43 +2401,43 @@ class ModelDB : public DB {
|
||||
|
||||
explicit ModelDB(const Options& options) : options_(options) {}
|
||||
using DB::Put;
|
||||
virtual Status Put(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& k, const Slice& v) override {
|
||||
Status Put(const WriteOptions& o, ColumnFamilyHandle* cf, const Slice& k,
|
||||
const Slice& v) override {
|
||||
WriteBatch batch;
|
||||
batch.Put(cf, k, v);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Close;
|
||||
virtual Status Close() override { return Status::OK(); }
|
||||
Status Close() override { return Status::OK(); }
|
||||
using DB::Delete;
|
||||
virtual Status Delete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
Status Delete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
WriteBatch batch;
|
||||
batch.Delete(cf, key);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::SingleDelete;
|
||||
virtual Status SingleDelete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
Status SingleDelete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
WriteBatch batch;
|
||||
batch.SingleDelete(cf, key);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Merge;
|
||||
virtual Status Merge(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& k, const Slice& v) override {
|
||||
Status Merge(const WriteOptions& o, ColumnFamilyHandle* cf, const Slice& k,
|
||||
const Slice& v) override {
|
||||
WriteBatch batch;
|
||||
batch.Merge(cf, k, v);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& /*options*/, ColumnFamilyHandle* /*cf*/,
|
||||
const Slice& key, PinnableSlice* /*value*/) override {
|
||||
Status Get(const ReadOptions& /*options*/, ColumnFamilyHandle* /*cf*/,
|
||||
const Slice& key, PinnableSlice* /*value*/) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
std::vector<Status> MultiGet(
|
||||
const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family*/,
|
||||
const std::vector<Slice>& keys,
|
||||
@@ -2459,25 +2449,31 @@ class ModelDB : public DB {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*options*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
virtual Status VerifyChecksum() override {
|
||||
using DB::IngestExternalFiles;
|
||||
Status IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& /*args*/) override {
|
||||
return Status::NotSupported("Not implemented");
|
||||
}
|
||||
|
||||
Status VerifyChecksum() override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
TablePropertiesCollection* /*props*/) override {
|
||||
return Status();
|
||||
}
|
||||
|
||||
virtual Status GetPropertiesOfTablesInRange(
|
||||
Status GetPropertiesOfTablesInRange(
|
||||
ColumnFamilyHandle* /*column_family*/, const Range* /*range*/,
|
||||
std::size_t /*n*/, TablePropertiesCollection* /*props*/) override {
|
||||
return Status();
|
||||
@@ -2485,19 +2481,18 @@ class ModelDB : public DB {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
using DB::KeyMayExist;
|
||||
virtual bool KeyMayExist(const ReadOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, std::string* /*value*/,
|
||||
bool* value_found = nullptr) override {
|
||||
bool KeyMayExist(const ReadOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
std::string* /*value*/,
|
||||
bool* value_found = nullptr) override {
|
||||
if (value_found != nullptr) {
|
||||
*value_found = false;
|
||||
}
|
||||
return true; // Not Supported directly
|
||||
}
|
||||
using DB::NewIterator;
|
||||
virtual Iterator* NewIterator(
|
||||
const ReadOptions& options,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
Iterator* NewIterator(const ReadOptions& options,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
if (options.snapshot == nullptr) {
|
||||
KVMap* saved = new KVMap;
|
||||
*saved = map_;
|
||||
@@ -2508,38 +2503,33 @@ class ModelDB : public DB {
|
||||
return new ModelIter(snapshot_state, false);
|
||||
}
|
||||
}
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family*/,
|
||||
std::vector<Iterator*>* /*iterators*/) override {
|
||||
Status NewIterators(const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family*/,
|
||||
std::vector<Iterator*>* /*iterators*/) override {
|
||||
return Status::NotSupported("Not supported yet");
|
||||
}
|
||||
virtual const Snapshot* GetSnapshot() override {
|
||||
const Snapshot* GetSnapshot() override {
|
||||
ModelSnapshot* snapshot = new ModelSnapshot;
|
||||
snapshot->map_ = map_;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override {
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override {
|
||||
delete reinterpret_cast<const ModelSnapshot*>(snapshot);
|
||||
}
|
||||
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* batch) override {
|
||||
Status Write(const WriteOptions& /*options*/, WriteBatch* batch) override {
|
||||
class Handler : public WriteBatch::Handler {
|
||||
public:
|
||||
KVMap* map_;
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
(*map_)[key.ToString()] = value.ToString();
|
||||
}
|
||||
virtual void Merge(const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
void Merge(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
// ignore merge for now
|
||||
// (*map_)[key.ToString()] = value.ToString();
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
map_->erase(key.ToString());
|
||||
}
|
||||
void Delete(const Slice& key) override { map_->erase(key.ToString()); }
|
||||
};
|
||||
Handler handler;
|
||||
handler.map_ = &map_;
|
||||
@@ -2547,67 +2537,64 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
using DB::GetProperty;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/,
|
||||
std::string* /*value*/) override {
|
||||
bool GetProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/, std::string* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetIntProperty;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/,
|
||||
uint64_t* /*value*/) override {
|
||||
bool GetIntProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/, uint64_t* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*property*/,
|
||||
std::map<std::string, std::string>* /*value*/) override {
|
||||
bool GetMapProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/,
|
||||
std::map<std::string, std::string>* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetAggregatedIntProperty;
|
||||
virtual bool GetAggregatedIntProperty(const Slice& /*property*/,
|
||||
uint64_t* /*value*/) override {
|
||||
bool GetAggregatedIntProperty(const Slice& /*property*/,
|
||||
uint64_t* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* /*column_family*/,
|
||||
const Range* /*range*/, int n,
|
||||
uint64_t* sizes,
|
||||
uint8_t /*include_flags*/
|
||||
= INCLUDE_FILES) override {
|
||||
void GetApproximateSizes(ColumnFamilyHandle* /*column_family*/,
|
||||
const Range* /*range*/, int n, uint64_t* sizes,
|
||||
uint8_t /*include_flags*/
|
||||
= INCLUDE_FILES) override {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sizes[i] = 0;
|
||||
}
|
||||
}
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(
|
||||
ColumnFamilyHandle* /*column_family*/, const Range& /*range*/,
|
||||
uint64_t* const count, uint64_t* const size) override {
|
||||
void GetApproximateMemTableStats(ColumnFamilyHandle* /*column_family*/,
|
||||
const Range& /*range*/,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override {
|
||||
*count = 0;
|
||||
*size = 0;
|
||||
}
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*start*/,
|
||||
const Slice* /*end*/) override {
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*start*/, const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& /*new_options*/)
|
||||
override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual Status CompactFiles(
|
||||
Status CompactFiles(
|
||||
const CompactionOptions& /*compact_options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1,
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr
|
||||
) override {
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr,
|
||||
CompactionJobInfo* /*compaction_job_info*/ = nullptr) override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
@@ -2626,69 +2613,60 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
using DB::NumberLevels;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* /*column_family*/) override {
|
||||
return 1;
|
||||
}
|
||||
int NumberLevels(ColumnFamilyHandle* /*column_family*/) override { return 1; }
|
||||
|
||||
using DB::MaxMemCompactionLevel;
|
||||
virtual int MaxMemCompactionLevel(
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
int MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
using DB::Level0StopWriteTrigger;
|
||||
virtual int Level0StopWriteTrigger(
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
int Level0StopWriteTrigger(ColumnFamilyHandle* /*column_family*/) override {
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual const std::string& GetName() const override { return name_; }
|
||||
const std::string& GetName() const override { return name_; }
|
||||
|
||||
virtual Env* GetEnv() const override { return nullptr; }
|
||||
Env* GetEnv() const override { return nullptr; }
|
||||
|
||||
using DB::GetOptions;
|
||||
virtual Options GetOptions(
|
||||
ColumnFamilyHandle* /*column_family*/) const override {
|
||||
Options GetOptions(ColumnFamilyHandle* /*column_family*/) const override {
|
||||
return options_;
|
||||
}
|
||||
|
||||
using DB::GetDBOptions;
|
||||
virtual DBOptions GetDBOptions() const override { return options_; }
|
||||
DBOptions GetDBOptions() const override { return options_; }
|
||||
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const rocksdb::FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
Status Flush(const rocksdb::FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
Status ret;
|
||||
return ret;
|
||||
}
|
||||
virtual Status Flush(
|
||||
Status Flush(
|
||||
const rocksdb::FlushOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_families*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status SyncWAL() override { return Status::OK(); }
|
||||
Status SyncWAL() override { return Status::OK(); }
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions() override { return Status::OK(); }
|
||||
Status DisableFileDeletions() override { return Status::OK(); }
|
||||
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&, uint64_t* /*size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
Status EnableFileDeletions(bool /*force*/) override { return Status::OK(); }
|
||||
Status GetLiveFiles(std::vector<std::string>&, uint64_t* /*size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& /*files*/) override {
|
||||
Status GetSortedWalFiles(VectorLogPtr& /*files*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(std::string /*name*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status DeleteFile(std::string /*name*/) override { return Status::OK(); }
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
Status GetUpdatesSince(
|
||||
rocksdb::SequenceNumber,
|
||||
std::unique_ptr<rocksdb::TransactionLogIterator>*,
|
||||
const TransactionLogIterator::ReadOptions& /*read_options*/ =
|
||||
@@ -2696,52 +2674,48 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not supported in Model DB");
|
||||
}
|
||||
|
||||
virtual void GetColumnFamilyMetaData(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
ColumnFamilyMetaData* /*metadata*/) override {}
|
||||
void GetColumnFamilyMetaData(ColumnFamilyHandle* /*column_family*/,
|
||||
ColumnFamilyMetaData* /*metadata*/) override {}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
virtual Status GetDbIdentity(std::string& /*identity*/) const override {
|
||||
Status GetDbIdentity(std::string& /*identity*/) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override { return 0; }
|
||||
SequenceNumber GetLatestSequenceNumber() const override { return 0; }
|
||||
|
||||
virtual bool SetPreserveDeletesSequenceNumber(
|
||||
SequenceNumber /*seqnum*/) override {
|
||||
bool SetPreserveDeletesSequenceNumber(SequenceNumber /*seqnum*/) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual ColumnFamilyHandle* DefaultColumnFamily() const override {
|
||||
return nullptr;
|
||||
}
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override { return nullptr; }
|
||||
|
||||
private:
|
||||
class ModelIter : public Iterator {
|
||||
public:
|
||||
ModelIter(const KVMap* map, bool owned)
|
||||
: map_(map), owned_(owned), iter_(map_->end()) {}
|
||||
~ModelIter() {
|
||||
~ModelIter() override {
|
||||
if (owned_) delete map_;
|
||||
}
|
||||
virtual bool Valid() const override { return iter_ != map_->end(); }
|
||||
virtual void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
virtual void SeekToLast() override {
|
||||
bool Valid() const override { return iter_ != map_->end(); }
|
||||
void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
void SeekToLast() override {
|
||||
if (map_->empty()) {
|
||||
iter_ = map_->end();
|
||||
} else {
|
||||
iter_ = map_->find(map_->rbegin()->first);
|
||||
}
|
||||
}
|
||||
virtual void Seek(const Slice& k) override {
|
||||
void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
void Next() override { ++iter_; }
|
||||
void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
iter_ = map_->end();
|
||||
return;
|
||||
@@ -2749,9 +2723,9 @@ class ModelDB : public DB {
|
||||
--iter_;
|
||||
}
|
||||
|
||||
virtual Slice key() const override { return iter_->first; }
|
||||
virtual Slice value() const override { return iter_->second; }
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
Slice key() const override { return iter_->first; }
|
||||
Slice value() const override { return iter_->second; }
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const KVMap* const map_;
|
||||
@@ -2819,15 +2793,14 @@ static bool CompareIterators(int step, DB* model, DB* db,
|
||||
class DBTestRandomized : public DBTest,
|
||||
public ::testing::WithParamInterface<int> {
|
||||
public:
|
||||
virtual void SetUp() override { option_config_ = GetParam(); }
|
||||
void SetUp() override { option_config_ = GetParam(); }
|
||||
|
||||
static std::vector<int> GenerateOptionConfigs() {
|
||||
std::vector<int> option_configs;
|
||||
// skip cuckoo hash as it does not support snapshot.
|
||||
for (int option_config = kDefault; option_config < kEnd; ++option_config) {
|
||||
if (!ShouldSkipOptions(option_config, kSkipDeletesFilterFirst |
|
||||
kSkipNoSeekToLast |
|
||||
kSkipHashCuckoo)) {
|
||||
if (!ShouldSkipOptions(option_config,
|
||||
kSkipDeletesFilterFirst | kSkipNoSeekToLast)) {
|
||||
option_configs.push_back(option_config);
|
||||
}
|
||||
}
|
||||
@@ -2857,7 +2830,6 @@ TEST_P(DBTestRandomized, Randomized) {
|
||||
int p = rnd.Uniform(100);
|
||||
int minimum = 0;
|
||||
if (option_config_ == kHashSkipList || option_config_ == kHashLinkList ||
|
||||
option_config_ == kHashCuckoo ||
|
||||
option_config_ == kPlainTableFirstBytePrefix ||
|
||||
option_config_ == kBlockBasedTableWithWholeKeyHashIndex ||
|
||||
option_config_ == kBlockBasedTableWithPrefixHashIndex) {
|
||||
@@ -3111,7 +3083,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLAndMaxOpenFilesTest) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.ttl = 600; // seconds
|
||||
options.ttl = 600; // seconds
|
||||
|
||||
// Check that it is not supported with max_open_files != -1.
|
||||
options.max_open_files = 100;
|
||||
@@ -3127,7 +3099,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLAndVariousTableFormatsTest) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.ttl = 600; // seconds
|
||||
options.ttl = 600; // seconds
|
||||
|
||||
options = CurrentOptions(options);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory());
|
||||
@@ -3137,10 +3109,6 @@ TEST_F(DBTest, FIFOCompactionWithTTLAndVariousTableFormatsTest) {
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
Destroy(options);
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
Destroy(options);
|
||||
options.table_factory.reset(NewAdaptiveTableFactory());
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
@@ -3162,7 +3130,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60 ; // 1 hour
|
||||
options.ttl = 1 * 60 * 60 ; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -3197,7 +3165,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
{
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -3239,7 +3207,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -3276,7 +3244,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
{
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
@@ -3320,7 +3288,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
options.write_buffer_size = 20 << 10; // 20K
|
||||
options.compaction_options_fifo.max_table_files_size = 1500 << 10; // 1.5MB
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
options = CurrentOptions(options);
|
||||
DestroyAndReopen(options);
|
||||
@@ -4619,7 +4587,7 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
ASSERT_LT(NumTableFilesAtLevel(0), 4);
|
||||
}
|
||||
|
||||
// Test dynamic FIFO copmaction options.
|
||||
// Test dynamic FIFO compaction options.
|
||||
// This test covers just option parsing and makes sure that the options are
|
||||
// correctly assigned. Also look at DBOptionsTest.SetFIFOCompactionOptions
|
||||
// test which makes sure that the FIFO compaction funcionality is working
|
||||
@@ -4633,7 +4601,7 @@ TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
// Initial defaults
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
1024 * 1024 * 1024);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 0);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 0);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
@@ -4641,21 +4609,21 @@ TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
{{"compaction_options_fifo", "{max_table_files_size=23;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 0);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 0);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo", "{ttl=97}"}}));
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "97"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 97);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 97);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo", "{ttl=203;}"}}));
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "203"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
@@ -4663,24 +4631,25 @@ TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
{{"compaction_options_fifo", "{allow_compaction=true;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo", "{max_table_files_size=31;ttl=19;}"}}));
|
||||
{{"compaction_options_fifo", "{max_table_files_size=31;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
31);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 19);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo",
|
||||
"{max_table_files_size=51;ttl=49;allow_compaction=true;}"}}));
|
||||
"{max_table_files_size=51;allow_compaction=true;}"}}));
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "49"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
51);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 49);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 49);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
}
|
||||
@@ -5047,14 +5016,14 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
public:
|
||||
explicit DelayedMergeOperator(DBTest* d) : db_test_(d) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
db_test_->env_->addon_time_.fetch_add(1000);
|
||||
merge_out->new_value = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayedMergeOperator"; }
|
||||
const char* Name() const override { return "DelayedMergeOperator"; }
|
||||
};
|
||||
|
||||
TEST_F(DBTest, MergeTestTime) {
|
||||
@@ -5190,7 +5159,7 @@ TEST_F(DBTest, EmptyCompactedDB) {
|
||||
TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
class CompactionFilterFactoryGetContext : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
saved_context = context;
|
||||
std::unique_ptr<CompactionFilter> empty_filter;
|
||||
@@ -5440,6 +5409,69 @@ TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, CompactFilesShouldTriggerAutoCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_background_compactions = 1;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.level0_slowdown_writes_trigger = 36;
|
||||
options.level0_stop_writes_trigger = 36;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// generate files for manual compaction
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
rocksdb::ColumnFamilyMetaData cf_meta_data;
|
||||
db_->GetColumnFamilyMetaData(db_->DefaultColumnFamily(), &cf_meta_data);
|
||||
|
||||
std::vector<std::string> input_files;
|
||||
input_files.push_back(cf_meta_data.levels[0].files[0].name);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"CompactFilesImpl:0",
|
||||
"DBTest::CompactFilesShouldTriggerAutoCompaction:Begin"},
|
||||
{"DBTest::CompactFilesShouldTriggerAutoCompaction:End",
|
||||
"CompactFilesImpl:1"},
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread manual_compaction_thread([&]() {
|
||||
auto s = db_->CompactFiles(CompactionOptions(),
|
||||
db_->DefaultColumnFamily(), input_files, 0);
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBTest::CompactFilesShouldTriggerAutoCompaction:Begin");
|
||||
// generate enough files to trigger compaction
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
db_->GetColumnFamilyMetaData(db_->DefaultColumnFamily(), &cf_meta_data);
|
||||
ASSERT_GT(cf_meta_data.levels[0].files.size(),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
TEST_SYNC_POINT(
|
||||
"DBTest::CompactFilesShouldTriggerAutoCompaction:End");
|
||||
|
||||
manual_compaction_thread.join();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
db_->GetColumnFamilyMetaData(db_->DefaultColumnFamily(), &cf_meta_data);
|
||||
ASSERT_LE(cf_meta_data.levels[0].files.size(),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Github issue #595
|
||||
// Large write batch with column families
|
||||
TEST_F(DBTest, LargeBatchWithColumnFamilies) {
|
||||
|
||||
+376
-64
@@ -30,7 +30,7 @@ class PrefixFullBloomWithReverseComparator
|
||||
public:
|
||||
PrefixFullBloomWithReverseComparator()
|
||||
: DBTestBase("/prefix_bloom_reverse") {}
|
||||
virtual void SetUp() override { if_cache_filter_ = GetParam(); }
|
||||
void SetUp() override { if_cache_filter_ = GetParam(); }
|
||||
bool if_cache_filter_;
|
||||
};
|
||||
|
||||
@@ -342,6 +342,7 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
ASSERT_GE(cache->GetUsage(), 1024 * 1024);
|
||||
Close();
|
||||
options.write_buffer_manager.reset();
|
||||
last_options_.write_buffer_manager.reset();
|
||||
ASSERT_LT(cache->GetUsage(), 1024 * 1024);
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -514,9 +515,9 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
apply_option_at_record_index_(apply_option_for_record_index),
|
||||
current_record_index_(0) {}
|
||||
|
||||
virtual WalProcessingOption LogRecord(
|
||||
const WriteBatch& /*batch*/, WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) const override {
|
||||
WalProcessingOption LogRecord(const WriteBatch& /*batch*/,
|
||||
WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) const override {
|
||||
WalFilter::WalProcessingOption option_to_return;
|
||||
|
||||
if (current_record_index_ == apply_option_at_record_index_) {
|
||||
@@ -534,7 +535,7 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
return option_to_return;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "TestWalFilter"; }
|
||||
const char* Name() const override { return "TestWalFilter"; }
|
||||
};
|
||||
|
||||
// Create 3 batches with two keys each
|
||||
@@ -686,7 +687,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
: new_write_batch_(new_write_batch),
|
||||
num_keys_to_add_in_new_batch_(num_keys_to_add_in_new_batch),
|
||||
num_keys_added_(0) {}
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
if (num_keys_added_ < num_keys_to_add_in_new_batch_) {
|
||||
new_write_batch_->Put(key, value);
|
||||
++num_keys_added_;
|
||||
@@ -710,9 +711,9 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
num_keys_to_add_in_new_batch_(num_keys_to_add_in_new_batch),
|
||||
current_record_index_(0) {}
|
||||
|
||||
virtual WalProcessingOption LogRecord(const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
WalProcessingOption LogRecord(const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
if (current_record_index_ >= change_records_from_index_) {
|
||||
ChangeBatchHandler handler(new_batch, num_keys_to_add_in_new_batch_);
|
||||
batch.Iterate(&handler);
|
||||
@@ -728,9 +729,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "TestWalFilterWithChangeBatch";
|
||||
}
|
||||
const char* Name() const override { return "TestWalFilterWithChangeBatch"; }
|
||||
};
|
||||
|
||||
std::vector<std::vector<std::string>> batch_keys(3);
|
||||
@@ -808,18 +807,17 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
TEST_F(DBTest2, WalFilterTestWithChangeBatchExtraKeys) {
|
||||
class TestWalFilterWithChangeBatchAddExtraKeys : public WalFilter {
|
||||
public:
|
||||
virtual WalProcessingOption LogRecord(const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
*new_batch = batch;
|
||||
new_batch->Put("key_extra", "value_extra");
|
||||
*batch_changed = true;
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
WalProcessingOption LogRecord(const WriteBatch& batch, WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
*new_batch = batch;
|
||||
new_batch->Put("key_extra", "value_extra");
|
||||
*batch_changed = true;
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "WalFilterTestWithChangeBatchExtraKeys";
|
||||
}
|
||||
const char* Name() const override {
|
||||
return "WalFilterTestWithChangeBatchExtraKeys";
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::vector<std::string>> batch_keys(3);
|
||||
@@ -883,18 +881,19 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
// for verification against the keys we expect.
|
||||
std::map<uint32_t, std::vector<std::string>> cf_wal_keys_;
|
||||
public:
|
||||
virtual void ColumnFamilyLogNumberMap(
|
||||
const std::map<uint32_t, uint64_t>& cf_lognumber_map,
|
||||
const std::map<std::string, uint32_t>& cf_name_id_map) override {
|
||||
cf_log_number_map_ = cf_lognumber_map;
|
||||
cf_name_id_map_ = cf_name_id_map;
|
||||
}
|
||||
void ColumnFamilyLogNumberMap(
|
||||
const std::map<uint32_t, uint64_t>& cf_lognumber_map,
|
||||
const std::map<std::string, uint32_t>& cf_name_id_map) override {
|
||||
cf_log_number_map_ = cf_lognumber_map;
|
||||
cf_name_id_map_ = cf_name_id_map;
|
||||
}
|
||||
|
||||
virtual WalProcessingOption LogRecordFound(
|
||||
unsigned long long log_number, const std::string& /*log_file_name*/,
|
||||
const WriteBatch& batch, WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) override {
|
||||
class LogRecordBatchHandler : public WriteBatch::Handler {
|
||||
WalProcessingOption LogRecordFound(unsigned long long log_number,
|
||||
const std::string& /*log_file_name*/,
|
||||
const WriteBatch& batch,
|
||||
WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) override {
|
||||
class LogRecordBatchHandler : public WriteBatch::Handler {
|
||||
private:
|
||||
const std::map<uint32_t, uint64_t> & cf_log_number_map_;
|
||||
std::map<uint32_t, std::vector<std::string>> & cf_wal_keys_;
|
||||
@@ -907,8 +906,8 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
cf_wal_keys_(cf_wal_keys),
|
||||
log_number_(current_log_number){}
|
||||
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& /*value*/) override {
|
||||
Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& /*value*/) override {
|
||||
auto it = cf_log_number_map_.find(column_family_id);
|
||||
assert(it != cf_log_number_map_.end());
|
||||
unsigned long long log_number_for_cf = it->second;
|
||||
@@ -926,11 +925,11 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
batch.Iterate(&handler);
|
||||
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "WalFilterTestWithColumnFamilies";
|
||||
}
|
||||
const char* Name() const override {
|
||||
return "WalFilterTestWithColumnFamilies";
|
||||
}
|
||||
|
||||
const std::map<uint32_t, std::vector<std::string>>& GetColumnFamilyKeys() {
|
||||
return cf_wal_keys_;
|
||||
@@ -1037,18 +1036,19 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
ASSERT_TRUE(index == keys_cf.size());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PresetCompressionDict) {
|
||||
// Temporarily disable it because the test is flaky.
|
||||
TEST_F(DBTest2, DISABLED_PresetCompressionDict) {
|
||||
// Verifies that compression ratio improves when dictionary is enabled, and
|
||||
// improves even further when the dictionary is trained by ZSTD.
|
||||
const size_t kBlockSizeBytes = 4 << 10;
|
||||
const size_t kL0FileBytes = 128 << 10;
|
||||
const size_t kApproxPerBlockOverheadBytes = 50;
|
||||
const int kNumL0Files = 5;
|
||||
const int kZstdTrainFactor = 16;
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env; // Make sure to use any custom env that the test is configured with.
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.arena_block_size = kBlockSizeBytes;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
@@ -1090,16 +1090,15 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case 1:
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.max_dict_bytes = 4 * kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case 2:
|
||||
if (compression_type != kZSTD) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes =
|
||||
kZstdTrainFactor * kBlockSizeBytes;
|
||||
options.compression_opts.max_dict_bytes = 4 * kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -1109,20 +1108,24 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
std::string seq_data =
|
||||
RandomString(&rnd, kBlockSizeBytes - kApproxPerBlockOverheadBytes);
|
||||
std::string seq_datas[10];
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
seq_datas[j] =
|
||||
RandomString(&rnd, kBlockSizeBytes - kApproxPerBlockOverheadBytes);
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
|
||||
for (int j = 0; j < kNumL0Files; ++j) {
|
||||
for (size_t k = 0; k < kL0FileBytes / kBlockSizeBytes + 1; ++k) {
|
||||
ASSERT_OK(Put(1, Key(static_cast<int>(
|
||||
j * (kL0FileBytes / kBlockSizeBytes) + k)),
|
||||
seq_data));
|
||||
auto key_num = j * (kL0FileBytes / kBlockSizeBytes) + k;
|
||||
ASSERT_OK(Put(1, Key(static_cast<int>(key_num)),
|
||||
seq_datas[(key_num / 10) % 10]));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_EQ(j + 1, NumTableFilesAtLevel(0, 1));
|
||||
}
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr);
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1],
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1, 1), 0);
|
||||
|
||||
@@ -1137,7 +1140,7 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
|
||||
for (size_t j = 0; j < kNumL0Files * (kL0FileBytes / kBlockSizeBytes);
|
||||
j++) {
|
||||
ASSERT_EQ(seq_data, Get(1, Key(static_cast<int>(j))));
|
||||
ASSERT_EQ(seq_datas[(j / 10) % 10], Get(1, Key(static_cast<int>(j))));
|
||||
}
|
||||
if (i) {
|
||||
ASSERT_GT(prev_out_bytes, out_bytes);
|
||||
@@ -1148,6 +1151,70 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PresetCompressionDictLocality) {
|
||||
if (!ZSTD_Supported()) {
|
||||
return;
|
||||
}
|
||||
// Verifies that compression dictionary is generated from local data. The
|
||||
// verification simply checks all output SSTs have different compression
|
||||
// dictionaries. We do not verify effectiveness as that'd likely be flaky in
|
||||
// the future.
|
||||
const int kNumEntriesPerFile = 1 << 10; // 1KB
|
||||
const int kNumBytesPerEntry = 1 << 10; // 1KB
|
||||
const int kNumFiles = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kZSTD;
|
||||
options.compression_opts.max_dict_bytes = 1 << 14; // 16KB
|
||||
options.compression_opts.zstd_max_train_bytes = 1 << 18; // 256KB
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(i * kNumEntriesPerFile + j),
|
||||
RandomString(&rnd, kNumBytesPerEntry)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), i + 1);
|
||||
}
|
||||
|
||||
// Store all the dictionaries generated during a full compaction.
|
||||
std::vector<std::string> compression_dicts;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WriteCompressionDictBlock:RawDict",
|
||||
[&](void* arg) {
|
||||
compression_dicts.emplace_back(static_cast<Slice*>(arg)->ToString());
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
CompactRangeOptions compact_range_opts;
|
||||
compact_range_opts.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(compact_range_opts, nullptr, nullptr));
|
||||
|
||||
// Dictionary compression should not be so good as to compress four totally
|
||||
// random files into one. If it does then there's probably something wrong
|
||||
// with the test.
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
||||
|
||||
// Furthermore, there should be one compression dictionary generated per file.
|
||||
// And they should all be different from each other.
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1),
|
||||
static_cast<int>(compression_dicts.size()));
|
||||
for (size_t i = 1; i < compression_dicts.size(); ++i) {
|
||||
std::string& a = compression_dicts[i - 1];
|
||||
std::string& b = compression_dicts[i];
|
||||
size_t alen = a.size();
|
||||
size_t blen = b.size();
|
||||
ASSERT_TRUE(alen != blen || memcmp(a.data(), b.data(), alen) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
class CompactionCompressionListener : public EventListener {
|
||||
public:
|
||||
explicit CompactionCompressionListener(Options* db_options)
|
||||
@@ -1345,11 +1412,49 @@ TEST_F(DBTest2, FirstSnapshotTest) {
|
||||
db_->ReleaseSnapshot(s1);
|
||||
}
|
||||
|
||||
class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, DuplicateSnapshot) {
|
||||
Options options;
|
||||
options = CurrentOptions(options);
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
SequenceNumber oldest_ww_snap, first_ww_snap;
|
||||
|
||||
Put("k", "v"); // inc seq
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
Put("k", "v"); // inc seq
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
snapshots.push_back(dbi->GetSnapshotForWriteConflictBoundary());
|
||||
first_ww_snap = snapshots.back()->GetSequenceNumber();
|
||||
Put("k", "v"); // inc seq
|
||||
snapshots.push_back(dbi->GetSnapshotForWriteConflictBoundary());
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
Put("k", "v"); // inc seq
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(dbi->mutex());
|
||||
auto seqs = dbi->snapshots().GetAll(&oldest_ww_snap);
|
||||
ASSERT_EQ(seqs.size(), 4); // duplicates are not counted
|
||||
ASSERT_EQ(oldest_ww_snap, first_ww_snap);
|
||||
}
|
||||
|
||||
for (auto s : snapshots) {
|
||||
db_->ReleaseSnapshot(s);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
class PinL0IndexAndFilterBlocksTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
PinL0IndexAndFilterBlocksTest() : DBTestBase("/db_pin_l0_index_bloom_test") {}
|
||||
virtual void SetUp() override { infinite_max_files_ = GetParam(); }
|
||||
void SetUp() override {
|
||||
infinite_max_files_ = std::get<0>(GetParam());
|
||||
disallow_preload_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
void CreateTwoLevels(Options* options, bool close_afterwards) {
|
||||
if (infinite_max_files_) {
|
||||
@@ -1386,6 +1491,7 @@ class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
}
|
||||
|
||||
bool infinite_max_files_;
|
||||
bool disallow_preload_;
|
||||
};
|
||||
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
@@ -1476,12 +1582,27 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
uint64_t im = TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS);
|
||||
uint64_t ih = TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT);
|
||||
|
||||
if (disallow_preload_) {
|
||||
// Now we have two files. We narrow the max open files to allow 3 entries
|
||||
// so that preloading SST files won't happen.
|
||||
options.max_open_files = 13;
|
||||
// RocksDB sanitize max open files to at least 20. Modify it back.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = static_cast<int*>(arg);
|
||||
*max_open_files = 13;
|
||||
});
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Reopen database. If max_open_files is set as -1, table readers will be
|
||||
// preloaded. This will trigger a BlockBasedTable::Open() and prefetch
|
||||
// L0 index and filter. Level 1's prefetching is disabled in DB::Open()
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
if (infinite_max_files_) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
if (!disallow_preload_) {
|
||||
// After reopen, cache miss are increased by one because we read (and only
|
||||
// read) filter and index on L0
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
@@ -1509,7 +1630,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
|
||||
// this should be read from L1
|
||||
value = Get(1, "a");
|
||||
if (infinite_max_files_) {
|
||||
if (!disallow_preload_) {
|
||||
// In inifinite max files case, there's a cache miss in executing Get()
|
||||
// because index and filter are not prefetched before.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
@@ -1527,10 +1648,45 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
ASSERT_EQ(im + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
// Force a full compaction to one single file. There will be a block
|
||||
// cache read for both of index and filter. If prefetch doesn't explicitly
|
||||
// happen, it will happen when verifying the file.
|
||||
Compact(1, "a", "zzzzz");
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
if (!disallow_preload_) {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
// Bloom and index hit will happen when a Get() happens.
|
||||
value = Get(1, "a");
|
||||
if (!disallow_preload_) {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PinL0IndexAndFilterBlocksTest,
|
||||
PinL0IndexAndFilterBlocksTest, ::testing::Bool());
|
||||
PinL0IndexAndFilterBlocksTest,
|
||||
::testing::Values(std::make_tuple(true, false),
|
||||
std::make_tuple(false, false),
|
||||
std::make_tuple(false, true)));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, MaxCompactionBytesTest) {
|
||||
@@ -1598,7 +1754,7 @@ class MockPersistentCache : public PersistentCache {
|
||||
"GetUniqueIdFromFile:FS_IOC_GETVERSION", UniqueIdCallback);
|
||||
}
|
||||
|
||||
virtual ~MockPersistentCache() {}
|
||||
~MockPersistentCache() override {}
|
||||
|
||||
PersistentCache::StatsType Stats() override {
|
||||
return PersistentCache::StatsType();
|
||||
@@ -1646,6 +1802,42 @@ class MockPersistentCache : public PersistentCache {
|
||||
const size_t max_size_ = 10 * 1024; // 10KiB
|
||||
};
|
||||
|
||||
#ifdef OS_LINUX
|
||||
// Make sure that in CPU time perf context counters, Env::NowCPUNanos()
|
||||
// is used, rather than Env::CPUNanos();
|
||||
TEST_F(DBTest2, TestPerfContextCpuTime) {
|
||||
// force resizing table cache so table handle is not preloaded so that
|
||||
// we can measure find_table_nanos during Get().
|
||||
dbfull()->TEST_table_cache()->SetCapacity(0);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
env_->now_cpu_count_.store(0);
|
||||
|
||||
// CPU timing is not enabled with kEnableTimeExceptForMutex
|
||||
SetPerfLevel(PerfLevel::kEnableTimeExceptForMutex);
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
ASSERT_EQ(0, get_perf_context()->get_cpu_nanos);
|
||||
ASSERT_EQ(0, env_->now_cpu_count_.load());
|
||||
|
||||
uint64_t kDummyAddonTime = uint64_t{1000000000000};
|
||||
|
||||
// Add time to NowNanos() reading.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::FindTable:0",
|
||||
[&](void* /*arg*/) { env_->addon_time_.fetch_add(kDummyAddonTime); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
ASSERT_GT(env_->now_cpu_count_.load(), 2);
|
||||
ASSERT_LT(get_perf_context()->get_cpu_nanos, kDummyAddonTime);
|
||||
ASSERT_GT(get_perf_context()->find_table_nanos, kDummyAddonTime);
|
||||
|
||||
SetPerfLevel(PerfLevel::kDisable);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif // OS_LINUX
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
TEST_F(DBTest2, PersistentCache) {
|
||||
int num_iter = 80;
|
||||
@@ -2453,9 +2645,7 @@ TEST_F(DBTest2, ReadCallbackTest) {
|
||||
class TestReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit TestReadCallback(SequenceNumber snapshot) : snapshot_(snapshot) {}
|
||||
virtual bool IsVisible(SequenceNumber seq) override {
|
||||
return seq <= snapshot_;
|
||||
}
|
||||
bool IsVisible(SequenceNumber seq) override { return seq <= snapshot_; }
|
||||
|
||||
private:
|
||||
SequenceNumber snapshot_;
|
||||
@@ -2756,6 +2946,77 @@ TEST_F(DBTest2, TraceWithLimit) {
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceWithSampling) {
|
||||
Options options = CurrentOptions();
|
||||
ReadOptions ro;
|
||||
WriteOptions wo;
|
||||
TraceOptions trace_opts;
|
||||
EnvOptions env_opts;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
|
||||
// test the trace file sampling options
|
||||
trace_opts.sampling_frequency = 2;
|
||||
std::string trace_filename = dbname_ + "/rocksdb.trace_sampling";
|
||||
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(Put(0, "b", "2"));
|
||||
ASSERT_OK(Put(0, "c", "3"));
|
||||
ASSERT_OK(Put(0, "d", "4"));
|
||||
ASSERT_OK(Put(0, "e", "5"));
|
||||
ASSERT_OK(db_->EndTrace());
|
||||
|
||||
std::string dbname2 = test::TmpDir(env_) + "/db_replay_sampling";
|
||||
std::string value;
|
||||
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;
|
||||
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);
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "b", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "c", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "d", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "e", &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_TRUE(db2->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
ASSERT_FALSE(db2->Get(ro, handles[0], "b", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "c", &value).IsNotFound());
|
||||
ASSERT_FALSE(db2->Get(ro, handles[0], "d", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "e", &value).IsNotFound());
|
||||
|
||||
for (auto handle : handles) {
|
||||
delete handle;
|
||||
}
|
||||
delete db2;
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, PinnableSliceAndMmapReads) {
|
||||
@@ -3088,6 +3349,57 @@ TEST_F(DBTest2, TestCompactFiles) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// TODO: figure out why this test fails in appveyor
|
||||
#ifndef OS_WIN
|
||||
TEST_F(DBTest2, MultiDBParallelOpenTest) {
|
||||
const int kNumDbs = 2;
|
||||
Options options = CurrentOptions();
|
||||
std::vector<std::string> dbnames;
|
||||
for (int i = 0; i < kNumDbs; ++i) {
|
||||
dbnames.emplace_back(test::TmpDir(env_) + "/db" + ToString(i));
|
||||
ASSERT_OK(DestroyDB(dbnames.back(), options));
|
||||
}
|
||||
|
||||
// Verify empty DBs can be created in parallel
|
||||
std::vector<std::thread> open_threads;
|
||||
std::vector<DB*> dbs{static_cast<unsigned int>(kNumDbs), nullptr};
|
||||
options.create_if_missing = true;
|
||||
for (int i = 0; i < kNumDbs; ++i) {
|
||||
open_threads.emplace_back(
|
||||
[&](int dbnum) {
|
||||
ASSERT_OK(DB::Open(options, dbnames[dbnum], &dbs[dbnum]));
|
||||
},
|
||||
i);
|
||||
}
|
||||
|
||||
// Now add some data and close, so next we can verify non-empty DBs can be
|
||||
// recovered in parallel
|
||||
for (int i = 0; i < kNumDbs; ++i) {
|
||||
open_threads[i].join();
|
||||
ASSERT_OK(dbs[i]->Put(WriteOptions(), "xi", "gua"));
|
||||
delete dbs[i];
|
||||
}
|
||||
|
||||
// Verify non-empty DBs can be recovered in parallel
|
||||
dbs.clear();
|
||||
open_threads.clear();
|
||||
for (int i = 0; i < kNumDbs; ++i) {
|
||||
open_threads.emplace_back(
|
||||
[&](int dbnum) {
|
||||
ASSERT_OK(DB::Open(options, dbnames[dbnum], &dbs[dbnum]));
|
||||
},
|
||||
i);
|
||||
}
|
||||
|
||||
// Wait and cleanup
|
||||
for (int i = 0; i < kNumDbs; ++i) {
|
||||
open_threads[i].join();
|
||||
delete dbs[i];
|
||||
ASSERT_OK(DestroyDB(dbnames[i], options));
|
||||
}
|
||||
}
|
||||
#endif // OS_WIN
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+44
-26
@@ -101,18 +101,18 @@ DBTestBase::~DBTestBase() {
|
||||
bool DBTestBase::ShouldSkipOptions(int option_config, int skip_mask) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
// These options are not supported in ROCKSDB_LITE
|
||||
if (option_config == kHashSkipList ||
|
||||
option_config == kPlainTableFirstBytePrefix ||
|
||||
option_config == kPlainTableCappedPrefix ||
|
||||
option_config == kPlainTableCappedPrefixNonMmap ||
|
||||
option_config == kPlainTableAllBytesPrefix ||
|
||||
option_config == kVectorRep || option_config == kHashLinkList ||
|
||||
option_config == kHashCuckoo || option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
option_config == kUniversalSubcompactions ||
|
||||
option_config == kFIFOCompaction ||
|
||||
option_config == kConcurrentSkipList) {
|
||||
return true;
|
||||
if (option_config == kHashSkipList ||
|
||||
option_config == kPlainTableFirstBytePrefix ||
|
||||
option_config == kPlainTableCappedPrefix ||
|
||||
option_config == kPlainTableCappedPrefixNonMmap ||
|
||||
option_config == kPlainTableAllBytesPrefix ||
|
||||
option_config == kVectorRep || option_config == kHashLinkList ||
|
||||
option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
option_config == kUniversalSubcompactions ||
|
||||
option_config == kFIFOCompaction ||
|
||||
option_config == kConcurrentSkipList) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -141,9 +141,6 @@ bool DBTestBase::ShouldSkipOptions(int option_config, int skip_mask) {
|
||||
option_config == kBlockBasedTableWithWholeKeyHashIndex)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipHashCuckoo) && (option_config == kHashCuckoo)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipFIFOCompaction) && option_config == kFIFOCompaction) {
|
||||
return true;
|
||||
}
|
||||
@@ -383,11 +380,6 @@ Options DBTestBase::GetOptions(
|
||||
NewHashLinkListRepFactory(4, 0, 3, true, 4));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
break;
|
||||
case kHashCuckoo:
|
||||
options.memtable_factory.reset(
|
||||
NewHashCuckooRepFactory(options.write_buffer_size));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
break;
|
||||
case kDirectIO: {
|
||||
options.use_direct_reads = true;
|
||||
options.use_direct_io_for_flush_and_compaction = true;
|
||||
@@ -604,6 +596,7 @@ Status DBTestBase::TryReopenWithColumnFamilies(
|
||||
column_families.push_back(ColumnFamilyDescriptor(cfs[i], options[i]));
|
||||
}
|
||||
DBOptions db_opts = DBOptions(options[0]);
|
||||
last_options_ = options[0];
|
||||
return DB::Open(db_opts, dbname_, column_families, &handles_, &db_);
|
||||
}
|
||||
|
||||
@@ -762,6 +755,31 @@ std::string DBTestBase::Get(int cf, const std::string& k,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> DBTestBase::MultiGet(std::vector<int> cfs,
|
||||
const std::vector<std::string>& k,
|
||||
const Snapshot* snapshot) {
|
||||
ReadOptions options;
|
||||
options.verify_checksums = true;
|
||||
options.snapshot = snapshot;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
std::vector<Slice> keys;
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (unsigned int i = 0; i < cfs.size(); ++i) {
|
||||
handles.push_back(handles_[cfs[i]]);
|
||||
keys.push_back(k[i]);
|
||||
}
|
||||
std::vector<Status> s = db_->MultiGet(options, handles, keys, &result);
|
||||
for (unsigned int i = 0; i < s.size(); ++i) {
|
||||
if (s[i].IsNotFound()) {
|
||||
result[i] = "NOT_FOUND";
|
||||
} else if (!s[i].ok()) {
|
||||
result[i] = s[i].ToString();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Status DBTestBase::Get(const std::string& k, PinnableSlice* v) {
|
||||
ReadOptions options;
|
||||
options.verify_checksums = true;
|
||||
@@ -814,8 +832,8 @@ std::string DBTestBase::AllEntriesFor(const Slice& user_key, int cf) {
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregatorV2 range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ScopedArenaIterator iter;
|
||||
if (cf == 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
@@ -1227,8 +1245,8 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregatorV2 range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
// This should be defined after range_del_agg so that it destructs the
|
||||
// assigned iterator before it range_del_agg is already destructed.
|
||||
ScopedArenaIterator iter;
|
||||
@@ -1437,8 +1455,8 @@ void DBTestBase::VerifyDBInternal(
|
||||
std::vector<std::pair<std::string, std::string>> true_data) {
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(last_options_.comparator);
|
||||
RangeDelAggregatorV2 range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
auto iter =
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, kMaxSequenceNumber);
|
||||
iter->SeekToFirst();
|
||||
|
||||
+33
-24
@@ -503,6 +503,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual uint64_t NowCPUNanos() override {
|
||||
now_cpu_count_.fetch_add(1);
|
||||
return target()->NowCPUNanos();
|
||||
}
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowNanos()) +
|
||||
addon_time_.load() * 1000;
|
||||
@@ -572,6 +577,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int64_t> addon_time_;
|
||||
|
||||
std::atomic<int> now_cpu_count_;
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
std::atomic<bool> time_elapse_only_sleep_;
|
||||
@@ -645,29 +652,28 @@ class DBTestBase : public testing::Test {
|
||||
kPlainTableAllBytesPrefix = 6,
|
||||
kVectorRep = 7,
|
||||
kHashLinkList = 8,
|
||||
kHashCuckoo = 9,
|
||||
kMergePut = 10,
|
||||
kFilter = 11,
|
||||
kFullFilterWithNewTableReaderForCompactions = 12,
|
||||
kUncompressed = 13,
|
||||
kNumLevel_3 = 14,
|
||||
kDBLogDir = 15,
|
||||
kWalDirAndMmapReads = 16,
|
||||
kManifestFileSize = 17,
|
||||
kPerfOptions = 18,
|
||||
kHashSkipList = 19,
|
||||
kUniversalCompaction = 20,
|
||||
kUniversalCompactionMultiLevel = 21,
|
||||
kCompressedBlockCache = 22,
|
||||
kInfiniteMaxOpenFiles = 23,
|
||||
kxxHashChecksum = 24,
|
||||
kFIFOCompaction = 25,
|
||||
kOptimizeFiltersForHits = 26,
|
||||
kRowCache = 27,
|
||||
kRecycleLogFiles = 28,
|
||||
kConcurrentSkipList = 29,
|
||||
kPipelinedWrite = 30,
|
||||
kConcurrentWALWrites = 31,
|
||||
kMergePut = 9,
|
||||
kFilter = 10,
|
||||
kFullFilterWithNewTableReaderForCompactions = 11,
|
||||
kUncompressed = 12,
|
||||
kNumLevel_3 = 13,
|
||||
kDBLogDir = 14,
|
||||
kWalDirAndMmapReads = 15,
|
||||
kManifestFileSize = 16,
|
||||
kPerfOptions = 17,
|
||||
kHashSkipList = 18,
|
||||
kUniversalCompaction = 19,
|
||||
kUniversalCompactionMultiLevel = 20,
|
||||
kCompressedBlockCache = 21,
|
||||
kInfiniteMaxOpenFiles = 22,
|
||||
kxxHashChecksum = 23,
|
||||
kFIFOCompaction = 24,
|
||||
kOptimizeFiltersForHits = 25,
|
||||
kRowCache = 26,
|
||||
kRecycleLogFiles = 27,
|
||||
kConcurrentSkipList = 28,
|
||||
kPipelinedWrite = 29,
|
||||
kConcurrentWALWrites = 30,
|
||||
kDirectIO,
|
||||
kLevelSubcompactions,
|
||||
kBlockBasedTableWithIndexRestartInterval,
|
||||
@@ -703,7 +709,6 @@ class DBTestBase : public testing::Test {
|
||||
kSkipPlainTable = 8,
|
||||
kSkipHashIndex = 16,
|
||||
kSkipNoSeekToLast = 32,
|
||||
kSkipHashCuckoo = 64,
|
||||
kSkipFIFOCompaction = 128,
|
||||
kSkipMmapReads = 256,
|
||||
};
|
||||
@@ -833,6 +838,10 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
Status Get(const std::string& k, PinnableSlice* v);
|
||||
|
||||
std::vector<std::string> MultiGet(std::vector<int> cfs,
|
||||
const std::vector<std::string>& k,
|
||||
const Snapshot* snapshot = nullptr);
|
||||
|
||||
uint64_t GetNumSnapshots();
|
||||
|
||||
uint64_t GetTimeOldestSnapshots();
|
||||
|
||||
@@ -27,7 +27,7 @@ class DBTestUniversalCompactionBase
|
||||
public:
|
||||
explicit DBTestUniversalCompactionBase(
|
||||
const std::string& path) : DBTestBase(path) {}
|
||||
virtual void SetUp() override {
|
||||
void SetUp() override {
|
||||
num_levels_ = std::get<0>(GetParam());
|
||||
exclusive_manual_compaction_ = std::get<1>(GetParam());
|
||||
}
|
||||
@@ -63,13 +63,13 @@ void VerifyCompactionResult(
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilter"; }
|
||||
const char* Name() const override { return "KeepFilter"; }
|
||||
};
|
||||
|
||||
class KeepFilterFactory : public CompactionFilterFactory {
|
||||
@@ -77,7 +77,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
explicit KeepFilterFactory(bool check_context = false)
|
||||
: check_context_(check_context) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (check_context_) {
|
||||
EXPECT_EQ(expect_full_compaction_.load(), context.is_full_compaction);
|
||||
@@ -86,7 +86,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
return std::unique_ptr<CompactionFilter>(new KeepFilter());
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilterFactory"; }
|
||||
const char* Name() const override { return "KeepFilterFactory"; }
|
||||
bool check_context_;
|
||||
std::atomic_bool expect_full_compaction_;
|
||||
std::atomic_bool expect_manual_compaction_;
|
||||
@@ -95,14 +95,14 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilter"; }
|
||||
const char* Name() const override { return "DelayFilter"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -111,12 +111,12 @@ class DelayFilter : public CompactionFilter {
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilterFactory"; }
|
||||
const char* Name() const override { return "DelayFilterFactory"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
|
||||
+28
-4
@@ -283,7 +283,31 @@ TEST_F(DBWALTest, RecoverWithTableHandle) {
|
||||
ASSERT_OK(Put(1, "bar", "v4"));
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "big", std::string(100, 'a')));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
|
||||
|
||||
options = CurrentOptions();
|
||||
const int kSmallMaxOpenFiles = 13;
|
||||
if (option_config_ == kDBLogDir) {
|
||||
// Use this option to check not preloading files
|
||||
// Set the max open files to be small enough so no preload will
|
||||
// happen.
|
||||
options.max_open_files = kSmallMaxOpenFiles;
|
||||
// RocksDB sanitize max open files to at least 20. Modify it back.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = static_cast<int*>(arg);
|
||||
*max_open_files = kSmallMaxOpenFiles;
|
||||
});
|
||||
|
||||
} else if (option_config_ == kWalDirAndMmapReads) {
|
||||
// Use this option to check always loading all files.
|
||||
options.max_open_files = 100;
|
||||
} else {
|
||||
options.max_open_files = -1;
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(handles_[1], &files);
|
||||
@@ -294,10 +318,10 @@ TEST_F(DBWALTest, RecoverWithTableHandle) {
|
||||
ASSERT_EQ(total_files, 3);
|
||||
for (const auto& level : files) {
|
||||
for (const auto& file : level) {
|
||||
if (kInfiniteMaxOpenFiles == option_config_) {
|
||||
ASSERT_TRUE(file.table_reader_handle != nullptr);
|
||||
} else {
|
||||
if (options.max_open_files == kSmallMaxOpenFiles) {
|
||||
ASSERT_TRUE(file.table_reader_handle == nullptr);
|
||||
} else {
|
||||
ASSERT_TRUE(file.table_reader_handle != nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+301
-124
@@ -14,10 +14,11 @@
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
class ExternalSSTFileBasicTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_test") {
|
||||
ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_basic_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
@@ -42,7 +43,8 @@ class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
const Options options, std::vector<int> keys,
|
||||
const std::vector<ValueType>& value_types,
|
||||
std::vector<std::pair<int, int>> range_deletions, int file_id,
|
||||
bool write_global_seqno, std::map<std::string, std::string>* true_data) {
|
||||
bool write_global_seqno, bool verify_checksums_before_ingest,
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
assert(value_types.size() == 1 || keys.size() == value_types.size());
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
@@ -107,6 +109,7 @@ class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = true;
|
||||
ifo.write_global_seqno = write_global_seqno;
|
||||
ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
|
||||
s = db_->IngestExternalFile({file_path}, ifo);
|
||||
}
|
||||
return s;
|
||||
@@ -115,21 +118,25 @@ class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys,
|
||||
const std::vector<ValueType>& value_types, int file_id,
|
||||
bool write_global_seqno, std::map<std::string, std::string>* true_data) {
|
||||
return GenerateAndAddExternalFile(options, keys, value_types, {}, file_id,
|
||||
write_global_seqno, true_data);
|
||||
bool write_global_seqno, bool verify_checksums_before_ingest,
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
return GenerateAndAddExternalFile(
|
||||
options, keys, value_types, {}, file_id, write_global_seqno,
|
||||
verify_checksums_before_ingest, true_data);
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys, const ValueType value_type,
|
||||
int file_id, bool write_global_seqno,
|
||||
int file_id, bool write_global_seqno, bool verify_checksums_before_ingest,
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
return GenerateAndAddExternalFile(options, keys,
|
||||
std::vector<ValueType>(1, value_type),
|
||||
file_id, write_global_seqno, true_data);
|
||||
return GenerateAndAddExternalFile(
|
||||
options, keys, std::vector<ValueType>(1, value_type), file_id,
|
||||
write_global_seqno, verify_checksums_before_ingest, true_data);
|
||||
}
|
||||
|
||||
~ExternalSSTFileBasicTest() { test::DestroyDir(env_, sst_files_dir_); }
|
||||
~ExternalSSTFileBasicTest() override {
|
||||
test::DestroyDir(env_, sst_files_dir_);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string sst_files_dir_;
|
||||
@@ -249,7 +256,8 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -257,39 +265,39 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
|
||||
int file_id = 1;
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2, 3, 4, 5, 6},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {10, 11, 12, 13},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 4, 6},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 4, 6}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {11, 15, 19},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {11, 15, 19}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {120, 130},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120, 130}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 130},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 130}, ValueType::kTypeValue, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
@@ -300,21 +308,21 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
}
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {60, 61, 62},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {40, 41, 42},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {40, 41, 42}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {20, 30, 40},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {20, 30, 40}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
@@ -322,29 +330,29 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
|
||||
// We will need a seqno for the file regardless if the file overwrite
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1002},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1000, 1002}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2000, 3002},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {2000, 3002}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 20, 40, 100, 150},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 20, 40, 100, 150}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {5000, 5001},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {5000, 5001}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
@@ -354,7 +362,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
@@ -363,59 +372,59 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
|
||||
int file_id = 1;
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2, 3, 4, 5, 6},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {10, 11, 12, 13},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 4, 6},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 4, 6}, ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {11, 15, 19},
|
||||
ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {11, 15, 19}, ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {120, 130},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120, 130}, ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 130},
|
||||
ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 130}, ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120}, {ValueType::kTypeValue}, {{120, 135}}, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {}, {}, {{110, 120}},
|
||||
file_id++, write_global_seqno,
|
||||
&true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {}, {}, {{110, 120}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
// The range deletion ends on a key, but it doesn't actually delete
|
||||
// this key because the largest key in the range is exclusive. Still,
|
||||
// it counts as an overlap so a new seqno will be assigned.
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {}, {}, {{100, 109}},
|
||||
file_id++, write_global_seqno,
|
||||
&true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {}, {}, {{100, 109}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
||||
|
||||
@@ -426,21 +435,21 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
}
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {60, 61, 62},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {40, 41, 42},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {40, 41, 42}, ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {20, 30, 40},
|
||||
ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {20, 30, 40}, ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
@@ -448,29 +457,29 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
|
||||
// We will need a seqno for the file regardless if the file overwrite
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1002},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1000, 1002}, ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2000, 3002},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {2000, 3002}, ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 20, 40, 100, 150},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 20, 40, 100, 150}, ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {5000, 5001},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {5000, 5001}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
@@ -480,7 +489,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
@@ -493,7 +503,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {1, 2, 3, 4, 5, 6},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge, ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
@@ -501,7 +512,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {10, 11, 12, 13},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
@@ -509,7 +521,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {1, 4, 6},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
@@ -517,19 +530,22 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {11, 15, 19},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeMerge,
|
||||
ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120, 130}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 130}, {ValueType::kTypeMerge, ValueType::kTypeDeletion},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
@@ -537,14 +553,16 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {150, 151, 152},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge,
|
||||
ValueType::kTypeDeletion},
|
||||
{{150, 160}, {180, 190}}, file_id++, write_global_seqno, &true_data));
|
||||
{{150, 160}, {180, 190}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {150, 151, 152},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
|
||||
{{200, 250}}, file_id++, write_global_seqno, &true_data));
|
||||
{{200, 250}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
|
||||
|
||||
@@ -552,7 +570,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {300, 301, 302},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge,
|
||||
ValueType::kTypeDeletion},
|
||||
{{1, 2}, {152, 154}}, file_id++, write_global_seqno, &true_data));
|
||||
{{1, 2}, {152, 154}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
||||
|
||||
@@ -566,7 +585,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 61, 62},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
@@ -574,7 +594,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {40, 41, 42},
|
||||
{ValueType::kTypeValue, ValueType::kTypeDeletion,
|
||||
ValueType::kTypeDeletion},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
@@ -582,7 +603,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {20, 30, 40},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeDeletion,
|
||||
ValueType::kTypeDeletion},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
@@ -592,13 +614,15 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1000, 1002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {2000, 3002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
@@ -606,7 +630,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {1, 20, 40, 100, 150},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeDeletion,
|
||||
ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
@@ -614,7 +639,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {5000, 5001}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
@@ -690,13 +716,15 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
// overlaps with L0 file but not memtable, so flush is skipped and file is
|
||||
// ingested into L0
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
{{65, 70}, {70, 85}}, file_id++, write_global_seqno, &true_data));
|
||||
{{65, 70}, {70, 85}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -706,7 +734,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// file is ingested into L5
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {10, 40}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -714,8 +743,9 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
|
||||
// overlaps with L5 file but not memtable or L0 file, so flush is skipped and
|
||||
// file is ingested into L4
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {}, {}, {{5, 15}}, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {}, {}, {{5, 15}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -727,7 +757,8 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// count increases by two.
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {100, 140}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, write_global_seqno, verify_checksums_before_ingest,
|
||||
&true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -740,15 +771,161 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// seqnum.
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
{{160, 200}}, file_id++, write_global_seqno, &true_data));
|
||||
{{160, 200}}, file_id++, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithBadBlockChecksum) {
|
||||
bool change_checksum_called = false;
|
||||
const auto& change_checksum = [&](void* arg) {
|
||||
if (!change_checksum_called) {
|
||||
char* buf = reinterpret_cast<char*>(arg);
|
||||
assert(nullptr != buf);
|
||||
buf[0] ^= 0x1;
|
||||
change_checksum_called = true;
|
||||
}
|
||||
};
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WriteRawBlock:TamperWithChecksum",
|
||||
change_checksum);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
int file_id = 0;
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
std::map<std::string, std::string> true_data;
|
||||
Status s = GenerateAndAddExternalFile(
|
||||
options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, verify_checksums_before_ingest, &true_data);
|
||||
if (verify_checksums_before_ingest) {
|
||||
ASSERT_NOK(s);
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
change_checksum_called = false;
|
||||
} while (ChangeOptionsForFileIngestionTest());
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithFirstByteTampered) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
int file_id = 0;
|
||||
EnvOptions env_options;
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id++);
|
||||
SstFileWriter sst_file_writer(env_options, options);
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
ASSERT_OK(s);
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
std::string key = Key(i);
|
||||
std::string value = Key(i) + ToString(0);
|
||||
ASSERT_OK(sst_file_writer.Put(key, value));
|
||||
}
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
{
|
||||
// Get file size
|
||||
uint64_t file_size = 0;
|
||||
ASSERT_OK(env_->GetFileSize(file_path, &file_size));
|
||||
ASSERT_GT(file_size, 8);
|
||||
std::unique_ptr<RandomRWFile> rwfile;
|
||||
ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
|
||||
// Manually corrupt the file
|
||||
// We deterministically corrupt the first byte because we currently
|
||||
// cannot choose a random offset. The reason for this limitation is that
|
||||
// we do not checksum property block at present.
|
||||
const uint64_t offset = 0;
|
||||
char scratch[8] = {0};
|
||||
Slice buf;
|
||||
ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
|
||||
scratch[0] ^= 0xff; // flip one bit
|
||||
ASSERT_OK(rwfile->Write(offset, buf));
|
||||
}
|
||||
// Ingest file.
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
s = db_->IngestExternalFile({file_path}, ifo);
|
||||
if (ifo.verify_checksums_before_ingest) {
|
||||
ASSERT_NOK(s);
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
} while (ChangeOptionsForFileIngestionTest());
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestExternalFileWithCorruptedPropsBlock) {
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
if (!verify_checksums_before_ingest) {
|
||||
return;
|
||||
}
|
||||
uint64_t props_block_offset = 0;
|
||||
size_t props_block_size = 0;
|
||||
const auto& get_props_block_offset = [&](void* arg) {
|
||||
props_block_offset = *reinterpret_cast<uint64_t*>(arg);
|
||||
};
|
||||
const auto& get_props_block_size = [&](void* arg) {
|
||||
props_block_size = *reinterpret_cast<uint64_t*>(arg);
|
||||
};
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockOffset",
|
||||
get_props_block_offset);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockSize",
|
||||
get_props_block_size);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
int file_id = 0;
|
||||
Random64 rand(time(nullptr));
|
||||
do {
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id++);
|
||||
Options options = CurrentOptions();
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
ASSERT_OK(s);
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
std::string key = Key(i);
|
||||
std::string value = Key(i) + ToString(0);
|
||||
ASSERT_OK(sst_file_writer.Put(key, value));
|
||||
}
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
{
|
||||
std::unique_ptr<RandomRWFile> rwfile;
|
||||
ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
|
||||
// Manually corrupt the file
|
||||
ASSERT_GT(props_block_size, 8);
|
||||
uint64_t offset =
|
||||
props_block_offset + rand.Next() % (props_block_size - 8);
|
||||
char scratch[8] = {0};
|
||||
Slice buf;
|
||||
ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
|
||||
scratch[0] ^= 0xff; // flip one bit
|
||||
ASSERT_OK(rwfile->Write(offset, buf));
|
||||
}
|
||||
|
||||
// Ingest file.
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
ifo.verify_checksums_before_ingest = true;
|
||||
s = db_->IngestExternalFile({file_path}, ifo);
|
||||
ASSERT_NOK(s);
|
||||
} while (ChangeOptionsForFileIngestionTest());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
||||
testing::Bool());
|
||||
testing::Values(std::make_tuple(true, true),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(false, false)));
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
@@ -167,7 +167,6 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
|
||||
bool consumed_seqno = false;
|
||||
bool force_global_seqno = false;
|
||||
|
||||
if (ingestion_options_.snapshot_consistency && !db_snapshots_->empty()) {
|
||||
@@ -197,7 +196,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
|
||||
&assigned_seqno);
|
||||
if (assigned_seqno == last_seqno + 1) {
|
||||
consumed_seqno = true;
|
||||
consumed_seqno_ = true;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -207,13 +206,6 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
f.largest_internal_key(), f.assigned_seqno, f.assigned_seqno,
|
||||
false);
|
||||
}
|
||||
|
||||
if (consumed_seqno) {
|
||||
versions_->SetLastAllocatedSequence(last_seqno + 1);
|
||||
versions_->SetLastPublishedSequence(last_seqno + 1);
|
||||
versions_->SetLastSequence(last_seqno + 1);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -269,6 +261,7 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
consumed_seqno_ = false;
|
||||
} else if (status.ok() && ingestion_options_.move_files) {
|
||||
// The files were moved and added successfully, remove original file links
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
@@ -316,6 +309,13 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
return status;
|
||||
}
|
||||
|
||||
if (ingestion_options_.verify_checksums_before_ingest) {
|
||||
status = table_reader->VerifyChecksum();
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Get the external file properties
|
||||
auto props = table_reader->GetTableProperties();
|
||||
const auto& uprops = props->user_collected_properties;
|
||||
|
||||
@@ -85,7 +85,8 @@ class ExternalSstFileIngestionJob {
|
||||
env_options_(env_options),
|
||||
db_snapshots_(db_snapshots),
|
||||
ingestion_options_(ingestion_options),
|
||||
job_start_time_(env_->NowMicros()) {}
|
||||
job_start_time_(env_->NowMicros()),
|
||||
consumed_seqno_(false) {}
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths,
|
||||
@@ -118,6 +119,9 @@ class ExternalSstFileIngestionJob {
|
||||
return files_to_ingest_;
|
||||
}
|
||||
|
||||
// Whether to increment VersionSet's seqno after this job runs
|
||||
bool ShouldIncrementLastSequence() const { return consumed_seqno_; }
|
||||
|
||||
private:
|
||||
// Open the external file and populate `file_to_ingest` with all the
|
||||
// external information we need to ingest this file.
|
||||
@@ -159,6 +163,7 @@ class ExternalSstFileIngestionJob {
|
||||
const IngestExternalFileOptions& ingestion_options_;
|
||||
VersionEdit edit_;
|
||||
uint64_t job_start_time_;
|
||||
bool consumed_seqno_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+568
-67
@@ -10,13 +10,15 @@
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class ExternalSSTFileTest : public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
class ExternalSSTFileTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
ExternalSSTFileTest() : DBTestBase("/external_sst_file_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
@@ -28,11 +30,60 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
env_->CreateDir(sst_files_dir_);
|
||||
}
|
||||
|
||||
Status GenerateOneExternalFile(
|
||||
const Options& options, ColumnFamilyHandle* cfh,
|
||||
std::vector<std::pair<std::string, std::string>>& data, int file_id,
|
||||
bool sort_data, std::string* external_file_path,
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
// Generate a file id if not provided
|
||||
if (-1 == file_id) {
|
||||
file_id = (++last_file_id_);
|
||||
}
|
||||
// Sort data if asked to do so
|
||||
if (sort_data) {
|
||||
std::sort(data.begin(), data.end(),
|
||||
[&](const std::pair<std::string, std::string>& e1,
|
||||
const std::pair<std::string, std::string>& e2) {
|
||||
return options.comparator->Compare(e1.first, e2.first) < 0;
|
||||
});
|
||||
auto uniq_iter = std::unique(
|
||||
data.begin(), data.end(),
|
||||
[&](const std::pair<std::string, std::string>& e1,
|
||||
const std::pair<std::string, std::string>& e2) {
|
||||
return options.comparator->Compare(e1.first, e2.first) == 0;
|
||||
});
|
||||
data.resize(uniq_iter - data.begin());
|
||||
}
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, cfh);
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
for (const auto& entry : data) {
|
||||
s = sst_file_writer.Put(entry.first, entry.second);
|
||||
if (!s.ok()) {
|
||||
sst_file_writer.Finish();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s = sst_file_writer.Finish();
|
||||
if (s.ok() && external_file_path != nullptr) {
|
||||
*external_file_path = file_path;
|
||||
}
|
||||
if (s.ok() && nullptr != true_data) {
|
||||
for (const auto& entry : data) {
|
||||
true_data->insert({entry.first, entry.second});
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options,
|
||||
std::vector<std::pair<std::string, std::string>> data, int file_id = -1,
|
||||
bool allow_global_seqno = false, bool write_global_seqno = false,
|
||||
bool sort_data = false,
|
||||
bool verify_checksums_before_ingest = true, bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
// Generate a file id if not provided
|
||||
@@ -76,6 +127,7 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = allow_global_seqno;
|
||||
ifo.write_global_seqno = allow_global_seqno ? write_global_seqno : false;
|
||||
ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
|
||||
if (cfh) {
|
||||
s = db_->IngestExternalFile(cfh, {file_path}, ifo);
|
||||
} else {
|
||||
@@ -152,34 +204,70 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFiles(
|
||||
const Options& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
const std::vector<IngestExternalFileOptions>& ifos,
|
||||
std::vector<std::vector<std::pair<std::string, std::string>>>& data,
|
||||
int file_id, bool sort_data,
|
||||
std::vector<std::map<std::string, std::string>>& true_data) {
|
||||
if (-1 == file_id) {
|
||||
file_id = (++last_file_id_);
|
||||
}
|
||||
// Generate external SST files, one for each column family
|
||||
size_t num_cfs = column_families.size();
|
||||
assert(ifos.size() == num_cfs);
|
||||
assert(data.size() == num_cfs);
|
||||
Status s;
|
||||
std::vector<IngestExternalFileArg> args(num_cfs);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
std::string external_file_path;
|
||||
s = GenerateOneExternalFile(
|
||||
options, column_families[i], data[i], file_id, sort_data,
|
||||
&external_file_path,
|
||||
true_data.size() == num_cfs ? &true_data[i] : nullptr);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
++file_id;
|
||||
|
||||
args[i].column_family = column_families[i];
|
||||
args[i].external_files.push_back(external_file_path);
|
||||
args[i].options = ifos[i];
|
||||
}
|
||||
s = db_->IngestExternalFiles(args);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<std::pair<int, std::string>> data,
|
||||
int file_id = -1, bool allow_global_seqno = false,
|
||||
bool write_global_seqno = false, bool sort_data = false,
|
||||
bool write_global_seqno = false,
|
||||
bool verify_checksums_before_ingest = true, bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
std::vector<std::pair<std::string, std::string>> file_data;
|
||||
for (auto& entry : data) {
|
||||
file_data.emplace_back(Key(entry.first), entry.second);
|
||||
}
|
||||
return GenerateAndAddExternalFile(options, file_data, file_id,
|
||||
allow_global_seqno, write_global_seqno,
|
||||
sort_data, true_data, cfh);
|
||||
return GenerateAndAddExternalFile(
|
||||
options, file_data, file_id, allow_global_seqno, write_global_seqno,
|
||||
verify_checksums_before_ingest, sort_data, true_data, cfh);
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys, int file_id = -1,
|
||||
bool allow_global_seqno = false, bool write_global_seqno = false,
|
||||
bool sort_data = false,
|
||||
bool verify_checksums_before_ingest = true, bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
std::vector<std::pair<std::string, std::string>> file_data;
|
||||
for (auto& k : keys) {
|
||||
file_data.emplace_back(Key(k), Key(k) + ToString(file_id));
|
||||
}
|
||||
return GenerateAndAddExternalFile(options, file_data, file_id,
|
||||
allow_global_seqno, write_global_seqno,
|
||||
sort_data, true_data, cfh);
|
||||
return GenerateAndAddExternalFile(
|
||||
options, file_data, file_id, allow_global_seqno, write_global_seqno,
|
||||
verify_checksums_before_ingest, sort_data, true_data, cfh);
|
||||
}
|
||||
|
||||
Status DeprecatedAddFile(const std::vector<std::string>& files,
|
||||
@@ -195,7 +283,7 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
return db_->IngestExternalFile(files, opts);
|
||||
}
|
||||
|
||||
~ExternalSSTFileTest() { test::DestroyDir(env_, sst_files_dir_); }
|
||||
~ExternalSSTFileTest() override { test::DestroyDir(env_, sst_files_dir_); }
|
||||
|
||||
protected:
|
||||
int last_file_id_ = 0;
|
||||
@@ -483,7 +571,7 @@ class SstFileWriterCollector : public TablePropertiesCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
@@ -497,7 +585,7 @@ class SstFileWriterCollectorFactory : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
explicit SstFileWriterCollectorFactory(std::string prefix)
|
||||
: prefix_(prefix), num_created_(0) {}
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
num_created_++;
|
||||
return new SstFileWriterCollector(prefix_);
|
||||
@@ -1157,13 +1245,13 @@ TEST_P(ExternalSSTFileTest, PickedLevel) {
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
// File 0 will go to last level (L3)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 10}, -1, false, false,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 10}, -1, false, false, true,
|
||||
false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "0,0,0,1");
|
||||
|
||||
// File 1 will go to level L2 (since it overlap with file 0 in L3)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 9}, -1, false, false, false,
|
||||
&true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 9}, -1, false, false, true,
|
||||
false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "0,0,1,1");
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
@@ -1192,13 +1280,13 @@ TEST_P(ExternalSSTFileTest, PickedLevel) {
|
||||
|
||||
// This file overlaps with file 0 (L3), file 1 (L2) and the
|
||||
// output of compaction going to L1
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {4, 7}, -1, false, false, false,
|
||||
&true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {4, 7}, -1, false, false, true,
|
||||
false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5,0,1,1");
|
||||
|
||||
// This file does not overlap with any file or with the running compaction
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, false,
|
||||
false, &true_data));
|
||||
true, false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5,0,1,2");
|
||||
|
||||
// Hold compaction from finishing
|
||||
@@ -1318,7 +1406,7 @@ TEST_F(ExternalSSTFileTest, IngestNonExistingFile) {
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
|
||||
// After full compaction, there should be only 1 file.
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(dbname_, &files);
|
||||
@@ -1429,12 +1517,12 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
// This file overlaps with the output of the compaction (going to L3)
|
||||
// so the file will be added to L0 since L3 is the base level
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {31, 32, 33, 34}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, true, false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5");
|
||||
|
||||
// This file does not overlap with the current running compactiong
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, false,
|
||||
false, &true_data));
|
||||
true, false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5,0,0,1");
|
||||
|
||||
// Hold compaction from finishing
|
||||
@@ -1449,25 +1537,25 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 15, 19}, -1, false, false,
|
||||
false, &true_data));
|
||||
true, false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "1,0,0,3");
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1001, 1002}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, true, false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "1,0,0,4");
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {500, 600, 700}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, true, false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "1,0,0,5");
|
||||
|
||||
// File 5 overlaps with file 2 (L3 / base level)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 10}, -1, false, false,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 10}, -1, false, false, true,
|
||||
false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "2,0,0,5");
|
||||
|
||||
// File 6 overlaps with file 2 (L3 / base level) and file 5 (L0)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 9}, -1, false, false, false,
|
||||
&true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 9}, -1, false, false, true,
|
||||
false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "3,0,0,5");
|
||||
|
||||
// Verify data in files
|
||||
@@ -1486,7 +1574,7 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
|
||||
// File 7 overlaps with file 4 (L3)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {650, 651, 652}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, true, false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "5,0,0,5");
|
||||
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
@@ -1626,7 +1714,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
options.level0_slowdown_writes_trigger = 256;
|
||||
options.level0_stop_writes_trigger = 256;
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
for (int iter = 0; iter < 2; iter++) {
|
||||
bool write_to_memtable = (iter == 0);
|
||||
DestroyAndReopen(options);
|
||||
@@ -1650,9 +1739,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
true_data[entry.first] = entry.second;
|
||||
}
|
||||
} else {
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, random_data, -1, true,
|
||||
write_global_seqno, true,
|
||||
&true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, random_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, true, &true_data));
|
||||
}
|
||||
}
|
||||
size_t kcnt = 0;
|
||||
@@ -1681,9 +1770,11 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
file_data.emplace_back(Key(i), "L4");
|
||||
}
|
||||
bool write_global_seqno = GetParam();
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
|
||||
// This file dont overlap with anything in the DB, will go to L4
|
||||
ASSERT_EQ("0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1693,8 +1784,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 80; i <= 130; i++) {
|
||||
file_data.emplace_back(Key(i), "L0");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
|
||||
// This file overlap with the memtable, so it will flush it and add
|
||||
// it self to L0
|
||||
@@ -1705,8 +1797,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 30; i <= 50; i++) {
|
||||
file_data.emplace_back(Key(i), "L4");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
|
||||
// This file dont overlap with anything in the DB and fit in L4 as well
|
||||
ASSERT_EQ("2,0,0,0,2", FilesPerLevel());
|
||||
@@ -1716,8 +1809,9 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 10; i <= 40; i++) {
|
||||
file_data.emplace_back(Key(i), "L3");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
|
||||
// This file overlap with files in L4, we will ingest it in L3
|
||||
ASSERT_EQ("2,0,0,1,2", FilesPerLevel());
|
||||
@@ -1740,17 +1834,20 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
&entries_in_memtable);
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
// No need for flush
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {90, 100, 110}, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {90, 100, 110}, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
// This file will flush the memtable
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {19, 20, 21}, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {19, 20, 21}, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_EQ(entries_in_memtable, 0);
|
||||
@@ -1764,15 +1861,17 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
// No need for flush, this file keys fit between the memtable keys
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {202, 203, 204}, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {202, 203, 204}, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, &true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
// This file will flush the memtable
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {206, 207}, -1, true, false,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {206, 207}, -1, true, false, write_global_seqno,
|
||||
verify_checksums_before_ingest, &true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_EQ(entries_in_memtable, 0);
|
||||
@@ -1790,13 +1889,16 @@ TEST_P(ExternalSSTFileTest, L0SortingIssue) {
|
||||
ASSERT_OK(Put(Key(1), "memtable"));
|
||||
ASSERT_OK(Put(Key(10), "memtable"));
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
// No Flush needed, No global seqno needed, Ingest in L1
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {7, 8}, -1, true,
|
||||
write_global_seqno, false));
|
||||
write_global_seqno,
|
||||
verify_checksums_before_ingest, false));
|
||||
// No Flush needed, but need a global seqno, Ingest in L0
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {7, 8}, -1, true,
|
||||
write_global_seqno, false));
|
||||
write_global_seqno,
|
||||
verify_checksums_before_ingest, false));
|
||||
printf("%s\n", FilesPerLevel().c_str());
|
||||
|
||||
// Overwrite what we added using external files
|
||||
@@ -2032,11 +2134,12 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
|
||||
options.listeners.emplace_back(listener);
|
||||
CreateAndReopenWithCF({"koko", "toto"}, options);
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
// Ingest into default cf
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
|
||||
write_global_seqno, true, nullptr,
|
||||
handles_[0]));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 2}, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, true, nullptr, handles_[0]));
|
||||
ASSERT_EQ(listener->ingested_files.size(), 1);
|
||||
ASSERT_EQ(listener->ingested_files.back().cf_name, "default");
|
||||
ASSERT_EQ(listener->ingested_files.back().global_seqno, 0);
|
||||
@@ -2046,9 +2149,9 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
|
||||
"default");
|
||||
|
||||
// Ingest into cf1
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
|
||||
write_global_seqno, true, nullptr,
|
||||
handles_[1]));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 2}, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, true, nullptr, handles_[1]));
|
||||
ASSERT_EQ(listener->ingested_files.size(), 2);
|
||||
ASSERT_EQ(listener->ingested_files.back().cf_name, "koko");
|
||||
ASSERT_EQ(listener->ingested_files.back().global_seqno, 0);
|
||||
@@ -2058,9 +2161,9 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
|
||||
"koko");
|
||||
|
||||
// Ingest into cf2
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
|
||||
write_global_seqno, true, nullptr,
|
||||
handles_[2]));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 2}, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, true, nullptr, handles_[2]));
|
||||
ASSERT_EQ(listener->ingested_files.size(), 3);
|
||||
ASSERT_EQ(listener->ingested_files.back().cf_name, "toto");
|
||||
ASSERT_EQ(listener->ingested_files.back().global_seqno, 0);
|
||||
@@ -2126,7 +2229,8 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = true;
|
||||
ifo.ingest_behind = true;
|
||||
ifo.write_global_seqno = GetParam();
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
|
||||
// Can't ingest behind since allow_ingest_behind isn't set to true
|
||||
ASSERT_NOK(GenerateAndAddExternalFileIngestBehind(options, ifo,
|
||||
@@ -2214,8 +2318,405 @@ TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, IngestFileWrittenWithCompressionDictionary) {
|
||||
if (!ZSTD_Supported()) {
|
||||
return;
|
||||
}
|
||||
const int kNumEntries = 1 << 10;
|
||||
const int kNumBytesPerEntry = 1 << 10;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kZSTD;
|
||||
options.compression_opts.max_dict_bytes = 1 << 14; // 16KB
|
||||
options.compression_opts.zstd_max_train_bytes = 1 << 18; // 256KB
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::atomic<int> num_compression_dicts(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WriteCompressionDictBlock:RawDict",
|
||||
[&](void* /* arg */) { ++num_compression_dicts; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
std::vector<std::pair<std::string, std::string>> random_data;
|
||||
for (int i = 0; i < kNumEntries; i++) {
|
||||
std::string val;
|
||||
test::RandomString(&rnd, kNumBytesPerEntry, &val);
|
||||
random_data.emplace_back(Key(i), std::move(val));
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, std::move(random_data)));
|
||||
ASSERT_EQ(1, num_compression_dicts);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_Success) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_injection_env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<ColumnFamilyHandle*> column_families;
|
||||
column_families.push_back(handles_[0]);
|
||||
column_families.push_back(handles_[1]);
|
||||
std::vector<IngestExternalFileOptions> ifos(column_families.size());
|
||||
for (auto& ifo : ifos) {
|
||||
ifo.allow_global_seqno = true; // Always allow global_seqno
|
||||
// May or may not write global_seqno
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
// Whether to verify checksums before ingestion
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string, std::string>>> data;
|
||||
data.push_back(
|
||||
{std::make_pair("foo1", "fv1"), std::make_pair("foo2", "fv2")});
|
||||
data.push_back(
|
||||
{std::make_pair("bar1", "bv1"), std::make_pair("bar2", "bv2")});
|
||||
// Resize the true_data vector upon construction to avoid re-alloc
|
||||
std::vector<std::map<std::string, std::string>> true_data(
|
||||
column_families.size());
|
||||
Status s = GenerateAndAddExternalFiles(options, column_families, ifos, data,
|
||||
-1, true, true_data);
|
||||
ASSERT_OK(s);
|
||||
Close();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
int cf = 0;
|
||||
for (const auto& verify_map : true_data) {
|
||||
for (const auto& elem : verify_map) {
|
||||
const std::string& key = elem.first;
|
||||
const std::string& value = elem.second;
|
||||
ASSERT_EQ(value, Get(cf, key));
|
||||
}
|
||||
++cf;
|
||||
}
|
||||
Close();
|
||||
Destroy(options, true /* delete_cf_paths */);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest,
|
||||
IngestFilesIntoMultipleColumnFamilies_NoMixedStateWithSnapshot) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::IngestExternalFiles:InstallSVForFirstCF:0",
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_MixedState:"
|
||||
"BeforeRead"},
|
||||
{"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_MixedState:"
|
||||
"AfterRead",
|
||||
"DBImpl::IngestExternalFiles:InstallSVForFirstCF:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_injection_env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
const std::vector<std::map<std::string, std::string>> data_before_ingestion =
|
||||
{{{"foo1", "fv1_0"}, {"foo2", "fv2_0"}, {"foo3", "fv3_0"}},
|
||||
{{"bar1", "bv1_0"}, {"bar2", "bv2_0"}, {"bar3", "bv3_0"}}};
|
||||
for (size_t i = 0; i != handles_.size(); ++i) {
|
||||
int cf = static_cast<int>(i);
|
||||
const auto& orig_data = data_before_ingestion[i];
|
||||
for (const auto& kv : orig_data) {
|
||||
ASSERT_OK(Put(cf, kv.first, kv.second));
|
||||
}
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
|
||||
std::vector<ColumnFamilyHandle*> column_families;
|
||||
column_families.push_back(handles_[0]);
|
||||
column_families.push_back(handles_[1]);
|
||||
std::vector<IngestExternalFileOptions> ifos(column_families.size());
|
||||
for (auto& ifo : ifos) {
|
||||
ifo.allow_global_seqno = true; // Always allow global_seqno
|
||||
// May or may not write global_seqno
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
// Whether to verify checksums before ingestion
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string, std::string>>> data;
|
||||
data.push_back(
|
||||
{std::make_pair("foo1", "fv1"), std::make_pair("foo2", "fv2")});
|
||||
data.push_back(
|
||||
{std::make_pair("bar1", "bv1"), std::make_pair("bar2", "bv2")});
|
||||
// Resize the true_data vector upon construction to avoid re-alloc
|
||||
std::vector<std::map<std::string, std::string>> true_data(
|
||||
column_families.size());
|
||||
// Take snapshot before ingestion starts
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
read_opts.snapshot = dbfull()->GetSnapshot();
|
||||
std::vector<Iterator*> iters(handles_.size());
|
||||
|
||||
// Range scan checks first kv of each CF before ingestion starts.
|
||||
for (size_t i = 0; i != handles_.size(); ++i) {
|
||||
iters[i] = dbfull()->NewIterator(read_opts, handles_[i]);
|
||||
iters[i]->SeekToFirst();
|
||||
ASSERT_TRUE(iters[i]->Valid());
|
||||
const std::string& key = iters[i]->key().ToString();
|
||||
const std::string& value = iters[i]->value().ToString();
|
||||
const std::map<std::string, std::string>& orig_data =
|
||||
data_before_ingestion[i];
|
||||
std::map<std::string, std::string>::const_iterator it = orig_data.find(key);
|
||||
ASSERT_NE(orig_data.end(), it);
|
||||
ASSERT_EQ(it->second, value);
|
||||
iters[i]->Next();
|
||||
}
|
||||
port::Thread ingest_thread([&]() {
|
||||
ASSERT_OK(GenerateAndAddExternalFiles(options, column_families, ifos, data,
|
||||
-1, true, true_data));
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_MixedState:"
|
||||
"BeforeRead");
|
||||
// Should see only data before ingestion
|
||||
for (size_t i = 0; i != handles_.size(); ++i) {
|
||||
const auto& orig_data = data_before_ingestion[i];
|
||||
for (; iters[i]->Valid(); iters[i]->Next()) {
|
||||
const std::string& key = iters[i]->key().ToString();
|
||||
const std::string& value = iters[i]->value().ToString();
|
||||
std::map<std::string, std::string>::const_iterator it =
|
||||
orig_data.find(key);
|
||||
ASSERT_NE(orig_data.end(), it);
|
||||
ASSERT_EQ(it->second, value);
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_MixedState:"
|
||||
"AfterRead");
|
||||
ingest_thread.join();
|
||||
for (auto* iter : iters) {
|
||||
delete iter;
|
||||
}
|
||||
iters.clear();
|
||||
dbfull()->ReleaseSnapshot(read_opts.snapshot);
|
||||
|
||||
Close();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
// Should see consistent state after ingestion for all column families even
|
||||
// without snapshot.
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
int cf = 0;
|
||||
for (const auto& verify_map : true_data) {
|
||||
for (const auto& elem : verify_map) {
|
||||
const std::string& key = elem.first;
|
||||
const std::string& value = elem.second;
|
||||
ASSERT_EQ(value, Get(cf, key));
|
||||
}
|
||||
++cf;
|
||||
}
|
||||
Close();
|
||||
Destroy(options, true /* delete_cf_paths */);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_PrepareFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_injection_env.get();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::IngestExternalFiles:BeforeLastJobPrepare:0",
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_PrepareFail:"
|
||||
"0"},
|
||||
{"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies:PrepareFail:"
|
||||
"1",
|
||||
"DBImpl::IngestExternalFiles:BeforeLastJobPrepare:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<ColumnFamilyHandle*> column_families;
|
||||
column_families.push_back(handles_[0]);
|
||||
column_families.push_back(handles_[1]);
|
||||
std::vector<IngestExternalFileOptions> ifos(column_families.size());
|
||||
for (auto& ifo : ifos) {
|
||||
ifo.allow_global_seqno = true; // Always allow global_seqno
|
||||
// May or may not write global_seqno
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
// Whether to verify block checksums before ingest
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string, std::string>>> data;
|
||||
data.push_back(
|
||||
{std::make_pair("foo1", "fv1"), std::make_pair("foo2", "fv2")});
|
||||
data.push_back(
|
||||
{std::make_pair("bar1", "bv1"), std::make_pair("bar2", "bv2")});
|
||||
// Resize the true_data vector upon construction to avoid re-alloc
|
||||
std::vector<std::map<std::string, std::string>> true_data(
|
||||
column_families.size());
|
||||
port::Thread ingest_thread([&]() {
|
||||
Status s = GenerateAndAddExternalFiles(options, column_families, ifos, data,
|
||||
-1, true, true_data);
|
||||
ASSERT_NOK(s);
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_PrepareFail:"
|
||||
"0");
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies:PrepareFail:"
|
||||
"1");
|
||||
ingest_thread.join();
|
||||
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
Close();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
int cf = 0;
|
||||
for (const auto& verify_map : true_data) {
|
||||
for (const auto& elem : verify_map) {
|
||||
const std::string& key = elem.first;
|
||||
ASSERT_EQ("NOT_FOUND", Get(cf, key));
|
||||
}
|
||||
++cf;
|
||||
}
|
||||
Close();
|
||||
Destroy(options, true /* delete_cf_paths */);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_CommitFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_injection_env.get();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::IngestExternalFiles:BeforeJobsRun:0",
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_CommitFail:"
|
||||
"0"},
|
||||
{"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_CommitFail:"
|
||||
"1",
|
||||
"DBImpl::IngestExternalFiles:BeforeJobsRun:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<ColumnFamilyHandle*> column_families;
|
||||
column_families.push_back(handles_[0]);
|
||||
column_families.push_back(handles_[1]);
|
||||
std::vector<IngestExternalFileOptions> ifos(column_families.size());
|
||||
for (auto& ifo : ifos) {
|
||||
ifo.allow_global_seqno = true; // Always allow global_seqno
|
||||
// May or may not write global_seqno
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
// Whether to verify block checksums before ingestion
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string, std::string>>> data;
|
||||
data.push_back(
|
||||
{std::make_pair("foo1", "fv1"), std::make_pair("foo2", "fv2")});
|
||||
data.push_back(
|
||||
{std::make_pair("bar1", "bv1"), std::make_pair("bar2", "bv2")});
|
||||
// Resize the true_data vector upon construction to avoid re-alloc
|
||||
std::vector<std::map<std::string, std::string>> true_data(
|
||||
column_families.size());
|
||||
port::Thread ingest_thread([&]() {
|
||||
Status s = GenerateAndAddExternalFiles(options, column_families, ifos, data,
|
||||
-1, true, true_data);
|
||||
ASSERT_NOK(s);
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_CommitFail:"
|
||||
"0");
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_CommitFail:"
|
||||
"1");
|
||||
ingest_thread.join();
|
||||
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
Close();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
int cf = 0;
|
||||
for (const auto& verify_map : true_data) {
|
||||
for (const auto& elem : verify_map) {
|
||||
const std::string& key = elem.first;
|
||||
ASSERT_EQ("NOT_FOUND", Get(cf, key));
|
||||
}
|
||||
++cf;
|
||||
}
|
||||
Close();
|
||||
Destroy(options, true /* delete_cf_paths */);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest,
|
||||
IngestFilesIntoMultipleColumnFamilies_PartialManifestWriteFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
SyncPoint::GetInstance()->ClearTrace();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"VersionSet::ProcessManifestWrites:BeforeWriteLastVersionEdit:0",
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_"
|
||||
"PartialManifestWriteFail:0"},
|
||||
{"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_"
|
||||
"PartialManifestWriteFail:1",
|
||||
"VersionSet::ProcessManifestWrites:BeforeWriteLastVersionEdit:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<ColumnFamilyHandle*> column_families;
|
||||
column_families.push_back(handles_[0]);
|
||||
column_families.push_back(handles_[1]);
|
||||
std::vector<IngestExternalFileOptions> ifos(column_families.size());
|
||||
for (auto& ifo : ifos) {
|
||||
ifo.allow_global_seqno = true; // Always allow global_seqno
|
||||
// May or may not write global_seqno
|
||||
ifo.write_global_seqno = std::get<0>(GetParam());
|
||||
// Whether to verify block checksums before ingestion
|
||||
ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string, std::string>>> data;
|
||||
data.push_back(
|
||||
{std::make_pair("foo1", "fv1"), std::make_pair("foo2", "fv2")});
|
||||
data.push_back(
|
||||
{std::make_pair("bar1", "bv1"), std::make_pair("bar2", "bv2")});
|
||||
// Resize the true_data vector upon construction to avoid re-alloc
|
||||
std::vector<std::map<std::string, std::string>> true_data(
|
||||
column_families.size());
|
||||
port::Thread ingest_thread([&]() {
|
||||
Status s = GenerateAndAddExternalFiles(options, column_families, ifos, data,
|
||||
-1, true, true_data);
|
||||
ASSERT_NOK(s);
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_"
|
||||
"PartialManifestWriteFail:0");
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT(
|
||||
"ExternalSSTFileTest::IngestFilesIntoMultipleColumnFamilies_"
|
||||
"PartialManifestWriteFail:1");
|
||||
ingest_thread.join();
|
||||
|
||||
fault_injection_env->DropUnsyncedFileData();
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
Close();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
int cf = 0;
|
||||
for (const auto& verify_map : true_data) {
|
||||
for (const auto& elem : verify_map) {
|
||||
const std::string& key = elem.first;
|
||||
ASSERT_EQ("NOT_FOUND", Get(cf, key));
|
||||
}
|
||||
++cf;
|
||||
}
|
||||
Close();
|
||||
Destroy(options, true /* delete_cf_paths */);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Bool());
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(true, true)));
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class FaultInjectionTest
|
||||
env_(nullptr),
|
||||
db_(nullptr) {}
|
||||
|
||||
~FaultInjectionTest() {
|
||||
~FaultInjectionTest() override {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class FileIndexerTest : public testing::Test {
|
||||
FileIndexerTest()
|
||||
: kNumLevels(4), files(new std::vector<FileMetaData*>[kNumLevels]) {}
|
||||
|
||||
~FileIndexerTest() {
|
||||
~FileIndexerTest() override {
|
||||
ClearFiles();
|
||||
delete[] files;
|
||||
}
|
||||
|
||||
+27
-16
@@ -24,14 +24,15 @@
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
#include "db/memtable.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -210,6 +211,8 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
uint64_t prev_fsync_nanos = 0;
|
||||
uint64_t prev_range_sync_nanos = 0;
|
||||
uint64_t prev_prepare_write_nanos = 0;
|
||||
uint64_t prev_cpu_write_nanos = 0;
|
||||
uint64_t prev_cpu_read_nanos = 0;
|
||||
if (measure_io_stats_) {
|
||||
prev_perf_level = GetPerfLevel();
|
||||
SetPerfLevel(PerfLevel::kEnableTime);
|
||||
@@ -217,6 +220,8 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
prev_fsync_nanos = IOSTATS(fsync_nanos);
|
||||
prev_range_sync_nanos = IOSTATS(range_sync_nanos);
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
prev_cpu_write_nanos = IOSTATS(cpu_write_nanos);
|
||||
prev_cpu_read_nanos = IOSTATS(cpu_read_nanos);
|
||||
}
|
||||
|
||||
// This will release and re-acquire the mutex.
|
||||
@@ -268,6 +273,10 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
stream << "file_fsync_nanos" << (IOSTATS(fsync_nanos) - prev_fsync_nanos);
|
||||
stream << "file_prepare_write_nanos"
|
||||
<< (IOSTATS(prepare_write_nanos) - prev_prepare_write_nanos);
|
||||
stream << "file_cpu_write_nanos"
|
||||
<< (IOSTATS(cpu_write_nanos) - prev_cpu_write_nanos);
|
||||
stream << "file_cpu_read_nanos"
|
||||
<< (IOSTATS(cpu_read_nanos) - prev_cpu_read_nanos);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -284,6 +293,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
const uint64_t start_cpu_micros = db_options_.env->NowCPUNanos() / 1000;
|
||||
Status s;
|
||||
{
|
||||
auto write_hint = cfd_->CalculateSSTWriteHint(0);
|
||||
@@ -295,11 +305,13 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// memtable and its associated range deletion memtable, respectively, at
|
||||
// corresponding indexes.
|
||||
std::vector<InternalIterator*> memtables;
|
||||
std::vector<InternalIterator*> range_del_iters;
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Arena arena;
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_data_size = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
for (MemTable* m : mems_) {
|
||||
ROCKS_LOG_INFO(
|
||||
@@ -308,31 +320,29 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
auto* range_del_iter =
|
||||
m->NewRangeTombstoneIterator(ro, versions_->LastSequence());
|
||||
m->NewRangeTombstoneIterator(ro, kMaxSequenceNumber);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.push_back(range_del_iter);
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
total_num_entries += m->num_entries();
|
||||
total_num_deletes += m->num_deletes();
|
||||
total_data_size += m->get_data_size();
|
||||
total_memory_usage += m->ApproximateMemoryUsage();
|
||||
}
|
||||
|
||||
event_logger_->Log()
|
||||
<< "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems_.size() << "num_entries" << total_num_entries
|
||||
<< "num_deletes" << total_num_deletes << "memory_usage"
|
||||
<< total_memory_usage << "flush_reason"
|
||||
<< GetFlushReasonString(cfd_->GetFlushReason());
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems_.size() << "num_entries"
|
||||
<< total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "total_data_size"
|
||||
<< total_data_size << "memory_usage"
|
||||
<< total_memory_usage << "flush_reason"
|
||||
<< GetFlushReasonString(cfd_->GetFlushReason());
|
||||
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(), &memtables[0],
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
std::unique_ptr<InternalIterator> range_del_iter(NewMergingIterator(
|
||||
&cfd_->internal_comparator(),
|
||||
range_del_iters.empty() ? nullptr : &range_del_iters[0],
|
||||
static_cast<int>(range_del_iters.size())));
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
@@ -358,7 +368,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
env_options_, cfd_->table_cache(), iter.get(),
|
||||
std::move(range_del_iter), &meta_, cfd_->internal_comparator(),
|
||||
std::move(range_del_iters), &meta_, cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(), cfd_->GetID(),
|
||||
cfd_->GetName(), existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_,
|
||||
@@ -403,6 +413,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.cpu_micros = db_options_.env->NowCPUNanos() / 1000 - start_cpu_micros;
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
MeasureTime(stats_, FLUSH_TIME, stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, stats);
|
||||
|
||||
+14
-13
@@ -279,7 +279,6 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
*cfd->GetLatestMutableCFOptions(), kMaxSequenceNumber);
|
||||
mem->SetID(i);
|
||||
mem->Ref();
|
||||
mem->TEST_AtomicFlushSequenceNumber() = 123;
|
||||
|
||||
for (size_t j = 0; j != num_keys_per_memtable; ++j) {
|
||||
std::string key(ToString(j + i * num_keys_per_memtable));
|
||||
@@ -309,7 +308,9 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
k++;
|
||||
}
|
||||
HistogramData hist;
|
||||
autovector<FileMetaData> file_metas;
|
||||
std::vector<FileMetaData> file_metas;
|
||||
// Call reserve to avoid auto-resizing
|
||||
file_metas.reserve(flush_jobs.size());
|
||||
mutex_.Lock();
|
||||
for (auto& job : flush_jobs) {
|
||||
job.PickMemTable();
|
||||
@@ -320,23 +321,23 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
ASSERT_OK(job.Run(nullptr /**/, &meta));
|
||||
file_metas.emplace_back(meta);
|
||||
}
|
||||
autovector<FileMetaData*> file_meta_ptrs;
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
for (size_t i = 0; i != all_cfds.size(); ++i) {
|
||||
const auto& mems = flush_jobs[i].GetMemTables();
|
||||
mems_list.push_back(&mems);
|
||||
}
|
||||
autovector<MemTableList*> imm_lists;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
for (auto cfd : all_cfds) {
|
||||
imm_lists.push_back(cfd->imm());
|
||||
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
|
||||
bool atomic_flush_commit_in_progress = false;
|
||||
Status s = MemTableList::TryInstallMemtableFlushResults(
|
||||
imm_lists, all_cfds, mutable_cf_options_list, mems_list,
|
||||
&atomic_flush_commit_in_progress, nullptr /* logs_prep_tracker */,
|
||||
versions_.get(), &mutex_, file_metas, &job_context.memtables_to_free,
|
||||
Status s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, all_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &mutex_, file_meta_ptrs, &job_context.memtables_to_free,
|
||||
nullptr /* db_directory */, nullptr /* log_buffer */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -370,17 +371,17 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
auto new_mem = cfd->ConstructNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
kMaxSequenceNumber);
|
||||
|
||||
std::vector<SequenceNumber> snapshots;
|
||||
std::set<SequenceNumber> snapshots_set;
|
||||
int keys = 10000;
|
||||
int max_inserts_per_keys = 8;
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < keys / 2; ++i) {
|
||||
snapshots.push_back(rnd.Uniform(keys * (max_inserts_per_keys / 2)) + 1);
|
||||
snapshots_set.insert(snapshots.back());
|
||||
snapshots_set.insert(rnd.Uniform(keys * (max_inserts_per_keys / 2)) + 1);
|
||||
}
|
||||
std::sort(snapshots.begin(), snapshots.end());
|
||||
// set has already removed the duplicate snapshots
|
||||
std::vector<SequenceNumber> snapshots(snapshots_set.begin(),
|
||||
snapshots_set.end());
|
||||
|
||||
new_mem->Ref();
|
||||
SequenceNumber current_seqno = 0;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/range_del_aggregator_v2.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
@@ -46,7 +46,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
pinned_iters_mgr_(nullptr),
|
||||
prefix_extractor_(prefix_extractor) {}
|
||||
|
||||
~ForwardLevelIterator() {
|
||||
~ForwardLevelIterator() override {
|
||||
// Reset current pointer
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(file_iter_);
|
||||
@@ -73,8 +73,8 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
delete file_iter_;
|
||||
}
|
||||
|
||||
RangeDelAggregatorV2 range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
file_iter_ = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
*files_[file_index_],
|
||||
@@ -610,8 +610,8 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
// New
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
}
|
||||
RangeDelAggregatorV2 range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
mutable_iter_ = sv_->mem->NewIterator(read_options_, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
@@ -669,8 +669,8 @@ void ForwardIterator::RenewIterators() {
|
||||
|
||||
mutable_iter_ = svnew->mem->NewIterator(read_options_, &arena_);
|
||||
svnew->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
RangeDelAggregatorV2 range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
svnew->mem->NewRangeTombstoneIterator(
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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).
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/in_memory_stats_history.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
InMemoryStatsHistoryIterator::~InMemoryStatsHistoryIterator() {}
|
||||
|
||||
bool InMemoryStatsHistoryIterator::Valid() const { return valid_; }
|
||||
|
||||
Status InMemoryStatsHistoryIterator::status() const { return status_; }
|
||||
|
||||
void InMemoryStatsHistoryIterator::Next() {
|
||||
// increment start_time by 1 to avoid infinite loop
|
||||
AdvanceIteratorByTime(GetStatsTime() + 1, end_time_);
|
||||
}
|
||||
|
||||
uint64_t InMemoryStatsHistoryIterator::GetStatsTime() const { return time_; }
|
||||
|
||||
const std::map<std::string, uint64_t>&
|
||||
InMemoryStatsHistoryIterator::GetStatsMap() const {
|
||||
return stats_map_;
|
||||
}
|
||||
|
||||
// advance the iterator to the next time between [start_time, end_time)
|
||||
// if success, update time_ and stats_map_ with new_time and stats_map
|
||||
void InMemoryStatsHistoryIterator::AdvanceIteratorByTime(uint64_t start_time,
|
||||
uint64_t end_time) {
|
||||
// try to find next entry in stats_history_ map
|
||||
if (db_impl_ != nullptr) {
|
||||
valid_ =
|
||||
db_impl_->FindStatsByTime(start_time, end_time, &time_, &stats_map_);
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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).
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/stats_history.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class InMemoryStatsHistoryIterator final : public StatsHistoryIterator {
|
||||
public:
|
||||
InMemoryStatsHistoryIterator(uint64_t start_time, uint64_t end_time,
|
||||
DBImpl* db_impl)
|
||||
: start_time_(start_time),
|
||||
end_time_(end_time),
|
||||
valid_(true),
|
||||
db_impl_(db_impl) {
|
||||
AdvanceIteratorByTime(start_time_, end_time_);
|
||||
}
|
||||
~InMemoryStatsHistoryIterator() override;
|
||||
bool Valid() const override;
|
||||
Status status() const override;
|
||||
|
||||
void Next() override;
|
||||
uint64_t GetStatsTime() const override;
|
||||
|
||||
const std::map<std::string, uint64_t>& GetStatsMap() const override;
|
||||
|
||||
private:
|
||||
// advance the iterator to the next stats history record with timestamp
|
||||
// between [start_time, end_time)
|
||||
void AdvanceIteratorByTime(uint64_t start_time, uint64_t end_time);
|
||||
|
||||
// No copying allowed
|
||||
InMemoryStatsHistoryIterator(const InMemoryStatsHistoryIterator&) = delete;
|
||||
void operator=(const InMemoryStatsHistoryIterator&) = delete;
|
||||
InMemoryStatsHistoryIterator(InMemoryStatsHistoryIterator&&) = delete;
|
||||
InMemoryStatsHistoryIterator& operator=(InMemoryStatsHistoryIterator&&) =
|
||||
delete;
|
||||
|
||||
uint64_t time_;
|
||||
uint64_t start_time_;
|
||||
uint64_t end_time_;
|
||||
std::map<std::string, uint64_t> stats_map_;
|
||||
Status status_;
|
||||
bool valid_;
|
||||
DBImpl* db_impl_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+10
-5
@@ -45,6 +45,8 @@ const std::map<LevelStatType, LevelStat> InternalStats::compaction_level_stats =
|
||||
{LevelStatType::READ_MBPS, LevelStat{"ReadMBps", "Rd(MB/s)"}},
|
||||
{LevelStatType::WRITE_MBPS, LevelStat{"WriteMBps", "Wr(MB/s)"}},
|
||||
{LevelStatType::COMP_SEC, LevelStat{"CompSec", "Comp(sec)"}},
|
||||
{LevelStatType::COMP_CPU_SEC,
|
||||
LevelStat{"CompMergeCPU", "CompMergeCPU(sec)"}},
|
||||
{LevelStatType::COMP_COUNT, LevelStat{"CompCount", "Comp(cnt)"}},
|
||||
{LevelStatType::AVG_SEC, LevelStat{"AvgSec", "Avg(sec)"}},
|
||||
{LevelStatType::KEY_IN, LevelStat{"KeyIn", "KeyIn"}},
|
||||
@@ -64,7 +66,7 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name) {
|
||||
};
|
||||
int line_size = snprintf(
|
||||
buf + written_size, len - written_size,
|
||||
"Level %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
|
||||
"Level %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
|
||||
// Note that we skip COMPACTED_FILES and merge it with Files column
|
||||
hdr(LevelStatType::NUM_FILES), hdr(LevelStatType::SIZE_BYTES),
|
||||
hdr(LevelStatType::SCORE), hdr(LevelStatType::READ_GB),
|
||||
@@ -72,9 +74,9 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name) {
|
||||
hdr(LevelStatType::WRITE_GB), hdr(LevelStatType::W_NEW_GB),
|
||||
hdr(LevelStatType::MOVED_GB), hdr(LevelStatType::WRITE_AMP),
|
||||
hdr(LevelStatType::READ_MBPS), hdr(LevelStatType::WRITE_MBPS),
|
||||
hdr(LevelStatType::COMP_SEC), hdr(LevelStatType::COMP_COUNT),
|
||||
hdr(LevelStatType::AVG_SEC), hdr(LevelStatType::KEY_IN),
|
||||
hdr(LevelStatType::KEY_DROP));
|
||||
hdr(LevelStatType::COMP_SEC), hdr(LevelStatType::COMP_CPU_SEC),
|
||||
hdr(LevelStatType::COMP_COUNT), hdr(LevelStatType::AVG_SEC),
|
||||
hdr(LevelStatType::KEY_IN), hdr(LevelStatType::KEY_DROP));
|
||||
|
||||
written_size += line_size;
|
||||
snprintf(buf + written_size, len - written_size, "%s\n",
|
||||
@@ -106,6 +108,7 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
(*level_stats)[LevelStatType::WRITE_MBPS] =
|
||||
stats.bytes_written / kMB / elapsed;
|
||||
(*level_stats)[LevelStatType::COMP_SEC] = stats.micros / kMicrosInSec;
|
||||
(*level_stats)[LevelStatType::COMP_CPU_SEC] = stats.cpu_micros / kMicrosInSec;
|
||||
(*level_stats)[LevelStatType::COMP_COUNT] = stats.count;
|
||||
(*level_stats)[LevelStatType::AVG_SEC] =
|
||||
stats.count == 0 ? 0 : stats.micros / kMicrosInSec / stats.count;
|
||||
@@ -132,7 +135,8 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
"%5.1f " /* W-Amp */
|
||||
"%8.1f " /* Rd(MB/s) */
|
||||
"%8.1f " /* Wr(MB/s) */
|
||||
"%9.0f " /* Comp(sec) */
|
||||
"%9.2f " /* Comp(sec) */
|
||||
"%17.2f " /* CompMergeCPU(sec) */
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%7s " /* KeyIn */
|
||||
@@ -153,6 +157,7 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
stat_value.at(LevelStatType::READ_MBPS),
|
||||
stat_value.at(LevelStatType::WRITE_MBPS),
|
||||
stat_value.at(LevelStatType::COMP_SEC),
|
||||
stat_value.at(LevelStatType::COMP_CPU_SEC),
|
||||
static_cast<int>(stat_value.at(LevelStatType::COMP_COUNT)),
|
||||
stat_value.at(LevelStatType::AVG_SEC),
|
||||
NumberToHumanString(
|
||||
|
||||
+10
-2
@@ -71,6 +71,7 @@ enum class LevelStatType {
|
||||
READ_MBPS,
|
||||
WRITE_MBPS,
|
||||
COMP_SEC,
|
||||
COMP_CPU_SEC,
|
||||
COMP_COUNT,
|
||||
AVG_SEC,
|
||||
KEY_IN,
|
||||
@@ -135,6 +136,7 @@ class InternalStats {
|
||||
// compactions that produced data for the specified "level".
|
||||
struct CompactionStats {
|
||||
uint64_t micros;
|
||||
uint64_t cpu_micros;
|
||||
|
||||
// The number of bytes read from all non-output levels
|
||||
uint64_t bytes_read_non_output_levels;
|
||||
@@ -172,6 +174,7 @@ class InternalStats {
|
||||
|
||||
explicit CompactionStats()
|
||||
: micros(0),
|
||||
cpu_micros(0),
|
||||
bytes_read_non_output_levels(0),
|
||||
bytes_read_output_level(0),
|
||||
bytes_written(0),
|
||||
@@ -190,6 +193,7 @@ class InternalStats {
|
||||
|
||||
explicit CompactionStats(CompactionReason reason, int c)
|
||||
: micros(0),
|
||||
cpu_micros(0),
|
||||
bytes_read_non_output_levels(0),
|
||||
bytes_read_output_level(0),
|
||||
bytes_written(0),
|
||||
@@ -214,14 +218,14 @@ class InternalStats {
|
||||
|
||||
explicit CompactionStats(const CompactionStats& c)
|
||||
: micros(c.micros),
|
||||
cpu_micros(c.cpu_micros),
|
||||
bytes_read_non_output_levels(c.bytes_read_non_output_levels),
|
||||
bytes_read_output_level(c.bytes_read_output_level),
|
||||
bytes_written(c.bytes_written),
|
||||
bytes_moved(c.bytes_moved),
|
||||
num_input_files_in_non_output_levels(
|
||||
c.num_input_files_in_non_output_levels),
|
||||
num_input_files_in_output_level(
|
||||
c.num_input_files_in_output_level),
|
||||
num_input_files_in_output_level(c.num_input_files_in_output_level),
|
||||
num_output_files(c.num_output_files),
|
||||
num_input_records(c.num_input_records),
|
||||
num_dropped_records(c.num_dropped_records),
|
||||
@@ -234,6 +238,7 @@ class InternalStats {
|
||||
|
||||
void Clear() {
|
||||
this->micros = 0;
|
||||
this->cpu_micros = 0;
|
||||
this->bytes_read_non_output_levels = 0;
|
||||
this->bytes_read_output_level = 0;
|
||||
this->bytes_written = 0;
|
||||
@@ -252,6 +257,7 @@ class InternalStats {
|
||||
|
||||
void Add(const CompactionStats& c) {
|
||||
this->micros += c.micros;
|
||||
this->cpu_micros += c.cpu_micros;
|
||||
this->bytes_read_non_output_levels += c.bytes_read_non_output_levels;
|
||||
this->bytes_read_output_level += c.bytes_read_output_level;
|
||||
this->bytes_written += c.bytes_written;
|
||||
@@ -272,6 +278,7 @@ class InternalStats {
|
||||
|
||||
void Subtract(const CompactionStats& c) {
|
||||
this->micros -= c.micros;
|
||||
this->cpu_micros -= c.cpu_micros;
|
||||
this->bytes_read_non_output_levels -= c.bytes_read_non_output_levels;
|
||||
this->bytes_read_output_level -= c.bytes_read_output_level;
|
||||
this->bytes_written -= c.bytes_written;
|
||||
@@ -595,6 +602,7 @@ class InternalStats {
|
||||
|
||||
struct CompactionStats {
|
||||
uint64_t micros;
|
||||
uint64_t cpu_micros;
|
||||
uint64_t bytes_read_non_output_levels;
|
||||
uint64_t bytes_read_output_level;
|
||||
uint64_t bytes_written;
|
||||
|
||||
@@ -175,6 +175,9 @@ struct JobContext {
|
||||
size_t num_alive_log_files = 0;
|
||||
uint64_t size_log_to_delete = 0;
|
||||
|
||||
// Snapshot taken before flush/compaction job.
|
||||
std::unique_ptr<ManagedSnapshot> job_snapshot;
|
||||
|
||||
explicit JobContext(int _job_id, bool create_superversion = false) {
|
||||
job_id = _job_id;
|
||||
manifest_file_number = 0;
|
||||
@@ -204,6 +207,7 @@ struct JobContext {
|
||||
|
||||
memtables_to_free.clear();
|
||||
logs_to_free.clear();
|
||||
job_snapshot.reset();
|
||||
}
|
||||
|
||||
~JobContext() {
|
||||
|
||||
+18
-11
@@ -46,22 +46,20 @@ class EventListenerTest : public DBTestBase {
|
||||
};
|
||||
|
||||
struct TestPropertiesCollector : public rocksdb::TablePropertiesCollector {
|
||||
virtual rocksdb::Status AddUserKey(const rocksdb::Slice& /*key*/,
|
||||
const rocksdb::Slice& /*value*/,
|
||||
rocksdb::EntryType /*type*/,
|
||||
rocksdb::SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
rocksdb::Status AddUserKey(const rocksdb::Slice& /*key*/,
|
||||
const rocksdb::Slice& /*value*/,
|
||||
rocksdb::EntryType /*type*/,
|
||||
rocksdb::SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
virtual rocksdb::Status Finish(
|
||||
rocksdb::Status Finish(
|
||||
rocksdb::UserCollectedProperties* properties) override {
|
||||
properties->insert({"0", "1"});
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "TestTablePropertiesCollector";
|
||||
}
|
||||
const char* Name() const override { return "TestTablePropertiesCollector"; }
|
||||
|
||||
rocksdb::UserCollectedProperties GetReadableProperties() const override {
|
||||
rocksdb::UserCollectedProperties ret;
|
||||
@@ -72,7 +70,7 @@ struct TestPropertiesCollector : public rocksdb::TablePropertiesCollector {
|
||||
|
||||
class TestPropertiesCollectorFactory : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
return new TestPropertiesCollector;
|
||||
}
|
||||
@@ -603,7 +601,7 @@ class TableFileCreationListener : public EventListener {
|
||||
|
||||
Status NewWritableFile(const std::string& fname,
|
||||
std::unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& options) {
|
||||
const EnvOptions& options) override {
|
||||
if (fname.size() > 4 && fname.substr(fname.size() - 4) == ".sst") {
|
||||
if (!status_.ok()) {
|
||||
return status_;
|
||||
@@ -905,6 +903,7 @@ class TestFileOperationListener : public EventListener {
|
||||
if (info.status.ok()) {
|
||||
++file_reads_success_;
|
||||
}
|
||||
ReportDuration(info);
|
||||
}
|
||||
|
||||
void OnFileWriteFinish(const FileOperationInfo& info) override {
|
||||
@@ -912,6 +911,7 @@ class TestFileOperationListener : public EventListener {
|
||||
if (info.status.ok()) {
|
||||
++file_writes_success_;
|
||||
}
|
||||
ReportDuration(info);
|
||||
}
|
||||
|
||||
bool ShouldBeNotifiedOnFileIO() override { return true; }
|
||||
@@ -920,6 +920,13 @@ class TestFileOperationListener : public EventListener {
|
||||
std::atomic<size_t> file_reads_success_;
|
||||
std::atomic<size_t> file_writes_;
|
||||
std::atomic<size_t> file_writes_success_;
|
||||
|
||||
private:
|
||||
void ReportDuration(const FileOperationInfo& info) const {
|
||||
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
info.finish_timestamp - info.start_timestamp);
|
||||
ASSERT_GT(duration.count(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, OnFileOperationTest) {
|
||||
|
||||
+4
-4
@@ -61,7 +61,7 @@ class LogTest : public ::testing::TestWithParam<int> {
|
||||
force_eof_position_(0),
|
||||
returned_partial_(false) { }
|
||||
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
EXPECT_TRUE(!returned_partial_) << "must not Read() after eof/error";
|
||||
|
||||
if (force_error_) {
|
||||
@@ -100,7 +100,7 @@ class LogTest : public ::testing::TestWithParam<int> {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status Skip(uint64_t n) override {
|
||||
Status Skip(uint64_t n) override {
|
||||
if (n > contents_.size()) {
|
||||
contents_.clear();
|
||||
return Status::NotFound("in-memory file skipepd past end");
|
||||
@@ -118,7 +118,7 @@ class LogTest : public ::testing::TestWithParam<int> {
|
||||
std::string message_;
|
||||
|
||||
ReportCollector() : dropped_bytes_(0) { }
|
||||
virtual void Corruption(size_t bytes, const Status& status) override {
|
||||
void Corruption(size_t bytes, const Status& status) override {
|
||||
dropped_bytes_ += bytes;
|
||||
message_.append(status.ToString());
|
||||
}
|
||||
@@ -661,7 +661,7 @@ class RetriableLogTest : public ::testing::TestWithParam<int> {
|
||||
std::string message_;
|
||||
|
||||
ReportCollector() : dropped_bytes_(0) {}
|
||||
virtual void Corruption(size_t bytes, const Status& status) override {
|
||||
void Corruption(size_t bytes, const Status& status) override {
|
||||
dropped_bytes_ += bytes;
|
||||
message_.append(status.ToString());
|
||||
}
|
||||
|
||||
+8
-9
@@ -13,17 +13,16 @@
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
|
||||
#include "port/jemalloc_helper.h"
|
||||
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
#ifdef __FreeBSD__
|
||||
#include <malloc_np.h>
|
||||
#else
|
||||
#include "jemalloc/jemalloc.h"
|
||||
|
||||
#ifdef JEMALLOC_NO_RENAME
|
||||
#define malloc_stats_print je_malloc_stats_print
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* cur;
|
||||
@@ -41,10 +40,10 @@ static void GetJemallocStatus(void* mstat_arg, const char* status) {
|
||||
snprintf(mstat->cur, buf_size, "%s", status);
|
||||
mstat->cur += status_len;
|
||||
}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
void DumpMallocStats(std::string* stats) {
|
||||
if (!HasJemalloc()) {
|
||||
return;
|
||||
}
|
||||
MallocStatus mstat;
|
||||
const unsigned int kMallocStatusLen = 1000000;
|
||||
std::unique_ptr<char[]> buf{new char[kMallocStatusLen + 1]};
|
||||
@@ -56,5 +55,5 @@ void DumpMallocStats(std::string* stats) {
|
||||
#else
|
||||
void DumpMallocStats(std::string*) {}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
}
|
||||
} // namespace rocksdb
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -51,15 +51,13 @@ class DestroyAllCompactionFilter : public CompactionFilter {
|
||||
public:
|
||||
DestroyAllCompactionFilter() {}
|
||||
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& existing_value,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return existing_value.ToString() == "destroy";
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "DestroyAllCompactionFilter";
|
||||
}
|
||||
const char* Name() const override { return "DestroyAllCompactionFilter"; }
|
||||
};
|
||||
|
||||
TEST_F(ManualCompactionTest, CompactTouchesAllKeys) {
|
||||
|
||||
+56
-39
@@ -51,6 +51,8 @@ ImmutableMemTableOptions::ImmutableMemTableOptions(
|
||||
mutable_cf_options.memtable_prefix_bloom_size_ratio) *
|
||||
8u),
|
||||
memtable_huge_page_size(mutable_cf_options.memtable_huge_page_size),
|
||||
memtable_whole_key_filtering(
|
||||
mutable_cf_options.memtable_whole_key_filtering),
|
||||
inplace_update_support(ioptions.inplace_update_support),
|
||||
inplace_update_num_locks(mutable_cf_options.inplace_update_num_locks),
|
||||
inplace_callback(ioptions.inplace_callback),
|
||||
@@ -109,11 +111,13 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
// something went wrong if we need to flush before inserting anything
|
||||
assert(!ShouldScheduleFlush());
|
||||
|
||||
if (prefix_extractor_ && moptions_.memtable_prefix_bloom_bits > 0) {
|
||||
prefix_bloom_.reset(new DynamicBloom(
|
||||
&arena_, moptions_.memtable_prefix_bloom_bits, ioptions.bloom_locality,
|
||||
6 /* hard coded 6 probes */, nullptr, moptions_.memtable_huge_page_size,
|
||||
ioptions.info_log));
|
||||
// use bloom_filter_ for both whole key and prefix bloom filter
|
||||
if ((prefix_extractor_ || moptions_.memtable_whole_key_filtering) &&
|
||||
moptions_.memtable_prefix_bloom_bits > 0) {
|
||||
bloom_filter_.reset(
|
||||
new DynamicBloom(&arena_, moptions_.memtable_prefix_bloom_bits,
|
||||
ioptions.bloom_locality, 6 /* hard coded 6 probes */,
|
||||
moptions_.memtable_huge_page_size, ioptions.info_log));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,14 +286,14 @@ class MemTableIterator : public InternalIterator {
|
||||
if (use_range_del_table) {
|
||||
iter_ = mem.range_del_table_->GetIterator(arena);
|
||||
} else if (prefix_extractor_ != nullptr && !read_options.total_order_seek) {
|
||||
bloom_ = mem.prefix_bloom_.get();
|
||||
bloom_ = mem.bloom_filter_.get();
|
||||
iter_ = mem.table_->GetDynamicPrefixIterator(arena);
|
||||
} else {
|
||||
iter_ = mem.table_->GetIterator(arena);
|
||||
}
|
||||
}
|
||||
|
||||
~MemTableIterator() {
|
||||
~MemTableIterator() override {
|
||||
#ifndef NDEBUG
|
||||
// Assert that the MemTableIterator is never deleted while
|
||||
// Pinning is Enabled.
|
||||
@@ -303,18 +307,18 @@ class MemTableIterator : public InternalIterator {
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
virtual void SetPinnedItersMgr(
|
||||
PinnedIteratorsManager* pinned_iters_mgr) override {
|
||||
void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override {
|
||||
pinned_iters_mgr_ = pinned_iters_mgr;
|
||||
}
|
||||
PinnedIteratorsManager* pinned_iters_mgr_ = nullptr;
|
||||
#endif
|
||||
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual void Seek(const Slice& k) override {
|
||||
bool Valid() const override { return valid_; }
|
||||
void Seek(const Slice& k) override {
|
||||
PERF_TIMER_GUARD(seek_on_memtable_time);
|
||||
PERF_COUNTER_ADD(seek_on_memtable_count, 1);
|
||||
if (bloom_ != nullptr) {
|
||||
if (bloom_) {
|
||||
// iterator should only use prefix bloom filter
|
||||
if (!bloom_->MayContain(
|
||||
prefix_extractor_->Transform(ExtractUserKey(k)))) {
|
||||
PERF_COUNTER_ADD(bloom_memtable_miss_count, 1);
|
||||
@@ -327,10 +331,10 @@ class MemTableIterator : public InternalIterator {
|
||||
iter_->Seek(k, nullptr);
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
void SeekForPrev(const Slice& k) override {
|
||||
PERF_TIMER_GUARD(seek_on_memtable_time);
|
||||
PERF_COUNTER_ADD(seek_on_memtable_count, 1);
|
||||
if (bloom_ != nullptr) {
|
||||
if (bloom_) {
|
||||
if (!bloom_->MayContain(
|
||||
prefix_extractor_->Transform(ExtractUserKey(k)))) {
|
||||
PERF_COUNTER_ADD(bloom_memtable_miss_count, 1);
|
||||
@@ -349,44 +353,44 @@ class MemTableIterator : public InternalIterator {
|
||||
Prev();
|
||||
}
|
||||
}
|
||||
virtual void SeekToFirst() override {
|
||||
void SeekToFirst() override {
|
||||
iter_->SeekToFirst();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void SeekToLast() override {
|
||||
void SeekToLast() override {
|
||||
iter_->SeekToLast();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void Next() override {
|
||||
void Next() override {
|
||||
PERF_COUNTER_ADD(next_on_memtable_count, 1);
|
||||
assert(Valid());
|
||||
iter_->Next();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void Prev() override {
|
||||
void Prev() override {
|
||||
PERF_COUNTER_ADD(prev_on_memtable_count, 1);
|
||||
assert(Valid());
|
||||
iter_->Prev();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual Slice key() const override {
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return GetLengthPrefixedSlice(iter_->key());
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
Slice key_slice = GetLengthPrefixedSlice(iter_->key());
|
||||
return GetLengthPrefixedSlice(key_slice.data() + key_slice.size());
|
||||
}
|
||||
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
virtual bool IsKeyPinned() const override {
|
||||
bool IsKeyPinned() const override {
|
||||
// memtable data is always pinned
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool IsValuePinned() const override {
|
||||
bool IsValuePinned() const override {
|
||||
// memtable value is always pinned, except if we allow inplace update.
|
||||
return value_pinned_;
|
||||
}
|
||||
@@ -414,7 +418,8 @@ InternalIterator* MemTable::NewIterator(const ReadOptions& read_options,
|
||||
|
||||
FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options, SequenceNumber read_seq) {
|
||||
if (read_options.ignore_range_deletions || is_range_del_table_empty_) {
|
||||
if (read_options.ignore_range_deletions ||
|
||||
is_range_del_table_empty_.load(std::memory_order_relaxed)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto* unfragmented_iter = new MemTableIterator(
|
||||
@@ -428,7 +433,7 @@ FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIterator(
|
||||
comparator_.comparator);
|
||||
|
||||
auto* fragmented_iter = new FragmentedRangeTombstoneIterator(
|
||||
fragmented_tombstone_list, read_seq, comparator_.comparator);
|
||||
fragmented_tombstone_list, comparator_.comparator, read_seq);
|
||||
return fragmented_iter;
|
||||
}
|
||||
|
||||
@@ -515,9 +520,11 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (prefix_bloom_) {
|
||||
assert(prefix_extractor_);
|
||||
prefix_bloom_->Add(prefix_extractor_->Transform(key));
|
||||
if (bloom_filter_ && prefix_extractor_) {
|
||||
bloom_filter_->Add(prefix_extractor_->Transform(key));
|
||||
}
|
||||
if (bloom_filter_ && moptions_.memtable_whole_key_filtering) {
|
||||
bloom_filter_->Add(key);
|
||||
}
|
||||
|
||||
// The first sequence number inserted into the memtable
|
||||
@@ -546,9 +553,11 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
post_process_info->num_deletes++;
|
||||
}
|
||||
|
||||
if (prefix_bloom_) {
|
||||
assert(prefix_extractor_);
|
||||
prefix_bloom_->AddConcurrently(prefix_extractor_->Transform(key));
|
||||
if (bloom_filter_ && prefix_extractor_) {
|
||||
bloom_filter_->AddConcurrently(prefix_extractor_->Transform(key));
|
||||
}
|
||||
if (bloom_filter_ && moptions_.memtable_whole_key_filtering) {
|
||||
bloom_filter_->AddConcurrently(key);
|
||||
}
|
||||
|
||||
// atomically update first_seqno_ and earliest_seqno_.
|
||||
@@ -563,8 +572,8 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
!first_seqno_.compare_exchange_weak(cur_earliest_seqno, s)) {
|
||||
}
|
||||
}
|
||||
if (is_range_del_table_empty_ && type == kTypeRangeDeletion) {
|
||||
is_range_del_table_empty_ = false;
|
||||
if (type == kTypeRangeDeletion) {
|
||||
is_range_del_table_empty_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
UpdateOldestKeyTime();
|
||||
return true;
|
||||
@@ -755,16 +764,24 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
Slice user_key = key.user_key();
|
||||
bool found_final_value = false;
|
||||
bool merge_in_progress = s->IsMergeInProgress();
|
||||
bool const may_contain =
|
||||
nullptr == prefix_bloom_
|
||||
? false
|
||||
: prefix_bloom_->MayContain(prefix_extractor_->Transform(user_key));
|
||||
if (prefix_bloom_ && !may_contain) {
|
||||
bool may_contain = true;
|
||||
if (bloom_filter_) {
|
||||
// when both memtable_whole_key_filtering and prefix_extractor_ are set,
|
||||
// only do whole key filtering for Get() to save CPU
|
||||
if (moptions_.memtable_whole_key_filtering) {
|
||||
may_contain = bloom_filter_->MayContain(user_key);
|
||||
} else {
|
||||
assert(prefix_extractor_);
|
||||
may_contain =
|
||||
bloom_filter_->MayContain(prefix_extractor_->Transform(user_key));
|
||||
}
|
||||
}
|
||||
if (bloom_filter_ && !may_contain) {
|
||||
// iter is null if prefix bloom says the key does not exist
|
||||
PERF_COUNTER_ADD(bloom_memtable_miss_count, 1);
|
||||
*seq = kMaxSequenceNumber;
|
||||
} else {
|
||||
if (prefix_bloom_) {
|
||||
if (bloom_filter_) {
|
||||
PERF_COUNTER_ADD(bloom_memtable_hit_count, 1);
|
||||
}
|
||||
Saver saver;
|
||||
|
||||
+16
-9
@@ -41,6 +41,7 @@ struct ImmutableMemTableOptions {
|
||||
size_t arena_block_size;
|
||||
uint32_t memtable_prefix_bloom_bits;
|
||||
size_t memtable_huge_page_size;
|
||||
bool memtable_whole_key_filtering;
|
||||
bool inplace_update_support;
|
||||
size_t inplace_update_num_locks;
|
||||
UpdateStatus (*inplace_callback)(char* existing_value,
|
||||
@@ -265,12 +266,16 @@ class MemTable {
|
||||
return num_deletes_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
uint64_t get_data_size() const {
|
||||
return data_size_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Dynamically change the memtable's capacity. If set below the current usage,
|
||||
// the next key added will trigger a flush. Can only increase size when
|
||||
// memtable prefix bloom is disabled, since we can't easily allocate more
|
||||
// space.
|
||||
void UpdateWriteBufferSize(size_t new_write_buffer_size) {
|
||||
if (prefix_bloom_ == nullptr ||
|
||||
if (bloom_filter_ == nullptr ||
|
||||
new_write_buffer_size < write_buffer_size_) {
|
||||
write_buffer_size_.store(new_write_buffer_size,
|
||||
std::memory_order_relaxed);
|
||||
@@ -386,14 +391,16 @@ class MemTable {
|
||||
|
||||
uint64_t GetID() const { return id_; }
|
||||
|
||||
SequenceNumber& TEST_AtomicFlushSequenceNumber() {
|
||||
return atomic_flush_seqno_;
|
||||
void SetFlushCompleted(bool completed) { flush_completed_ = completed; }
|
||||
|
||||
uint64_t GetFileNumber() const { return file_number_; }
|
||||
|
||||
void SetFileNumber(uint64_t file_num) { file_number_ = file_num; }
|
||||
|
||||
void SetFlushInProgress(bool in_progress) {
|
||||
flush_in_progress_ = in_progress;
|
||||
}
|
||||
|
||||
void TEST_SetFlushCompleted(bool completed) { flush_completed_ = completed; }
|
||||
|
||||
void TEST_SetFileNumber(uint64_t file_num) { file_number_ = file_num; }
|
||||
|
||||
private:
|
||||
enum FlushStateEnum { FLUSH_NOT_REQUESTED, FLUSH_REQUESTED, FLUSH_SCHEDULED };
|
||||
|
||||
@@ -409,7 +416,7 @@ class MemTable {
|
||||
ConcurrentArena arena_;
|
||||
std::unique_ptr<MemTableRep> table_;
|
||||
std::unique_ptr<MemTableRep> range_del_table_;
|
||||
bool is_range_del_table_empty_;
|
||||
std::atomic_bool is_range_del_table_empty_;
|
||||
|
||||
// Total data size of all data inserted
|
||||
std::atomic<uint64_t> data_size_;
|
||||
@@ -448,7 +455,7 @@ class MemTable {
|
||||
std::vector<port::RWMutex> locks_;
|
||||
|
||||
const SliceTransform* const prefix_extractor_;
|
||||
std::unique_ptr<DynamicBloom> prefix_bloom_;
|
||||
std::unique_ptr<DynamicBloom> bloom_filter_;
|
||||
|
||||
std::atomic<FlushStateEnum> flush_state_;
|
||||
|
||||
|
||||
+106
-223
@@ -159,7 +159,7 @@ bool MemTableListVersion::GetFromList(
|
||||
|
||||
Status MemTableListVersion::AddRangeTombstoneIterators(
|
||||
const ReadOptions& read_opts, Arena* /*arena*/,
|
||||
RangeDelAggregatorV2* range_del_agg) {
|
||||
RangeDelAggregator* range_del_agg) {
|
||||
assert(range_del_agg != nullptr);
|
||||
for (auto& m : memlist_) {
|
||||
// Using kMaxSequenceNumber is OK because these are immutable memtables.
|
||||
@@ -260,228 +260,6 @@ void MemTableListVersion::TrimHistory(autovector<MemTable*>* to_delete) {
|
||||
}
|
||||
}
|
||||
|
||||
// Try to record multiple successful flush to the MANIFEST as an atomic unit.
|
||||
// This function may just return Status::OK if there has already been
|
||||
// a concurrent thread performing actual recording.
|
||||
Status MemTableList::TryInstallMemtableFlushResults(
|
||||
autovector<MemTableList*>& imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
bool* atomic_flush_commit_in_progress, LogsWithPrepTracker* prep_tracker,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData>& file_metas,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
|
||||
for (size_t k = 0; k != mems_list.size(); ++k) {
|
||||
for (size_t i = 0; i != mems_list[k]->size(); ++i) {
|
||||
assert(i == 0 || (*mems_list[k])[i]->GetEdits()->NumEntries() == 0);
|
||||
(*mems_list[k])[i]->flush_completed_ = true;
|
||||
(*mems_list[k])[i]->file_number_ = file_metas[k].fd.GetNumber();
|
||||
}
|
||||
}
|
||||
|
||||
assert(atomic_flush_commit_in_progress != nullptr);
|
||||
Status s;
|
||||
if (*atomic_flush_commit_in_progress) {
|
||||
// If the function reaches here, there must be a concurrent thread that
|
||||
// have already started recording to MANIFEST. Therefore we should just
|
||||
// return Status::OK and let the othe thread finish writing to MANIFEST on
|
||||
// our behalf.
|
||||
return s;
|
||||
}
|
||||
|
||||
// If the function reaches here, the current thread will start writing to
|
||||
// MANIFEST. It may record to MANIFEST the flush results of other flushes.
|
||||
*atomic_flush_commit_in_progress = true;
|
||||
|
||||
auto comp = [&imm_lists](size_t lh, size_t rh) {
|
||||
const auto& memlist1 = imm_lists[lh]->current_->memlist_;
|
||||
const auto& memlist2 = imm_lists[rh]->current_->memlist_;
|
||||
auto it1 = memlist1.rbegin();
|
||||
auto it2 = memlist2.rbegin();
|
||||
return (*it1)->atomic_flush_seqno_ > (*it2)->atomic_flush_seqno_;
|
||||
};
|
||||
// The top of the heap is the memtable with smallest atomic_flush_seqno_.
|
||||
std::priority_queue<size_t, std::vector<size_t>, decltype(comp)> heap(comp);
|
||||
// Sequence number of the oldest unfinished atomic flush.
|
||||
SequenceNumber min_unfinished_seqno = kMaxSequenceNumber;
|
||||
// Populate the heap with first element of each imm iff. it has been
|
||||
// flushed to storage, i.e. flush_completed_ is true.
|
||||
size_t num = imm_lists.size();
|
||||
assert(num == cfds.size());
|
||||
for (size_t i = 0; i != num; ++i) {
|
||||
std::list<MemTable*>& memlist = imm_lists[i]->current_->memlist_;
|
||||
if (memlist.empty()) {
|
||||
continue;
|
||||
}
|
||||
auto it = memlist.rbegin();
|
||||
if ((*it)->flush_completed_) {
|
||||
heap.emplace(i);
|
||||
} else if (min_unfinished_seqno > (*it)->atomic_flush_seqno_) {
|
||||
min_unfinished_seqno = (*it)->atomic_flush_seqno_;
|
||||
}
|
||||
}
|
||||
|
||||
while (s.ok() && !heap.empty()) {
|
||||
autovector<size_t> batch;
|
||||
SequenceNumber seqno = kMaxSequenceNumber;
|
||||
// Pop from the heap the memtables that belong to the same atomic flush,
|
||||
// namely their atomic_flush_seqno_ are equal.
|
||||
do {
|
||||
size_t pos = heap.top();
|
||||
const auto& memlist = imm_lists[pos]->current_->memlist_;
|
||||
MemTable* mem = *(memlist.rbegin());
|
||||
if (seqno == kMaxSequenceNumber) {
|
||||
// First mem in this batch.
|
||||
seqno = mem->atomic_flush_seqno_;
|
||||
batch.emplace_back(pos);
|
||||
heap.pop();
|
||||
} else if (mem->atomic_flush_seqno_ == seqno) {
|
||||
// mem has the same atomic_flush_seqno_, thus in the same atomic flush.
|
||||
batch.emplace_back(pos);
|
||||
heap.pop();
|
||||
} else if (mem->atomic_flush_seqno_ > seqno) {
|
||||
// mem belongs to another atomic flush with higher seqno, break the
|
||||
// loop.
|
||||
break;
|
||||
}
|
||||
} while (!heap.empty());
|
||||
if (seqno >= min_unfinished_seqno) {
|
||||
// If there is an older, unfinished atomic flush, then we should not
|
||||
// proceed.
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"MemTableList::TryInstallMemtableFlushResults:"
|
||||
"HasOlderUnfinishedAtomicFlush:0",
|
||||
nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
// Found the earliest, complete atomic flush. No earlier atomic flush is
|
||||
// pending. Therefore ready to record it to the MANIFEST.
|
||||
uint32_t num_entries = 0;
|
||||
autovector<ColumnFamilyData*> tmp_cfds;
|
||||
autovector<const MutableCFOptions*> tmp_mutable_cf_options_list;
|
||||
std::vector<autovector<MemTable*>> memtables_to_flush;
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
for (auto pos : batch) {
|
||||
tmp_cfds.emplace_back(cfds[pos]);
|
||||
tmp_mutable_cf_options_list.emplace_back(mutable_cf_options_list[pos]);
|
||||
const auto& memlist = imm_lists[pos]->current_->memlist_;
|
||||
uint64_t batch_file_number = 0;
|
||||
autovector<MemTable*> tmp_mems;
|
||||
autovector<VersionEdit*> edits;
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (!m->flush_completed_ ||
|
||||
(it != memlist.rbegin() && m->file_number_ != batch_file_number)) {
|
||||
break;
|
||||
}
|
||||
if (it == memlist.rbegin()) {
|
||||
batch_file_number = m->file_number_;
|
||||
edits.push_back(m->GetEdits());
|
||||
++num_entries;
|
||||
}
|
||||
tmp_mems.push_back(m);
|
||||
}
|
||||
edit_lists.push_back(edits);
|
||||
memtables_to_flush.push_back(tmp_mems);
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"MemTableList::TryInstallMemtableFlushResults:FoundBatchToCommit:0",
|
||||
&num_entries);
|
||||
|
||||
// Mark the version edits as an atomic group
|
||||
uint32_t remaining = num_entries;
|
||||
for (auto& edit_list : edit_lists) {
|
||||
assert(edit_list.size() == 1);
|
||||
edit_list[0]->MarkAtomicGroup(--remaining);
|
||||
}
|
||||
assert(remaining == 0);
|
||||
|
||||
size_t batch_sz = batch.size();
|
||||
assert(batch_sz > 0);
|
||||
assert(batch_sz == memtables_to_flush.size());
|
||||
assert(batch_sz == tmp_cfds.size());
|
||||
assert(batch_sz == edit_lists.size());
|
||||
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
for (size_t i = 0; i != batch_sz; ++i) {
|
||||
auto& edit_list = edit_lists[i];
|
||||
assert(!edit_list.empty());
|
||||
edit_list.back()->SetMinLogNumberToKeep(
|
||||
PrecomputeMinLogNumberToKeep(vset, *tmp_cfds[i], edit_list,
|
||||
memtables_to_flush[i], prep_tracker));
|
||||
}
|
||||
}
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(tmp_cfds, tmp_mutable_cf_options_list, edit_lists, mu,
|
||||
db_directory);
|
||||
|
||||
for (const auto pos : batch) {
|
||||
imm_lists[pos]->InstallNewVersion();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (size_t i = 0; i != batch_sz; ++i) {
|
||||
if (tmp_cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
size_t pos = batch[i];
|
||||
for (auto m : memtables_to_flush[i]) {
|
||||
assert(m->file_number_ > 0);
|
||||
uint64_t mem_id = m->GetID();
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " done",
|
||||
tmp_cfds[i]->GetName().c_str(), m->file_number_,
|
||||
mem_id);
|
||||
imm_lists[pos]->current_->Remove(m, to_delete);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != batch_sz; ++i) {
|
||||
size_t pos = batch[i];
|
||||
for (auto m : memtables_to_flush[i]) {
|
||||
uint64_t mem_id = m->GetID();
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " failed",
|
||||
tmp_cfds[i]->GetName().c_str(), m->file_number_,
|
||||
mem_id);
|
||||
m->flush_completed_ = false;
|
||||
m->flush_in_progress_ = false;
|
||||
m->edit_.Clear();
|
||||
m->file_number_ = 0;
|
||||
imm_lists[pos]->num_flush_not_started_++;
|
||||
}
|
||||
imm_lists[pos]->imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
// Adjust the heap AFTER installing new MemTableListVersions because the
|
||||
// compare function 'comp' needs to capture the most up-to-date state of
|
||||
// imm_lists.
|
||||
for (auto pos : batch) {
|
||||
const auto& memlist = imm_lists[pos]->current_->memlist_;
|
||||
if (!memlist.empty()) {
|
||||
MemTable* mem = *(memlist.rbegin());
|
||||
if (mem->flush_completed_) {
|
||||
heap.emplace(pos);
|
||||
} else if (min_unfinished_seqno > mem->atomic_flush_seqno_) {
|
||||
min_unfinished_seqno = mem->atomic_flush_seqno_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*atomic_flush_commit_in_progress = false;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Returns true if there is at least one memtable on which flush has
|
||||
// not yet started.
|
||||
bool MemTableList::IsFlushPending() const {
|
||||
@@ -749,4 +527,109 @@ uint64_t MemTableList::PrecomputeMinLogContainingPrepSection(
|
||||
return min_log;
|
||||
}
|
||||
|
||||
// Commit a successful atomic flush in the manifest file.
|
||||
Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_metas,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
|
||||
size_t num = mems_list.size();
|
||||
assert(cfds.size() == num);
|
||||
if (imm_lists != nullptr) {
|
||||
assert(imm_lists->size() == num);
|
||||
}
|
||||
for (size_t k = 0; k != num; ++k) {
|
||||
#ifndef NDEBUG
|
||||
const auto* imm =
|
||||
(imm_lists == nullptr) ? cfds[k]->imm() : imm_lists->at(k);
|
||||
if (!mems_list[k]->empty()) {
|
||||
assert((*mems_list[k])[0]->GetID() == imm->GetEarliestMemTableID());
|
||||
}
|
||||
#endif
|
||||
assert(nullptr != file_metas[k]);
|
||||
for (size_t i = 0; i != mems_list[k]->size(); ++i) {
|
||||
assert(i == 0 || (*mems_list[k])[i]->GetEdits()->NumEntries() == 0);
|
||||
(*mems_list[k])[i]->SetFlushCompleted(true);
|
||||
(*mems_list[k])[i]->SetFileNumber(file_metas[k]->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
|
||||
Status s;
|
||||
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
uint32_t num_entries = 0;
|
||||
for (const auto mems : mems_list) {
|
||||
assert(mems != nullptr);
|
||||
autovector<VersionEdit*> edits;
|
||||
assert(!mems->empty());
|
||||
edits.emplace_back((*mems)[0]->GetEdits());
|
||||
++num_entries;
|
||||
edit_lists.emplace_back(edits);
|
||||
}
|
||||
// Mark the version edits as an atomic group if the number of version edits
|
||||
// exceeds 1.
|
||||
if (cfds.size() > 1) {
|
||||
for (auto& edits : edit_lists) {
|
||||
assert(edits.size() == 1);
|
||||
edits[0]->MarkAtomicGroup(--num_entries);
|
||||
}
|
||||
assert(0 == num_entries);
|
||||
}
|
||||
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfds, mutable_cf_options_list, edit_lists, mu,
|
||||
db_directory);
|
||||
|
||||
for (size_t k = 0; k != cfds.size(); ++k) {
|
||||
auto* imm = (imm_lists == nullptr) ? cfds[k]->imm() : imm_lists->at(k);
|
||||
imm->InstallNewVersion();
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
auto* imm = (imm_lists == nullptr) ? cfds[i]->imm() : imm_lists->at(i);
|
||||
for (auto m : *mems_list[i]) {
|
||||
assert(m->GetFileNumber() > 0);
|
||||
uint64_t mem_id = m->GetID();
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " done",
|
||||
cfds[i]->GetName().c_str(), m->GetFileNumber(),
|
||||
mem_id);
|
||||
imm->current_->Remove(m, to_delete);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
auto* imm = (imm_lists == nullptr) ? cfds[i]->imm() : imm_lists->at(i);
|
||||
for (auto m : *mems_list[i]) {
|
||||
uint64_t mem_id = m->GetID();
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " failed",
|
||||
cfds[i]->GetName().c_str(), m->GetFileNumber(),
|
||||
mem_id);
|
||||
m->SetFlushCompleted(false);
|
||||
m->SetFlushInProgress(false);
|
||||
m->GetEdits()->Clear();
|
||||
m->SetFileNumber(0);
|
||||
imm->num_flush_not_started_++;
|
||||
}
|
||||
imm->imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+39
-16
@@ -15,7 +15,7 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/range_del_aggregator_v2.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -31,6 +31,7 @@ class ColumnFamilyData;
|
||||
class InternalKeyComparator;
|
||||
class InstrumentedMutex;
|
||||
class MergeIteratorBuilder;
|
||||
class MemTableList;
|
||||
|
||||
// keeps a list of immutable memtables in a vector. the list is immutable
|
||||
// if refcount is bigger than one. It is used as a state for Get() and
|
||||
@@ -91,7 +92,7 @@ class MemTableListVersion {
|
||||
}
|
||||
|
||||
Status AddRangeTombstoneIterators(const ReadOptions& read_opts, Arena* arena,
|
||||
RangeDelAggregatorV2* range_del_agg);
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
void AddIterators(const ReadOptions& options,
|
||||
std::vector<InternalIterator*>* iterator_list,
|
||||
@@ -114,6 +115,18 @@ class MemTableListVersion {
|
||||
SequenceNumber GetEarliestSequenceNumber(bool include_history = false) const;
|
||||
|
||||
private:
|
||||
friend class MemTableList;
|
||||
|
||||
friend Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// REQUIRE: m is an immutable memtable
|
||||
void Add(MemTable* m, autovector<MemTable*>* to_delete);
|
||||
// REQUIRE: m is an immutable memtable
|
||||
@@ -132,8 +145,6 @@ class MemTableListVersion {
|
||||
|
||||
void UnrefMemTable(autovector<MemTable*>* to_delete, MemTable* m);
|
||||
|
||||
friend class MemTableList;
|
||||
|
||||
// Immutable MemTables that have not yet been flushed.
|
||||
std::list<MemTable*> memlist_;
|
||||
|
||||
@@ -163,18 +174,6 @@ class MemTableListVersion {
|
||||
// write thread.)
|
||||
class MemTableList {
|
||||
public:
|
||||
// Commit a successful atomic flush in the manifest file
|
||||
static Status TryInstallMemtableFlushResults(
|
||||
autovector<MemTableList*>& imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
bool* atomic_flush_commit_in_progress, LogsWithPrepTracker* prep_tracker,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// A list of memtables.
|
||||
explicit MemTableList(int min_write_buffer_number_to_merge,
|
||||
int max_write_buffer_number_to_maintain)
|
||||
@@ -296,6 +295,16 @@ class MemTableList {
|
||||
}
|
||||
|
||||
private:
|
||||
friend Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// DB mutex held
|
||||
void InstallNewVersion();
|
||||
|
||||
@@ -317,4 +326,18 @@ class MemTableList {
|
||||
size_t current_memory_usage_;
|
||||
};
|
||||
|
||||
// Installs memtable atomic flush results.
|
||||
// In most cases, imm_lists is nullptr, and the function simply uses the
|
||||
// immutable memtable lists associated with the cfds. There are unit tests that
|
||||
// installs flush results for external immutable memtable lists other than the
|
||||
// cfds' own immutable memtable lists, e.g. MemTableLIstTest. In this case,
|
||||
// imm_lists parameter is not nullptr.
|
||||
extern Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
} // namespace rocksdb
|
||||
+106
-267
@@ -8,7 +8,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -63,7 +62,7 @@ class MemTableListTest : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
~MemTableListTest() {
|
||||
~MemTableListTest() override {
|
||||
if (db) {
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs(handles.size());
|
||||
for (int i = 0; i != static_cast<int>(handles.size()); ++i) {
|
||||
@@ -86,17 +85,46 @@ class MemTableListTest : public testing::Test {
|
||||
Status Mock_InstallMemtableFlushResults(
|
||||
MemTableList* list, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& m, autovector<MemTable*>* to_delete) {
|
||||
autovector<MemTableList*> lists;
|
||||
lists.emplace_back(list);
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
mems_list.emplace_back(&m);
|
||||
return Mock_InstallMemtableFlushResults(
|
||||
lists, {0} /* cf_ids */, {&mutable_cf_options}, mems_list, to_delete);
|
||||
// Create a mock Logger
|
||||
test::NullLogger logger;
|
||||
LogBuffer log_buffer(DEBUG_LEVEL, &logger);
|
||||
|
||||
CreateDB();
|
||||
// Create a mock VersionSet
|
||||
DBOptions db_options;
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
EnvOptions env_options;
|
||||
std::shared_ptr<Cache> table_cache(NewLRUCache(50000, 16));
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
WriteController write_controller(10000000u);
|
||||
|
||||
VersionSet versions(dbname, &immutable_db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller);
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("one", ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("two", ColumnFamilyOptions());
|
||||
|
||||
EXPECT_OK(versions.Recover(cf_descs, false));
|
||||
|
||||
// Create mock default ColumnFamilyData
|
||||
auto column_family_set = versions.GetColumnFamilySet();
|
||||
LogsWithPrepTracker dummy_prep_tracker;
|
||||
auto cfd = column_family_set->GetDefault();
|
||||
EXPECT_TRUE(nullptr != cfd);
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
// Create dummy mutex.
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return list->TryInstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex,
|
||||
file_num, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
|
||||
// Calls MemTableList::InstallMemtableFlushResults() and sets up all
|
||||
// structures needed to call this function.
|
||||
Status Mock_InstallMemtableFlushResults(
|
||||
Status Mock_InstallMemtableAtomicFlushResults(
|
||||
autovector<MemTableList*>& lists, const autovector<uint32_t>& cf_ids,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
@@ -128,44 +156,28 @@ class MemTableListTest : public testing::Test {
|
||||
auto column_family_set = versions.GetColumnFamilySet();
|
||||
|
||||
LogsWithPrepTracker dummy_prep_tracker;
|
||||
if (1 == cf_ids.size()) {
|
||||
auto cfd = column_family_set->GetColumnFamily(cf_ids[0]);
|
||||
EXPECT_TRUE(nullptr != cfd);
|
||||
EXPECT_EQ(1, lists.size());
|
||||
MemTableList* list = lists[0];
|
||||
EXPECT_EQ(1, mutable_cf_options_list.size());
|
||||
const MutableCFOptions& mutable_cf_options =
|
||||
*(mutable_cf_options_list.at(0));
|
||||
const autovector<MemTable*>* mems = mems_list.at(0);
|
||||
EXPECT_TRUE(nullptr != mems);
|
||||
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
// Create dummy mutex.
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return list->TryInstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, *mems, &dummy_prep_tracker, &versions,
|
||||
&mutex, file_num, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
for (int i = 0; i != static_cast<int>(cf_ids.size()); ++i) {
|
||||
cfds.emplace_back(column_family_set->GetColumnFamily(cf_ids[i]));
|
||||
EXPECT_NE(nullptr, cfds[i]);
|
||||
}
|
||||
autovector<FileMetaData> file_metas;
|
||||
std::vector<FileMetaData> file_metas;
|
||||
file_metas.reserve(cf_ids.size());
|
||||
for (size_t i = 0; i != cf_ids.size(); ++i) {
|
||||
FileMetaData meta;
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
meta.fd = FileDescriptor(file_num, 0, 0);
|
||||
file_metas.emplace_back(meta);
|
||||
}
|
||||
bool atomic_flush_commit_in_progress = false;
|
||||
autovector<FileMetaData*> file_meta_ptrs;
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return MemTableList::TryInstallMemtableFlushResults(
|
||||
lists, cfds, mutable_cf_options_list, mems_list,
|
||||
&atomic_flush_commit_in_progress, &dummy_prep_tracker, &versions,
|
||||
&mutex, file_metas, to_delete, nullptr, &log_buffer);
|
||||
return InstallMemtableAtomicFlushResults(
|
||||
&lists, cfds, mutable_cf_options_list, mems_list, &versions, &mutex,
|
||||
file_meta_ptrs, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -731,18 +743,28 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
to_delete.clear();
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, FlushMultipleCFsTest) {
|
||||
TEST_F(MemTableListTest, EmptyAtomicFlusTest) {
|
||||
autovector<MemTableList*> lists;
|
||||
autovector<uint32_t> cf_ids;
|
||||
autovector<const MutableCFOptions*> options_list;
|
||||
autovector<const autovector<MemTable*>*> to_flush;
|
||||
autovector<MemTable*> to_delete;
|
||||
Status s = Mock_InstallMemtableAtomicFlushResults(lists, cf_ids, options_list,
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(to_delete.empty());
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, AtomicFlusTest) {
|
||||
const int num_cfs = 3;
|
||||
const int num_tables_per_cf = 5;
|
||||
const int num_tables_per_cf = 2;
|
||||
SequenceNumber seq = 1;
|
||||
Status s;
|
||||
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
autovector<MemTable*> to_delete;
|
||||
|
||||
// Create MemTableLists
|
||||
int min_write_buffer_number_to_merge = 3;
|
||||
@@ -783,135 +805,72 @@ TEST_F(MemTableListTest, FlushMultipleCFsTest) {
|
||||
std::vector<autovector<MemTable*>> flush_candidates(num_cfs);
|
||||
|
||||
// Nothing to flush
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
auto list = lists[i];
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
auto* list = lists[i];
|
||||
ASSERT_FALSE(list->IsFlushPending());
|
||||
ASSERT_FALSE(list->imm_flush_needed.load(std::memory_order_acquire));
|
||||
list->PickMemtablesToFlush(nullptr /* memtable_id */, &flush_candidates[i]);
|
||||
ASSERT_EQ(0, static_cast<int>(flush_candidates[i].size()));
|
||||
ASSERT_EQ(0, flush_candidates[i].size());
|
||||
}
|
||||
|
||||
// Request flush even though there is nothing to flush
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
auto list = lists[i];
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
auto* list = lists[i];
|
||||
list->FlushRequested();
|
||||
ASSERT_FALSE(list->IsFlushPending());
|
||||
ASSERT_FALSE(list->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
// Add tables to column families
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
for (int j = 0; j != num_tables_per_cf; ++j) {
|
||||
autovector<MemTable*> to_delete;
|
||||
// Add tables to the immutable memtalbe lists associated with column families
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
for (auto j = 0; j != num_tables_per_cf; ++j) {
|
||||
lists[i]->Add(tables[i][j], &to_delete);
|
||||
}
|
||||
ASSERT_EQ(num_tables_per_cf, lists[i]->NumNotFlushed());
|
||||
ASSERT_TRUE(lists[i]->IsFlushPending());
|
||||
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
std::vector<uint64_t> flush_memtable_ids = {1, 1, 0};
|
||||
// +----+
|
||||
// list[0]: |0 1|
|
||||
// list[1]: |0 1|
|
||||
// | +--+
|
||||
// list[2]: |0| 1
|
||||
// +-+
|
||||
// Pick memtables to flush
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
flush_candidates[i].clear();
|
||||
lists[i]->PickMemtablesToFlush(&flush_memtable_ids[i],
|
||||
&flush_candidates[i]);
|
||||
ASSERT_EQ(flush_memtable_ids[i] - 0 + 1,
|
||||
static_cast<uint64_t>(flush_candidates[i].size()));
|
||||
}
|
||||
autovector<MemTableList*> tmp_lists;
|
||||
autovector<uint32_t> tmp_cf_ids;
|
||||
autovector<const MutableCFOptions*> tmp_options_list;
|
||||
autovector<const autovector<MemTable*>*> to_flush;
|
||||
std::vector<uint64_t> prev_memtable_ids;
|
||||
// For each column family, determine the memtables to flush
|
||||
for (int k = 0; k != 4; ++k) {
|
||||
std::vector<uint64_t> flush_memtable_ids;
|
||||
if (0 == k) {
|
||||
// +----+
|
||||
// list[0]: |0 1| 2 3 4
|
||||
// list[1]: |0 1| 2 3 4
|
||||
// | +--+
|
||||
// list[2]: |0| 1 2 3 4
|
||||
// +-+
|
||||
flush_memtable_ids = {1, 1, 0};
|
||||
} else if (1 == k) {
|
||||
// +----+ +---+
|
||||
// list[0]: |0 1| |2 3| 4
|
||||
// list[1]: |0 1| |2 3| 4
|
||||
// | +--+ +---+
|
||||
// list[2]: |0| 1 2 3 4
|
||||
// +-+
|
||||
flush_memtable_ids = {3, 3, 0};
|
||||
} else if (2 == k) {
|
||||
// +-----+ +---+
|
||||
// list[0]: |0 1| |2 3| 4
|
||||
// list[1]: |0 1| |2 3| 4
|
||||
// | +---+ +---+
|
||||
// | | +-------+
|
||||
// list[2]: |0| |1 2 3| 4
|
||||
// +-+ +-------+
|
||||
flush_memtable_ids = {3, 3, 3};
|
||||
} else {
|
||||
// +-----+ +---+ +-+
|
||||
// list[0]: |0 1| |2 3| |4|
|
||||
// list[1]: |0 1| |2 3| |4|
|
||||
// | +---+ +---+ | |
|
||||
// | | +-------+ | |
|
||||
// list[2]: |0| |1 2 3| |4|
|
||||
// +-+ +-------+ +-+
|
||||
flush_memtable_ids = {4, 4, 4};
|
||||
}
|
||||
assert(num_cfs == static_cast<int>(flush_memtable_ids.size()));
|
||||
|
||||
// Pick memtables to flush
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
flush_candidates[i].clear();
|
||||
lists[i]->PickMemtablesToFlush(&flush_memtable_ids[i],
|
||||
&flush_candidates[i]);
|
||||
for (auto mem : flush_candidates[i]) {
|
||||
mem->TEST_AtomicFlushSequenceNumber() = SequenceNumber(k);
|
||||
}
|
||||
if (prev_memtable_ids.empty()) {
|
||||
ASSERT_EQ(flush_memtable_ids[i] - 0 + 1, flush_candidates[i].size());
|
||||
} else {
|
||||
ASSERT_EQ(flush_memtable_ids[i] - prev_memtable_ids[i],
|
||||
flush_candidates[i].size());
|
||||
}
|
||||
ASSERT_EQ(num_tables_per_cf, lists[i]->NumNotFlushed());
|
||||
ASSERT_FALSE(lists[i]->HasFlushRequested());
|
||||
if (flush_memtable_ids[i] == num_tables_per_cf - 1) {
|
||||
ASSERT_FALSE(
|
||||
lists[i]->imm_flush_needed.load(std::memory_order_acquire));
|
||||
} else {
|
||||
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
}
|
||||
prev_memtable_ids = flush_memtable_ids;
|
||||
|
||||
if (k < 3) {
|
||||
for (const auto& mems : flush_candidates) {
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
for (auto m : mems) {
|
||||
m->TEST_SetFlushCompleted(true);
|
||||
m->TEST_SetFileNumber(file_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (k == 0) {
|
||||
// Rollback first pick of tables
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
auto list = lists[i];
|
||||
const auto& mems = flush_candidates[i];
|
||||
for (auto m : mems) {
|
||||
m->TEST_SetFileNumber(0);
|
||||
}
|
||||
list->RollbackMemtableFlush(flush_candidates[i], 0);
|
||||
ASSERT_TRUE(list->IsFlushPending());
|
||||
ASSERT_TRUE(list->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
prev_memtable_ids.clear();
|
||||
}
|
||||
|
||||
if (k == 3) {
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
to_flush.emplace_back(&flush_candidates[i]);
|
||||
}
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
if (!flush_candidates[i].empty()) {
|
||||
to_flush.push_back(&flush_candidates[i]);
|
||||
tmp_lists.push_back(lists[i]);
|
||||
tmp_cf_ids.push_back(i);
|
||||
tmp_options_list.push_back(mutable_cf_options_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
s = Mock_InstallMemtableFlushResults(lists, cf_ids, mutable_cf_options_list,
|
||||
to_flush, &to_delete);
|
||||
Status s = Mock_InstallMemtableAtomicFlushResults(
|
||||
tmp_lists, tmp_cf_ids, tmp_options_list, to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
for (auto j = 0; j != num_tables_per_cf; ++j) {
|
||||
if (static_cast<uint64_t>(j) <= flush_memtable_ids[i]) {
|
||||
ASSERT_LT(0, tables[i][j]->GetFileNumber());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(
|
||||
static_cast<size_t>(num_tables_per_cf) - flush_candidates[i].size(),
|
||||
lists[i]->NumNotFlushed());
|
||||
}
|
||||
|
||||
to_delete.clear();
|
||||
for (auto list : lists) {
|
||||
list->current()->Unref(&to_delete);
|
||||
@@ -933,126 +892,6 @@ TEST_F(MemTableListTest, FlushMultipleCFsTest) {
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
delete m;
|
||||
}
|
||||
to_delete.clear();
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, HasOlderAtomicFlush) {
|
||||
const size_t num_cfs = 3;
|
||||
const size_t num_memtables_per_cf = 2;
|
||||
SequenceNumber seq = 1;
|
||||
Status s;
|
||||
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
autovector<MemTable*> to_delete;
|
||||
|
||||
// Create MemTableLists
|
||||
int min_write_buffer_number_to_merge = 3;
|
||||
int max_write_buffer_number_to_maintain = 7;
|
||||
autovector<MemTableList*> lists;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
lists.emplace_back(new MemTableList(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain));
|
||||
}
|
||||
|
||||
autovector<uint32_t> cf_ids;
|
||||
std::vector<std::vector<MemTable*>> tables;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
uint32_t cf_id = 0;
|
||||
for (size_t k = 0; k != num_cfs; ++k) {
|
||||
std::vector<MemTable*> elem;
|
||||
mutable_cf_options_list.emplace_back(new MutableCFOptions(options));
|
||||
uint64_t memtable_id = 0;
|
||||
for (int i = 0; i != num_memtables_per_cf; ++i) {
|
||||
MemTable* mem =
|
||||
new MemTable(cmp, ioptions, *(mutable_cf_options_list.back()), &wb,
|
||||
kMaxSequenceNumber, cf_id);
|
||||
mem->SetID(memtable_id++);
|
||||
mem->Ref();
|
||||
|
||||
std::string value;
|
||||
|
||||
mem->Add(++seq, kTypeValue, "key1", ToString(i));
|
||||
mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN");
|
||||
mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value");
|
||||
mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM");
|
||||
mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), "");
|
||||
|
||||
elem.push_back(mem);
|
||||
}
|
||||
tables.emplace_back(elem);
|
||||
cf_ids.push_back(cf_id++);
|
||||
}
|
||||
|
||||
// Add tables to column families' immutable memtable lists
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
for (size_t j = 0; j != num_memtables_per_cf; ++j) {
|
||||
lists[i]->Add(tables[i][j], &to_delete);
|
||||
}
|
||||
lists[i]->FlushRequested();
|
||||
ASSERT_EQ(num_memtables_per_cf, lists[i]->NumNotFlushed());
|
||||
ASSERT_TRUE(lists[i]->IsFlushPending());
|
||||
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
std::vector<autovector<MemTable*>> flush_candidates(num_cfs);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
lists[i]->PickMemtablesToFlush(nullptr, &flush_candidates[i]);
|
||||
for (auto m : flush_candidates[i]) {
|
||||
m->TEST_AtomicFlushSequenceNumber() = 123;
|
||||
}
|
||||
lists[i]->RollbackMemtableFlush(flush_candidates[i], 0);
|
||||
}
|
||||
uint64_t memtable_id = num_memtables_per_cf - 1;
|
||||
autovector<MemTable*> other_flush_candidates;
|
||||
lists[0]->PickMemtablesToFlush(&memtable_id, &other_flush_candidates);
|
||||
for (auto m : other_flush_candidates) {
|
||||
m->TEST_AtomicFlushSequenceNumber() = 124;
|
||||
m->TEST_SetFlushCompleted(true);
|
||||
m->TEST_SetFileNumber(1);
|
||||
}
|
||||
autovector<const autovector<MemTable*>*> to_flush;
|
||||
to_flush.emplace_back(&other_flush_candidates);
|
||||
bool has_older_unfinished_atomic_flush = false;
|
||||
bool found_batch_to_commit = false;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTableList::TryInstallMemtableFlushResults:"
|
||||
"HasOlderUnfinishedAtomicFlush:0",
|
||||
[&](void* /*arg*/) { has_older_unfinished_atomic_flush = true; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTableList::TryInstallMemtableFlushResults:FoundBatchToCommit:0",
|
||||
[&](void* /*arg*/) { found_batch_to_commit = true; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
s = Mock_InstallMemtableFlushResults(lists, cf_ids, mutable_cf_options_list,
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(has_older_unfinished_atomic_flush);
|
||||
ASSERT_FALSE(found_batch_to_commit);
|
||||
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_TRUE(to_delete.empty());
|
||||
for (auto list : lists) {
|
||||
list->current()->Unref(&to_delete);
|
||||
delete list;
|
||||
}
|
||||
lists.clear();
|
||||
ASSERT_EQ(num_cfs * num_memtables_per_cf, to_delete.size());
|
||||
for (auto m : to_delete) {
|
||||
m->Ref();
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
delete m;
|
||||
}
|
||||
to_delete.clear();
|
||||
for (auto& opts : mutable_cf_options_list) {
|
||||
delete opts;
|
||||
opts = nullptr;
|
||||
}
|
||||
mutable_cf_options_list.clear();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+4
-2
@@ -79,7 +79,8 @@ class MergeContext {
|
||||
return GetOperandsDirectionForward();
|
||||
}
|
||||
|
||||
// Return all the operands in the order as they were merged (passed to FullMerge or FullMergeV2)
|
||||
// Return all the operands in the order as they were merged (passed to
|
||||
// FullMerge or FullMergeV2)
|
||||
const std::vector<Slice>& GetOperandsDirectionForward() {
|
||||
if (!operand_list_) {
|
||||
return empty_operand_list;
|
||||
@@ -89,7 +90,8 @@ class MergeContext {
|
||||
return *operand_list_;
|
||||
}
|
||||
|
||||
// Return all the operands in the reversed order relative to how they were merged (passed to FullMerge or FullMergeV2)
|
||||
// Return all the operands in the reversed order relative to how they were
|
||||
// merged (passed to FullMerge or FullMergeV2)
|
||||
const std::vector<Slice>& GetOperandsDirectionBackward() {
|
||||
if (!operand_list_) {
|
||||
return empty_operand_list;
|
||||
|
||||
+28
-17
@@ -110,8 +110,11 @@ Status MergeHelper::TimedFullMerge(const MergeOperator* merge_operator,
|
||||
// keys_ stores the list of keys encountered while merging.
|
||||
// operands_ stores the list of merge operands encountered while merging.
|
||||
// keys_[i] corresponds to operands_[i] for each i.
|
||||
//
|
||||
// TODO: Avoid the snapshot stripe map lookup in CompactionRangeDelAggregator
|
||||
// and just pass the StripeRep corresponding to the stripe being merged.
|
||||
Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const SequenceNumber stop_before,
|
||||
const bool at_bottom) {
|
||||
// Get a copy of the internal key, before it's invalidated by iter->Next()
|
||||
@@ -135,7 +138,11 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// orig_ikey is backed by original_key if keys_.empty()
|
||||
// orig_ikey is backed by keys_.back() if !keys_.empty()
|
||||
ParsedInternalKey orig_ikey;
|
||||
ParseInternalKey(original_key, &orig_ikey);
|
||||
bool succ = ParseInternalKey(original_key, &orig_ikey);
|
||||
assert(succ);
|
||||
if (!succ) {
|
||||
return Status::Corruption("Cannot parse key in MergeUntil");
|
||||
}
|
||||
|
||||
Status s;
|
||||
bool hit_the_next_user_key = false;
|
||||
@@ -163,9 +170,11 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
break;
|
||||
} else if (stop_before > 0 && ikey.sequence <= stop_before &&
|
||||
LIKELY(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey.sequence,
|
||||
stop_before))) {
|
||||
// hit an entry that's visible by the previous snapshot, can't touch that
|
||||
snapshot_checker_->CheckInSnapshot(ikey.sequence,
|
||||
stop_before) !=
|
||||
SnapshotCheckerResult::kNotInSnapshot)) {
|
||||
// hit an entry that's possibly visible by the previous snapshot, can't
|
||||
// touch that
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -277,22 +286,24 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// We are sure we have seen this key's entire history if we are at the
|
||||
// last level and exhausted all internal keys of this user key.
|
||||
// NOTE: !iter->Valid() does not necessarily mean we hit the
|
||||
// beginning of a user key, as versions of a user key might be
|
||||
// split into multiple files (even files on the same level)
|
||||
// and some files might not be included in the compaction/merge.
|
||||
// We are sure we have seen this key's entire history if:
|
||||
// at_bottom == true (this does not necessarily mean it is the bottommost
|
||||
// layer, but rather that we are confident the key does not appear on any of
|
||||
// the lower layers, at_bottom == false doesn't mean it does appear, just
|
||||
// that we can't be sure, see Compaction::IsBottommostLevel for details)
|
||||
// AND
|
||||
// we have either encountered another key or end of key history on this
|
||||
// layer.
|
||||
//
|
||||
// There are also cases where we have seen the root of history of this
|
||||
// key without being sure of it. Then, we simply miss the opportunity
|
||||
// When these conditions are true we are able to merge all the keys
|
||||
// using full merge.
|
||||
//
|
||||
// For these cases we are not sure about, we simply miss the opportunity
|
||||
// to combine the keys. Since VersionSet::SetupOtherInputs() always makes
|
||||
// sure that all merge-operands on the same level get compacted together,
|
||||
// this will simply lead to these merge operands moving to the next level.
|
||||
//
|
||||
// So, we only perform the following logic (to merge all operands together
|
||||
// without a Put/Delete) if we are certain that we have seen the end of key.
|
||||
bool surely_seen_the_beginning = hit_the_next_user_key && at_bottom;
|
||||
bool surely_seen_the_beginning =
|
||||
(hit_the_next_user_key || !iter->Valid()) && at_bottom;
|
||||
if (surely_seen_the_beginning) {
|
||||
// do a final merge with nullptr as the existing value and say
|
||||
// bye to the merge type (it's now converted to a Put)
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ class MergeHelper {
|
||||
//
|
||||
// REQUIRED: The first key in the input is not corrupted.
|
||||
Status MergeUntil(InternalIterator* iter,
|
||||
RangeDelAggregator* range_del_agg = nullptr,
|
||||
CompactionRangeDelAggregator* range_del_agg = nullptr,
|
||||
const SequenceNumber stop_before = 0,
|
||||
const bool at_bottom = false);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class MergeHelperTest : public testing::Test {
|
||||
public:
|
||||
MergeHelperTest() { env_ = Env::Default(); }
|
||||
|
||||
~MergeHelperTest() = default;
|
||||
~MergeHelperTest() override = default;
|
||||
|
||||
Status Run(SequenceNumber stop_before, bool at_bottom,
|
||||
SequenceNumber latest_snapshot = 0) {
|
||||
@@ -130,7 +130,7 @@ TEST_F(MergeHelperTest, SingleOperand) {
|
||||
|
||||
AddKeyVal("a", 50, kTypeMerge, test::EncodeInt(1U));
|
||||
|
||||
ASSERT_TRUE(Run(31, true).IsMergeInProgress());
|
||||
ASSERT_TRUE(Run(31, false).IsMergeInProgress());
|
||||
ASSERT_FALSE(iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("a", 50, kTypeMerge), merge_helper_->keys()[0]);
|
||||
ASSERT_EQ(test::EncodeInt(1U), merge_helper_->values()[0]);
|
||||
|
||||
+8
-13
@@ -38,11 +38,8 @@ class CountMergeOperator : public AssociativeMergeOperator {
|
||||
mergeOperator_ = MergeOperators::CreateUInt64AddOperator();
|
||||
}
|
||||
|
||||
virtual bool Merge(const Slice& key,
|
||||
const Slice* existing_value,
|
||||
const Slice& value,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
bool Merge(const Slice& key, const Slice* existing_value, const Slice& value,
|
||||
std::string* new_value, Logger* logger) const override {
|
||||
assert(new_value->empty());
|
||||
++num_merge_operator_calls;
|
||||
if (existing_value == nullptr) {
|
||||
@@ -58,19 +55,17 @@ class CountMergeOperator : public AssociativeMergeOperator {
|
||||
logger);
|
||||
}
|
||||
|
||||
virtual bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
assert(new_value->empty());
|
||||
++num_partial_merge_calls;
|
||||
return mergeOperator_->PartialMergeMulti(key, operand_list, new_value,
|
||||
logger);
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "UInt64AddOperator";
|
||||
}
|
||||
const char* Name() const override { return "UInt64AddOperator"; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<MergeOperator> mergeOperator_;
|
||||
@@ -230,7 +225,7 @@ class MergeBasedCounters : public Counters {
|
||||
}
|
||||
|
||||
// mapped to a rocksdb Merge operation
|
||||
virtual bool add(const std::string& key, uint64_t value) override {
|
||||
bool add(const std::string& key, uint64_t value) override {
|
||||
char encoded[sizeof(uint64_t)];
|
||||
EncodeFixed64(encoded, value);
|
||||
Slice slice(encoded, sizeof(uint64_t));
|
||||
|
||||
Loaded 100 of 512 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user