mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
110
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ad033ce2b | ||
|
|
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
+1
-4
@@ -71,10 +71,7 @@ install:
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl --silent --fail --show-error --location https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == java_test ]]; then
|
||||
java -version && echo "JAVA_HOME=${JAVA_HOME}";
|
||||
mkdir cmake-dist && curl -sfSL https://cmake.org/files/v3.8/cmake-3.8.1-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
|
||||
+9
-19
@@ -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()
|
||||
|
||||
@@ -596,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
|
||||
@@ -636,18 +643,10 @@ 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
|
||||
@@ -664,9 +663,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
|
||||
@@ -822,6 +819,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}"
|
||||
@@ -967,11 +965,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
|
||||
@@ -980,8 +973,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
|
||||
@@ -1001,7 +992,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})
|
||||
|
||||
+23
-12
@@ -1,18 +1,32 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.18.4 (3/3/2020)
|
||||
* Various fixes for ARM64 support (#6250)
|
||||
* Fix JEMALLOC_CXX_THROW macro missing from older Jemalloc versions, causing build failures on some platforms.
|
||||
|
||||
## 5.18.3 (2/11/2019)
|
||||
### Bug Fixes
|
||||
* Fix possible LSM corruption when both range deletions and subcompactions are used. The symptom of this corruption is L1+ files overlapping in the user key space.
|
||||
|
||||
## 5.18.2 (01/31/2019)
|
||||
## Unreleased
|
||||
### 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.
|
||||
|
||||
### Public API Change
|
||||
* 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.
|
||||
|
||||
### 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
|
||||
* Introduced `JemallocNodumpAllocator` memory allocator. When being use, block cache will be excluded from core dump.
|
||||
@@ -24,7 +38,6 @@
|
||||
* Add xxhash64 checksum support
|
||||
* Introduced `MemoryAllocator`, which lets the user specify custom memory allocator for block based table.
|
||||
* Improved `DeleteRange` to prevent read performance degradation. The feature is no longer marked as experimental.
|
||||
* Enabled checkpoint on readonly db (DBImplReadOnly).
|
||||
|
||||
### Public API Change
|
||||
* `DBOptions::use_direct_reads` now affects reads issued by `BackupEngine` on the database's SSTs.
|
||||
@@ -41,8 +54,6 @@
|
||||
* Fixed Get correctness bug in the presence of range tombstones where merge operands covered by a range tombstone always result in NotFound.
|
||||
* Start populating `NO_FILE_CLOSES` ticker statistic, which was always zero previously.
|
||||
* The default value of NewBloomFilterPolicy()'s argument use_block_based_builder is changed to false. Note that this new default may cause large temp memory usage when building very large SST files.
|
||||
* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls.
|
||||
* Make DB ignore dropped column families while committing results of atomic flush.
|
||||
|
||||
## 5.17.0 (10/05/2018)
|
||||
### Public API Change
|
||||
|
||||
@@ -137,12 +137,6 @@ CFLAGS += -DHAVE_POWER8
|
||||
HAVE_POWER8=1
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -march=armv8-a+crc
|
||||
CFLAGS += -march=armv8-a+crc
|
||||
ARMCRC_SOURCE=1
|
||||
endif
|
||||
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG)
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
@@ -204,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)
|
||||
@@ -409,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 \
|
||||
@@ -488,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 \
|
||||
@@ -502,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 \
|
||||
@@ -519,7 +508,6 @@ TESTS = \
|
||||
deletefile_test \
|
||||
obsolete_files_test \
|
||||
table_test \
|
||||
geodb_test \
|
||||
delete_scheduler_test \
|
||||
options_test \
|
||||
options_settable_test \
|
||||
@@ -536,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 \
|
||||
@@ -610,7 +597,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),)
|
||||
@@ -1159,9 +1146,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)
|
||||
|
||||
@@ -1300,18 +1284,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)
|
||||
|
||||
@@ -1329,9 +1304,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)
|
||||
|
||||
@@ -1458,9 +1430,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)
|
||||
|
||||
@@ -1509,9 +1478,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)
|
||||
|
||||
@@ -1551,9 +1517,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)
|
||||
|
||||
@@ -1641,7 +1604,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 sparc64))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -1650,10 +1613,10 @@ else
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ppc% arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
ifeq (,$(findstring ppc,$(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux$(ARCH).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
@@ -1666,15 +1629,15 @@ ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= https://downloads.sourceforge.net/project/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
BZIP2_DOWNLOAD_BASE ?= https://web.archive.org/web/20180624184835/http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.7
|
||||
SNAPPY_SHA256 ?= 3dfa02e873ff51a11ee02b9ca391807f0c8ea0529a4924afa645fbf97163f9d4
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.2
|
||||
LZ4_SHA256 ?= 658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc
|
||||
LZ4_VER ?= 1.8.3
|
||||
LZ4_SHA256 ?= 33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.4.4
|
||||
ZSTD_SHA256 ?= a364f5162c7d1a455cc915e8e3cf5f4bd8b75d09bc0f53965b0c9ca1383c52c8
|
||||
ZSTD_VER ?= 1.3.7
|
||||
ZSTD_SHA256 ?= 5dd1e90eb16c25425880c8a91327f63de22891ffed082fcc17e5ae84fce0d5fb
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -1714,7 +1677,7 @@ endif
|
||||
libz.a:
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
ifeq (,$(wildcard ./zlib-$(ZLIB_VER).tar.gz))
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/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 \
|
||||
@@ -1728,7 +1691,7 @@ endif
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
ifeq (,$(wildcard ./bzip2-$(BZIP2_VER).tar.gz))
|
||||
curl --fail --output bzip2-$(BZIP2_VER).tar.gz --location ${CURL_SSL_OPTS} ${BZIP2_DOWNLOAD_BASE}/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 \
|
||||
@@ -1742,7 +1705,7 @@ endif
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
ifeq (,$(wildcard ./snappy-$(SNAPPY_VER).tar.gz))
|
||||
curl --fail --output snappy-$(SNAPPY_VER).tar.gz --location ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(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 \
|
||||
@@ -1751,13 +1714,13 @@ endif
|
||||
fi
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
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}
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' cmake .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
ifeq (,$(wildcard ./lz4-$(LZ4_VER).tar.gz))
|
||||
curl --fail --output lz4-$(LZ4_VER).tar.gz --location ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(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 \
|
||||
@@ -1771,7 +1734,7 @@ endif
|
||||
libzstd.a:
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
ifeq (,$(wildcard ./zstd-$(ZSTD_VER).tar.gz))
|
||||
curl --fail --output zstd-$(ZSTD_VER).tar.gz --location ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(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 \
|
||||
@@ -1863,14 +1826,6 @@ rocksdbjavastaticdockerppc64le:
|
||||
fi
|
||||
docker start -a rocksdb_linux_ppc64le-be
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_ARM64V8_CONTAINER=`docker ps -aqf name=rocksdb_linux_arm64v8-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_ARM64V8_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_arm64v8-be evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_arm64v8-be
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
rocksdbjavastaticpublishdocker: rocksdbjavastaticreleasedocker rocksdbjavastaticpublishcentral
|
||||
@@ -1949,7 +1904,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)
|
||||
@@ -1986,7 +1942,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,16 +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")
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
if sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
@@ -217,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",
|
||||
@@ -258,14 +257,9 @@ 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",
|
||||
@@ -282,9 +276,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",
|
||||
@@ -305,11 +297,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(
|
||||
@@ -322,16 +314,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(
|
||||
@@ -342,22 +331,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]
|
||||
@@ -447,11 +436,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",
|
||||
@@ -522,11 +506,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",
|
||||
@@ -687,11 +666,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",
|
||||
@@ -767,11 +741,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",
|
||||
@@ -802,11 +771,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",
|
||||
@@ -972,11 +936,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",
|
||||
@@ -1102,26 +1061,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
|
||||
]
|
||||
+5
-65
@@ -1,75 +1,15 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
THIRDPARTY_HOME: $(APPVEYOR_BUILD_FOLDER)\thirdparty
|
||||
SNAPPY_HOME: $(THIRDPARTY_HOME)\snappy-1.1.7
|
||||
SNAPPY_INCLUDE: $(SNAPPY_HOME);$(SNAPPY_HOME)\build
|
||||
SNAPPY_LIB_DEBUG: $(SNAPPY_HOME)\build\Debug\snappy.lib
|
||||
SNAPPY_LIB_RELEASE: $(SNAPPY_HOME)\build\Release\snappy.lib
|
||||
LZ4_HOME: $(THIRDPARTY_HOME)\lz4-1.8.3
|
||||
LZ4_INCLUDE: $(LZ4_HOME)\lib
|
||||
LZ4_LIB_DEBUG: $(LZ4_HOME)\visual\VS2010\bin\x64_Debug\liblz4_static.lib
|
||||
LZ4_LIB_RELEASE: $(LZ4_HOME)\visual\VS2010\bin\x64_Release\liblz4_static.lib
|
||||
ZSTD_HOME: $(THIRDPARTY_HOME)\zstd-1.4.0
|
||||
ZSTD_INCLUDE: $(ZSTD_HOME)\lib;$(ZSTD_HOME)\lib\dictBuilder
|
||||
ZSTD_LIB_DEBUG: $(ZSTD_HOME)\build\VS2010\bin\x64_Debug\libzstd_static.lib
|
||||
ZSTD_LIB_RELEASE: $(ZSTD_HOME)\build\VS2010\bin\x64_Release\libzstd_static.lib
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: Visual Studio 14 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
- echo "Building Snappy dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output snappy-1.1.7.zip --location https://github.com/google/snappy/archive/1.1.7.zip
|
||||
- unzip snappy-1.1.7.zip
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -G "%CMAKE_GENERATOR%" ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output lz4-1.8.3.zip --location https://github.com/lz4/lz4/archive/v1.8.3.zip
|
||||
- unzip lz4-1.8.3.zip
|
||||
- cd lz4-1.8.3\visual\VS2010
|
||||
- ps: $CMD="$Env:DEV_ENV"; & $CMD lz4.sln /upgrade
|
||||
- msbuild lz4.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild lz4.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building ZStd dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output zstd-1.4.0.zip --location https://github.com/facebook/zstd/archive/v1.4.0.zip
|
||||
- unzip zstd-1.4.0.zip
|
||||
- cd zstd-1.4.0\build\VS2010
|
||||
- ps: $CMD="$Env:DEV_ENV"; & $CMD zstd.sln /upgrade
|
||||
- msbuild zstd.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild zstd.sln /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: normal
|
||||
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test,db_merge_operand_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
+30
-44
@@ -3,14 +3,11 @@ 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(":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",
|
||||
@@ -37,7 +34,7 @@ rocksdb_compiler_flags = [
|
||||
"-Wnarrowing",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
@@ -49,13 +46,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,16 +65,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")
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
if sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
"""
|
||||
|
||||
|
||||
@@ -86,11 +82,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,
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -98,11 +94,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,
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -121,27 +117,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,13 +53,11 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -370,6 +368,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
|
||||
@@ -527,8 +535,6 @@ if test -z "$PORTABLE"; then
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^arm`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" != "AIX" ] && [ "$TARGET_OS" != "SunOS" ]; then
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/60d6f124a78798b73944f5ba87c2306ae3460153/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/3ee276cbacfad3074e3f07bf826ac47f06970f4e/1.3.5/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/9c910d36d6235cc40e8ff559358f1833452300ca/master/platform007/5b0f53e
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/92ff90349e2f43ea0a8246d8b1cf17b6869013e3/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -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/"
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -377,7 +377,7 @@ function send_to_ods {
|
||||
echo >&2 "ERROR: Key $key doesn't have a value."
|
||||
return
|
||||
fi
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
|
||||
@@ -85,9 +85,7 @@ NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
# in gcc-5 there are known problems with TSAN like https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71090.
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
TSAN="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"
|
||||
@@ -646,7 +644,7 @@ run_regression()
|
||||
|
||||
# parameters: $1 -- key, $2 -- value
|
||||
function send_size_to_ods {
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
|
||||
@@ -53,45 +53,6 @@ function get_lib_base()
|
||||
log_variable $__res_var
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# platform007 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 7.x platform007
|
||||
get_lib_base glibc 2.26 platform007
|
||||
get_lib_base snappy LATEST platform007
|
||||
get_lib_base zlib LATEST platform007
|
||||
get_lib_base bzip2 LATEST platform007
|
||||
get_lib_base lz4 LATEST platform007
|
||||
get_lib_base zstd LATEST platform007
|
||||
get_lib_base gflags LATEST platform007
|
||||
get_lib_base jemalloc LATEST platform007
|
||||
get_lib_base numa LATEST platform007
|
||||
get_lib_base libunwind LATEST platform007
|
||||
get_lib_base tbb LATEST platform007
|
||||
|
||||
get_lib_base kernel-headers fb platform007
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform007
|
||||
get_lib_base lua 5.3.4 platform007
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
###########################################################
|
||||
|
||||
@@ -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)
|
||||
@@ -1784,11 +1784,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 +2067,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;
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
|
||||
+3
-7
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2042,7 +2042,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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+2
-2
@@ -373,8 +373,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;
|
||||
|
||||
+94
-46
@@ -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 {
|
||||
|
||||
@@ -61,26 +78,28 @@ CompactionIterator::CompactionIterator(
|
||||
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) {
|
||||
@@ -91,6 +110,7 @@ CompactionIterator::CompactionIterator(
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:AfterInit", compaction_.get());
|
||||
}
|
||||
|
||||
CompactionIterator::~CompactionIterator() {
|
||||
@@ -162,10 +182,7 @@ void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
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_))))) {
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(ikey_.sequence, latest_snapshot_))) {
|
||||
// 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 +258,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 +287,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 +309,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 +393,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 +406,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 +417,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 +455,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 +474,34 @@ 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.
|
||||
assert(last_snapshot == current_user_key_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
snapshot_checker_->CheckInSnapshot(current_user_key_sequence_,
|
||||
last_snapshot) ==
|
||||
SnapshotCheckerResult::kSnapshotReleased));
|
||||
|
||||
++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 +538,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,13 +632,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 &&
|
||||
if ((compaction_ != nullptr && !compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() && bottommost_level_ && valid_ &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) && ikey_.type != kTypeMerge &&
|
||||
!cmp_->Equal(compaction_->GetLargestUserKey(), ikey_.user_key)) {
|
||||
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
||||
ikey_.sequence = 0;
|
||||
@@ -642,12 +653,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 +683,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"
|
||||
@@ -132,10 +133,25 @@ 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_;
|
||||
@@ -151,6 +167,7 @@ class CompactionIterator {
|
||||
bool visible_at_tip_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
|
||||
bool ignore_snapshots_;
|
||||
|
||||
// State
|
||||
|
||||
@@ -190,13 +190,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:
|
||||
|
||||
+37
-24
@@ -1189,10 +1189,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
|
||||
@@ -1204,19 +1206,10 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
lower_bound = nullptr;
|
||||
}
|
||||
if (next_table_min_key != nullptr) {
|
||||
// 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.
|
||||
// This isn't the last file in the subcompaction, so extend until the next
|
||||
// file starts.
|
||||
upper_bound_guard = ExtractUserKey(*next_table_min_key);
|
||||
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;
|
||||
}
|
||||
upper_bound = &upper_bound_guard;
|
||||
} else {
|
||||
// This is the last file in the subcompaction, so extend until the
|
||||
// subcompaction ends.
|
||||
@@ -1234,13 +1227,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
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
|
||||
@@ -1286,11 +1272,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 &&
|
||||
@@ -1312,9 +1311,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();
|
||||
}
|
||||
|
||||
@@ -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", 2U, 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", 2U, kTypeValue), test::EncodeInt(2U)}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
|
||||
@@ -59,6 +59,9 @@ 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",
|
||||
@@ -85,8 +88,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 +97,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 +621,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) {
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
@@ -956,6 +957,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);
|
||||
@@ -631,6 +631,74 @@ TEST_F(DBBlockCacheTest, CompressedCache) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
const int kNumFiles = 4;
|
||||
const int kNumEntriesPerFile = 32;
|
||||
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
|
||||
|
||||
+205
-9
@@ -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"
|
||||
|
||||
@@ -412,6 +414,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 +440,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 +469,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 +491,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 +504,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 +516,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 +1603,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 +3900,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),
|
||||
|
||||
+118
-47
@@ -1216,6 +1216,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);
|
||||
|
||||
@@ -1330,6 +1331,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);
|
||||
|
||||
@@ -1338,33 +1340,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;
|
||||
@@ -1394,7 +1459,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));
|
||||
@@ -1430,24 +1495,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);
|
||||
@@ -1866,7 +1921,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();
|
||||
@@ -1905,21 +1960,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) {
|
||||
@@ -2387,9 +2453,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
|
||||
@@ -2472,9 +2538,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;
|
||||
@@ -2864,11 +2930,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();
|
||||
@@ -2919,6 +2989,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
|
||||
}
|
||||
|
||||
@@ -3164,8 +3236,7 @@ Status DBImpl::IngestExternalFile(
|
||||
&mutex_, directories_.GetDbDir());
|
||||
}
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *mutable_cf_options,
|
||||
FlushReason::kExternalFileIngestion);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *mutable_cf_options);
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
|
||||
+47
-13
@@ -65,6 +65,7 @@ class Arena;
|
||||
class ArenaWrappedDBIter;
|
||||
class MemTable;
|
||||
class TableCache;
|
||||
class TaskLimiterToken;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
@@ -191,13 +192,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 +238,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);
|
||||
@@ -692,6 +700,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);
|
||||
|
||||
@@ -1050,7 +1064,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
|
||||
@@ -1103,6 +1118,12 @@ 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();
|
||||
|
||||
@@ -1126,10 +1147,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;
|
||||
|
||||
@@ -1352,7 +1378,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
|
||||
@@ -1419,6 +1445,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_;
|
||||
|
||||
@@ -1458,7 +1486,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
|
||||
@@ -1527,8 +1555,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;
|
||||
@@ -1553,6 +1580,13 @@ 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;
|
||||
#endif
|
||||
|
||||
bool ShouldPurge(uint64_t file_number) const;
|
||||
void MarkAsGrabbedForPurge(uint64_t file_number);
|
||||
|
||||
+211
-101
@@ -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();
|
||||
@@ -115,14 +137,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
|
||||
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();
|
||||
}
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
nullptr /* memtable_id */, env_options_for_compaction_, versions_.get(),
|
||||
@@ -265,14 +285,12 @@ 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;
|
||||
std::vector<FlushJob> jobs;
|
||||
std::vector<MutableCFOptions> all_mutable_cf_options;
|
||||
@@ -310,18 +328,21 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs.back().PickMemTable();
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
autovector<FileMetaData> file_meta;
|
||||
Status s;
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
||||
file_meta.emplace_back();
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const MutableCFOptions& mutable_cf_options =
|
||||
*cfds[i]->GetLatestMutableCFOptions();
|
||||
// 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 */
|
||||
}
|
||||
|
||||
if (logfile_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
@@ -425,21 +446,19 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
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);
|
||||
versions_.get(), &mutex_, file_meta, &job_context->memtables_to_free,
|
||||
directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
@@ -451,7 +470,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
InstallSuperVersionAndScheduleWork(cfds[i],
|
||||
&job_context->superversion_contexts[i],
|
||||
all_mutable_cf_options[i]);
|
||||
*cfds[i]->GetLatestMutableCFOptions());
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Level summary: %s\n",
|
||||
cfds[i]->GetName().c_str(),
|
||||
@@ -467,7 +486,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
NotifyOnFlushCompleted(cfds[i], &file_meta[i], all_mutable_cf_options[i],
|
||||
NotifyOnFlushCompleted(cfds[i], &file_meta[i],
|
||||
*cfds[i]->GetLatestMutableCFOptions(),
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
if (sfm) {
|
||||
std::string file_path = MakeTableFileName(
|
||||
@@ -530,6 +550,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.
|
||||
@@ -581,6 +602,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.
|
||||
@@ -754,7 +776,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;
|
||||
@@ -762,6 +785,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
|
||||
@@ -793,7 +817,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();
|
||||
}
|
||||
@@ -833,7 +857,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)) {
|
||||
@@ -907,18 +932,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_,
|
||||
@@ -928,19 +952,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
|
||||
@@ -961,9 +973,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
|
||||
@@ -977,6 +989,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()) {
|
||||
@@ -1119,36 +1136,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);
|
||||
}
|
||||
@@ -1403,6 +1392,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.
|
||||
@@ -1440,6 +1431,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,
|
||||
@@ -1448,6 +1444,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
}
|
||||
}
|
||||
|
||||
log_buffer.FlushBufferToLog();
|
||||
assert(!manual.in_progress);
|
||||
assert(HasPendingManualCompaction());
|
||||
RemoveManualCompaction(&manual);
|
||||
@@ -1883,6 +1880,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()) {
|
||||
@@ -2141,7 +2164,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
|
||||
@@ -2276,6 +2304,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;
|
||||
@@ -2327,17 +2357,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();
|
||||
}
|
||||
|
||||
@@ -2407,6 +2443,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 ==
|
||||
@@ -2423,15 +2461,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(
|
||||
@@ -2473,9 +2515,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(),
|
||||
@@ -2497,6 +2539,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()
|
||||
@@ -2514,22 +2558,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_,
|
||||
@@ -2553,11 +2598,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);
|
||||
@@ -2738,6 +2785,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
|
||||
@@ -2752,10 +2838,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_
|
||||
@@ -2822,4 +2905,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
|
||||
+6
-1
@@ -41,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) {
|
||||
@@ -419,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
+29
-32
@@ -265,19 +265,18 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count,
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size,
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1,
|
||||
concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
write_done_by_other, concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -468,20 +467,20 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count);
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
|
||||
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::kIntStatsWriteDoneBySelf, 1);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF, 1);
|
||||
if (wal_write_group.size > 1) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
@@ -505,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) {
|
||||
@@ -592,16 +591,15 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size,
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1,
|
||||
concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
write_done_by_other, concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -910,12 +908,12 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (status.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_SYNCED, 1);
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
}
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size);
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
@@ -961,10 +959,9 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (status.ok()) {
|
||||
const bool concurrent = true;
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size,
|
||||
concurrent);
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size, concurrent);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal,
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
@@ -1051,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;
|
||||
}
|
||||
@@ -1258,8 +1255,8 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
}
|
||||
assert(!delayed || !write_options.no_slowdown);
|
||||
if (delayed) {
|
||||
default_cf_internal_stats_->AddDBStats(
|
||||
InternalStats::kIntStatsWriteStallMicros, time_delayed);
|
||||
default_cf_internal_stats_->AddDBStats(InternalStats::WRITE_STALL_MICROS,
|
||||
time_delayed);
|
||||
RecordTick(stats_, STALL_MICROS, time_delayed);
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -547,8 +548,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;
|
||||
|
||||
@@ -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"));
|
||||
|
||||
+11
-2
@@ -1138,6 +1138,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) {
|
||||
@@ -1159,8 +1167,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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
+2
-2
@@ -2606,8 +2606,8 @@ class ModelDB : public DB {
|
||||
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.");
|
||||
}
|
||||
|
||||
|
||||
+183
-6
@@ -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();
|
||||
@@ -1345,11 +1346,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(); }
|
||||
virtual 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 +1425,7 @@ class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
}
|
||||
|
||||
bool infinite_max_files_;
|
||||
bool disallow_preload_;
|
||||
};
|
||||
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
@@ -1476,12 +1516,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 +1564,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 +1582,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) {
|
||||
@@ -1646,6 +1736,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_EQ(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;
|
||||
@@ -3088,6 +3214,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) {
|
||||
|
||||
@@ -604,6 +604,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 +763,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;
|
||||
|
||||
@@ -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_;
|
||||
@@ -833,6 +840,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();
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace rocksdb {
|
||||
class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_test") {
|
||||
ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_basic_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
+6
-12
@@ -308,9 +308,7 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
k++;
|
||||
}
|
||||
HistogramData hist;
|
||||
std::vector<FileMetaData> file_metas;
|
||||
// Call reserve to avoid auto-resizing
|
||||
file_metas.reserve(flush_jobs.size());
|
||||
autovector<FileMetaData> file_metas;
|
||||
mutex_.Lock();
|
||||
for (auto& job : flush_jobs) {
|
||||
job.PickMemTable();
|
||||
@@ -321,10 +319,6 @@ 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();
|
||||
@@ -337,7 +331,7 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
|
||||
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,
|
||||
versions_.get(), &mutex_, file_metas, &job_context.memtables_to_free,
|
||||
nullptr /* db_directory */, nullptr /* log_buffer */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -371,17 +365,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;
|
||||
|
||||
+8
-11
@@ -949,17 +949,14 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
seconds_up, interval_seconds_up);
|
||||
value->append(buf);
|
||||
// Cumulative
|
||||
uint64_t user_bytes_written =
|
||||
GetDBStats(InternalStats::kIntStatsBytesWritten);
|
||||
uint64_t num_keys_written =
|
||||
GetDBStats(InternalStats::kIntStatsNumKeysWritten);
|
||||
uint64_t write_other = GetDBStats(InternalStats::kIntStatsWriteDoneByOther);
|
||||
uint64_t write_self = GetDBStats(InternalStats::kIntStatsWriteDoneBySelf);
|
||||
uint64_t wal_bytes = GetDBStats(InternalStats::kIntStatsWalFileBytes);
|
||||
uint64_t wal_synced = GetDBStats(InternalStats::kIntStatsWalFileSynced);
|
||||
uint64_t write_with_wal = GetDBStats(InternalStats::kIntStatsWriteWithWal);
|
||||
uint64_t write_stall_micros =
|
||||
GetDBStats(InternalStats::kIntStatsWriteStallMicros);
|
||||
uint64_t user_bytes_written = GetDBStats(InternalStats::BYTES_WRITTEN);
|
||||
uint64_t num_keys_written = GetDBStats(InternalStats::NUMBER_KEYS_WRITTEN);
|
||||
uint64_t write_other = GetDBStats(InternalStats::WRITE_DONE_BY_OTHER);
|
||||
uint64_t write_self = GetDBStats(InternalStats::WRITE_DONE_BY_SELF);
|
||||
uint64_t wal_bytes = GetDBStats(InternalStats::WAL_FILE_BYTES);
|
||||
uint64_t wal_synced = GetDBStats(InternalStats::WAL_FILE_SYNCED);
|
||||
uint64_t write_with_wal = GetDBStats(InternalStats::WRITE_WITH_WAL);
|
||||
uint64_t write_stall_micros = GetDBStats(InternalStats::WRITE_STALL_MICROS);
|
||||
|
||||
const int kHumanMicrosLen = 32;
|
||||
char human_micros[kHumanMicrosLen];
|
||||
|
||||
+20
-20
@@ -108,15 +108,15 @@ class InternalStats {
|
||||
};
|
||||
|
||||
enum InternalDBStatsType {
|
||||
kIntStatsWalFileBytes,
|
||||
kIntStatsWalFileSynced,
|
||||
kIntStatsBytesWritten,
|
||||
kIntStatsNumKeysWritten,
|
||||
kIntStatsWriteDoneByOther,
|
||||
kIntStatsWriteDoneBySelf,
|
||||
kIntStatsWriteWithWal,
|
||||
kIntStatsWriteStallMicros,
|
||||
kIntStatsNumMax,
|
||||
WAL_FILE_BYTES,
|
||||
WAL_FILE_SYNCED,
|
||||
BYTES_WRITTEN,
|
||||
NUMBER_KEYS_WRITTEN,
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
WRITE_STALL_MICROS,
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
InternalStats(int num_levels, Env* env, ColumnFamilyData* cfd)
|
||||
@@ -292,7 +292,7 @@ class InternalStats {
|
||||
};
|
||||
|
||||
void Clear() {
|
||||
for (int i = 0; i < kIntStatsNumMax; i++) {
|
||||
for (int i = 0; i < INTERNAL_DB_STATS_ENUM_MAX; i++) {
|
||||
db_stats_[i].store(0);
|
||||
}
|
||||
for (int i = 0; i < INTERNAL_CF_STATS_ENUM_MAX; i++) {
|
||||
@@ -382,7 +382,7 @@ class InternalStats {
|
||||
bool HandleBlockCacheStat(Cache** block_cache);
|
||||
|
||||
// Per-DB stats
|
||||
std::atomic<uint64_t> db_stats_[kIntStatsNumMax];
|
||||
std::atomic<uint64_t> db_stats_[INTERNAL_DB_STATS_ENUM_MAX];
|
||||
// Per-ColumnFamily stats
|
||||
uint64_t cf_stats_value_[INTERNAL_CF_STATS_ENUM_MAX];
|
||||
uint64_t cf_stats_count_[INTERNAL_CF_STATS_ENUM_MAX];
|
||||
@@ -580,15 +580,15 @@ class InternalStats {
|
||||
};
|
||||
|
||||
enum InternalDBStatsType {
|
||||
kIntStatsWalFileBytes,
|
||||
kIntStatsWalFileSynced,
|
||||
kIntStatsBytesWritten,
|
||||
kIntStatsNumKeysWritten,
|
||||
kIntStatsWriteDoneByOther,
|
||||
kIntStatsWriteDoneBySelf,
|
||||
kIntStatsWriteWithWal,
|
||||
kIntStatsWriteStallMicros,
|
||||
kIntStatsNumMax,
|
||||
WAL_FILE_BYTES,
|
||||
WAL_FILE_SYNCED,
|
||||
BYTES_WRITTEN,
|
||||
NUMBER_KEYS_WRITTEN,
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
WRITE_STALL_MICROS,
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
InternalStats(int /*num_levels*/, Env* /*env*/, ColumnFamilyData* /*cfd*/) {}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+8
-7
@@ -110,10 +110,10 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
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));
|
||||
prefix_bloom_.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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +414,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(
|
||||
@@ -563,8 +564,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;
|
||||
|
||||
+1
-1
@@ -411,7 +411,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_;
|
||||
|
||||
+2
-3
@@ -533,7 +533,7 @@ Status InstallMemtableAtomicFlushResults(
|
||||
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,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData>& file_metas,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -553,11 +553,10 @@ Status InstallMemtableAtomicFlushResults(
|
||||
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());
|
||||
(*mems_list[k])[i]->SetFileNumber(file_metas[k].fd.GetNumber());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -123,7 +123,7 @@ class MemTableListVersion {
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<FileMetaData>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -301,7 +301,7 @@ class MemTableList {
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<FileMetaData>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -337,7 +337,7 @@ extern Status InstallMemtableAtomicFlushResults(
|
||||
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,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
} // namespace rocksdb
|
||||
@@ -161,23 +161,18 @@ class MemTableListTest : public testing::Test {
|
||||
cfds.emplace_back(column_family_set->GetColumnFamily(cf_ids[i]));
|
||||
EXPECT_NE(nullptr, cfds[i]);
|
||||
}
|
||||
std::vector<FileMetaData> file_metas;
|
||||
file_metas.reserve(cf_ids.size());
|
||||
autovector<FileMetaData> file_metas;
|
||||
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);
|
||||
}
|
||||
autovector<FileMetaData*> file_meta_ptrs;
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return InstallMemtableAtomicFlushResults(
|
||||
&lists, cfds, mutable_cf_options_list, mems_list, &versions, &mutex,
|
||||
file_meta_ptrs, to_delete, nullptr, &log_buffer);
|
||||
file_metas, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+19
-15
@@ -166,9 +166,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;
|
||||
}
|
||||
|
||||
@@ -280,22 +282,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)
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -696,6 +696,10 @@ TEST_F(PerfContextTest, PerfContextByLevelGetSet) {
|
||||
PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 7);
|
||||
PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, 7);
|
||||
PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_true_positive, 1, 2);
|
||||
PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 1, 0);
|
||||
PERF_COUNTER_BY_LEVEL_ADD(block_cache_hit_count, 5, 2);
|
||||
PERF_COUNTER_BY_LEVEL_ADD(block_cache_miss_count, 2, 3);
|
||||
PERF_COUNTER_BY_LEVEL_ADD(block_cache_miss_count, 4, 1);
|
||||
ASSERT_EQ(
|
||||
0, (*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
ASSERT_EQ(
|
||||
@@ -706,6 +710,14 @@ TEST_F(PerfContextTest, PerfContextByLevelGetSet) {
|
||||
.bloom_filter_full_positive);
|
||||
ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[2]
|
||||
.bloom_filter_full_true_positive);
|
||||
ASSERT_EQ(1, (*(get_perf_context()->level_to_perf_context))[0]
|
||||
.block_cache_hit_count);
|
||||
ASSERT_EQ(5, (*(get_perf_context()->level_to_perf_context))[2]
|
||||
.block_cache_hit_count);
|
||||
ASSERT_EQ(2, (*(get_perf_context()->level_to_perf_context))[3]
|
||||
.block_cache_miss_count);
|
||||
ASSERT_EQ(4, (*(get_perf_context()->level_to_perf_context))[1]
|
||||
.block_cache_miss_count);
|
||||
std::string zero_excluded = get_perf_context()->ToString(true);
|
||||
ASSERT_NE(std::string::npos,
|
||||
zero_excluded.find("bloom_filter_useful = 1@level5, 2@level7"));
|
||||
@@ -713,6 +725,10 @@ TEST_F(PerfContextTest, PerfContextByLevelGetSet) {
|
||||
zero_excluded.find("bloom_filter_full_positive = 1@level0"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
zero_excluded.find("bloom_filter_full_true_positive = 1@level2"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
zero_excluded.find("block_cache_hit_count = 1@level0, 5@level2"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
zero_excluded.find("block_cache_miss_count = 4@level1, 2@level3"));
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -158,6 +158,8 @@ class PlainTableDBTest : public testing::Test,
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
bool mmap_mode() const { return mmap_mode_; }
|
||||
|
||||
void DestroyAndReopen(Options* options = nullptr) {
|
||||
//Destroy using last options
|
||||
Destroy(&last_options_);
|
||||
@@ -174,6 +176,12 @@ class PlainTableDBTest : public testing::Test,
|
||||
return DB::Open(*options, dbname_, db);
|
||||
}
|
||||
|
||||
Status ReopenForReadOnly(Options* options) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
return DB::OpenForReadOnly(*options, dbname_, &db_);
|
||||
}
|
||||
|
||||
Status TryReopen(Options* options = nullptr) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
@@ -547,6 +555,50 @@ TEST_P(PlainTableDBTest, Flush2) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(PlainTableDBTest, Immortal) {
|
||||
for (EncodingType encoding_type : {kPlain, kPrefix}) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.max_open_files = -1;
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
PlainTableOptions plain_table_options;
|
||||
plain_table_options.hash_table_ratio = 0.75;
|
||||
plain_table_options.index_sparseness = 16;
|
||||
plain_table_options.user_key_len = kPlainTableVariableLength;
|
||||
plain_table_options.bloom_bits_per_key = 10;
|
||||
plain_table_options.encoding_type = encoding_type;
|
||||
options.table_factory.reset(NewPlainTableFactory(plain_table_options));
|
||||
|
||||
DestroyAndReopen(&options);
|
||||
ASSERT_OK(Put("0000000000000bar", "b"));
|
||||
ASSERT_OK(Put("1000000000000foo", "v1"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
int copied = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"GetContext::SaveValue::PinSelf", [&](void* /*arg*/) { copied++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ("b", Get("0000000000000bar"));
|
||||
ASSERT_EQ("v1", Get("1000000000000foo"));
|
||||
ASSERT_EQ(2, copied);
|
||||
copied = 0;
|
||||
|
||||
Close();
|
||||
ASSERT_OK(ReopenForReadOnly(&options));
|
||||
|
||||
ASSERT_EQ("b", Get("0000000000000bar"));
|
||||
ASSERT_EQ("v1", Get("1000000000000foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("1000000000000bar"));
|
||||
if (mmap_mode()) {
|
||||
ASSERT_EQ(0, copied);
|
||||
} else {
|
||||
ASSERT_EQ(2, copied);
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(PlainTableDBTest, Iterator) {
|
||||
for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024;
|
||||
huge_page_tlb_size += 2 * 1024 * 1024) {
|
||||
|
||||
@@ -470,8 +470,6 @@ CompactionRangeDelAggregator::NewIterator(const Slice* lower_bound,
|
||||
new TruncatedRangeDelMergingIter(icmp_, lower_bound, upper_bound,
|
||||
upper_bound_inclusive, parent_iters_));
|
||||
|
||||
// TODO: add tests where tombstone fragments can be outside of upper and lower
|
||||
// bound range
|
||||
auto fragmented_tombstone_list =
|
||||
std::make_shared<FragmentedRangeTombstoneList>(
|
||||
std::move(merging_iter), *icmp_, true /* for_compaction */,
|
||||
|
||||
@@ -197,7 +197,8 @@ int main(int argc, char** argv) {
|
||||
// real workloads.
|
||||
for (int j = 0; j < FLAGS_num_range_tombstones; j++) {
|
||||
uint64_t start = rnd.Uniform(FLAGS_tombstone_start_upper_bound);
|
||||
uint64_t end = start + static_cast<uint64_t>(std::max(1.0, normal_dist(random_gen)));
|
||||
uint64_t end = static_cast<uint64_t>(
|
||||
std::round(start + std::max(1.0, normal_dist(random_gen))));
|
||||
persistent_range_tombstones[j] = rocksdb::PersistentRangeTombstone(
|
||||
rocksdb::Key(start), rocksdb::Key(end), j);
|
||||
}
|
||||
|
||||
+16
-8
@@ -8,22 +8,30 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Callback class that control GC of duplicate keys in flush/compaction
|
||||
enum class SnapshotCheckerResult : int {
|
||||
kInSnapshot = 0,
|
||||
kNotInSnapshot = 1,
|
||||
// In case snapshot is released and the checker has no clue whether
|
||||
// the given sequence is visible to the snapshot.
|
||||
kSnapshotReleased = 2,
|
||||
};
|
||||
|
||||
// Callback class that control GC of duplicate keys in flush/compaction.
|
||||
class SnapshotChecker {
|
||||
public:
|
||||
virtual ~SnapshotChecker() {}
|
||||
virtual bool IsInSnapshot(SequenceNumber sequence,
|
||||
SequenceNumber snapshot_sequence) const = 0;
|
||||
virtual SnapshotCheckerResult CheckInSnapshot(
|
||||
SequenceNumber sequence, SequenceNumber snapshot_sequence) const = 0;
|
||||
};
|
||||
|
||||
class DisableGCSnapshotChecker : public SnapshotChecker {
|
||||
public:
|
||||
virtual ~DisableGCSnapshotChecker() {}
|
||||
virtual bool IsInSnapshot(
|
||||
virtual SnapshotCheckerResult CheckInSnapshot(
|
||||
SequenceNumber /*sequence*/,
|
||||
SequenceNumber /*snapshot_sequence*/) const override {
|
||||
// By returning false, we prevent all the values from being GCed
|
||||
return false;
|
||||
// By returning kNotInSnapshot, we prevent all the values from being GCed
|
||||
return SnapshotCheckerResult::kNotInSnapshot;
|
||||
}
|
||||
static DisableGCSnapshotChecker* Instance() { return &instance_; }
|
||||
|
||||
@@ -41,8 +49,8 @@ class WritePreparedSnapshotChecker : public SnapshotChecker {
|
||||
explicit WritePreparedSnapshotChecker(WritePreparedTxnDB* txn_db);
|
||||
virtual ~WritePreparedSnapshotChecker() {}
|
||||
|
||||
virtual bool IsInSnapshot(SequenceNumber sequence,
|
||||
SequenceNumber snapshot_sequence) const override;
|
||||
virtual SnapshotCheckerResult CheckInSnapshot(
|
||||
SequenceNumber sequence, SequenceNumber snapshot_sequence) const override;
|
||||
|
||||
private:
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
+5
-2
@@ -86,7 +86,7 @@ class SnapshotList {
|
||||
}
|
||||
|
||||
// retrieve all snapshot numbers up until max_seq. They are sorted in
|
||||
// ascending order.
|
||||
// ascending order (with no duplicates).
|
||||
std::vector<SequenceNumber> GetAll(
|
||||
SequenceNumber* oldest_write_conflict_snapshot = nullptr,
|
||||
const SequenceNumber& max_seq = kMaxSequenceNumber) const {
|
||||
@@ -104,7 +104,10 @@ class SnapshotList {
|
||||
if (s->next_->number_ > max_seq) {
|
||||
break;
|
||||
}
|
||||
ret.push_back(s->next_->number_);
|
||||
// Avoid duplicates
|
||||
if (ret.empty() || ret.back() != s->next_->number_) {
|
||||
ret.push_back(s->next_->number_);
|
||||
}
|
||||
|
||||
if (oldest_write_conflict_snapshot != nullptr &&
|
||||
*oldest_write_conflict_snapshot == kMaxSequenceNumber &&
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ Status TableCache::FindTable(const EnvOptions& env_options,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level,
|
||||
bool prefetch_index_and_filter_in_cache) {
|
||||
PERF_TIMER_GUARD(find_table_nanos);
|
||||
PERF_TIMER_GUARD_WITH_ENV(find_table_nanos, ioptions_.env);
|
||||
Status s;
|
||||
uint64_t number = fd.GetNumber();
|
||||
Slice key = GetSliceForFileNumber(&number);
|
||||
|
||||
@@ -122,6 +122,8 @@ class TableCache {
|
||||
// Release the handle from a cache
|
||||
void ReleaseHandle(Cache::Handle* handle);
|
||||
|
||||
Cache* get_cache() const { return cache_; }
|
||||
|
||||
// Capacity of the backing Cache that indicates inifinite TableCache capacity.
|
||||
// For example when max_open_files is -1 we set the backing Cache to this.
|
||||
static const int kInfiniteCapacity = 0x400000;
|
||||
|
||||
+41
-1
@@ -366,8 +366,40 @@ class VersionBuilder::Rep {
|
||||
|
||||
void LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
assert(table_cache_ != nullptr);
|
||||
|
||||
size_t table_cache_capacity = table_cache_->get_cache()->GetCapacity();
|
||||
bool always_load = (table_cache_capacity == TableCache::kInfiniteCapacity);
|
||||
size_t max_load = port::kMaxSizet;
|
||||
|
||||
if (!always_load) {
|
||||
// If it is initial loading and not set to always laoding all the
|
||||
// files, we only load up to kInitialLoadLimit files, to limit the
|
||||
// time reopening the DB.
|
||||
const size_t kInitialLoadLimit = 16;
|
||||
size_t load_limit;
|
||||
// If the table cache is not 1/4 full, we pin the table handle to
|
||||
// file metadata to avoid the cache read costs when reading the file.
|
||||
// The downside of pinning those files is that LRU won't be followed
|
||||
// for those files. This doesn't matter much because if number of files
|
||||
// of the DB excceeds table cache capacity, eventually no table reader
|
||||
// will be pinned and LRU will be followed.
|
||||
if (is_initial_load) {
|
||||
load_limit = std::min(kInitialLoadLimit, table_cache_capacity / 4);
|
||||
} else {
|
||||
load_limit = table_cache_capacity / 4;
|
||||
}
|
||||
|
||||
size_t table_cache_usage = table_cache_->get_cache()->GetUsage();
|
||||
if (table_cache_usage >= load_limit) {
|
||||
return;
|
||||
} else {
|
||||
max_load = load_limit - table_cache_usage;
|
||||
}
|
||||
}
|
||||
|
||||
// <file metadata, level>
|
||||
std::vector<std::pair<FileMetaData*, int>> files_meta;
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
@@ -375,6 +407,12 @@ class VersionBuilder::Rep {
|
||||
auto* file_meta = file_meta_pair.second;
|
||||
assert(!file_meta->table_reader_handle);
|
||||
files_meta.emplace_back(file_meta, level);
|
||||
if (files_meta.size() >= max_load) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (files_meta.size() >= max_load) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,9 +490,11 @@ void VersionBuilder::SaveTo(VersionStorageInfo* vstorage) {
|
||||
void VersionBuilder::LoadTableHandlers(InternalStats* internal_stats,
|
||||
int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
rep_->LoadTableHandlers(internal_stats, max_threads,
|
||||
prefetch_index_and_filter_in_cache, prefix_extractor);
|
||||
prefetch_index_and_filter_in_cache, is_initial_load,
|
||||
prefix_extractor);
|
||||
}
|
||||
|
||||
void VersionBuilder::MaybeAddFile(VersionStorageInfo* vstorage, int level,
|
||||
|
||||
@@ -35,6 +35,7 @@ class VersionBuilder {
|
||||
void SaveTo(VersionStorageInfo* vstorage);
|
||||
void LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor);
|
||||
void MaybeAddFile(VersionStorageInfo* vstorage, int level, FileMetaData* f);
|
||||
|
||||
|
||||
+1
-1
@@ -263,7 +263,7 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input) {
|
||||
break;
|
||||
}
|
||||
if (!GetLengthPrefixedSlice(input, &field)) {
|
||||
return "new-file4 custom field lenth prefixed slice error";
|
||||
return "new-file4 custom field length prefixed slice error";
|
||||
}
|
||||
switch (custom_tag) {
|
||||
case kPathId:
|
||||
|
||||
+11
-14
@@ -1887,9 +1887,9 @@ void SortFileByOverlappingRatio(
|
||||
next_level_it++;
|
||||
}
|
||||
|
||||
assert(file->fd.file_size != 0);
|
||||
assert(file->compensated_file_size != 0);
|
||||
file_to_order[file->fd.GetNumber()] =
|
||||
overlapping_bytes * 1024u / file->fd.file_size;
|
||||
overlapping_bytes * 1024u / file->compensated_file_size;
|
||||
}
|
||||
|
||||
std::sort(temp->begin(), temp->end(),
|
||||
@@ -3013,9 +3013,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
mu->Unlock();
|
||||
|
||||
TEST_SYNC_POINT("VersionSet::LogAndApply:WriteManifest");
|
||||
if (!first_writer.edit_list.front()->IsColumnFamilyManipulation() &&
|
||||
column_family_set_->get_table_cache()->GetCapacity() ==
|
||||
TableCache::kInfiniteCapacity) {
|
||||
if (!first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
|
||||
for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
|
||||
assert(!builder_guards.empty() &&
|
||||
builder_guards.size() == versions.size());
|
||||
@@ -3025,6 +3023,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
builder_guards[i]->version_builder()->LoadTableHandlers(
|
||||
cfd->internal_stats(), cfd->ioptions()->optimize_filters_for_hits,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
false /* is_initial_load */,
|
||||
mutable_cf_options_ptrs[i]->prefix_extractor.get());
|
||||
}
|
||||
}
|
||||
@@ -3682,15 +3681,13 @@ Status VersionSet::Recover(
|
||||
assert(builders_iter != builders.end());
|
||||
auto* builder = builders_iter->second->version_builder();
|
||||
|
||||
if (GetColumnFamilySet()->get_table_cache()->GetCapacity() ==
|
||||
TableCache::kInfiniteCapacity) {
|
||||
// unlimited table cache. Pre-load table handle now.
|
||||
// Need to do it out of the mutex.
|
||||
builder->LoadTableHandlers(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
}
|
||||
// unlimited table cache. Pre-load table handle now.
|
||||
// Need to do it out of the mutex.
|
||||
builder->LoadTableHandlers(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
true /* is_initial_load */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
|
||||
Version* v = new Version(cfd, this, env_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ class WriteThread {
|
||||
std::atomic<uint8_t> state; // write under StateMutex() or pre-link
|
||||
WriteGroup* write_group;
|
||||
SequenceNumber sequence; // the sequence number to use for the first key
|
||||
Status status; // status of memtable inserter
|
||||
Status status;
|
||||
Status callback_status; // status returned by callback->Callback()
|
||||
|
||||
std::aligned_storage<sizeof(std::mutex)>::type state_mutex_bytes;
|
||||
|
||||
@@ -31,4 +31,11 @@
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
</script>
|
||||
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
|
||||
<script>!function(d,s,id){
|
||||
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
|
||||
if(!d.getElementById(id)){
|
||||
js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';
|
||||
fjs.parentNode.insertBefore(js,fjs);
|
||||
}
|
||||
}(document, 'script', 'twitter-wjs');
|
||||
</script>
|
||||
@@ -2,4 +2,11 @@
|
||||
<a href="https://twitter.com/{{ include.href }}" class="twitter-follow-button pluginBlock" data-show-count="false">Follow @{{ include.href }}</a>
|
||||
</div>
|
||||
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
|
||||
<script>!function(d,s,id){
|
||||
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
|
||||
if(!d.getElementById(id)){
|
||||
js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';
|
||||
fjs.parentNode.insertBefore(js,fjs);
|
||||
}
|
||||
}(document, 'script', 'twitter-wjs');
|
||||
</script>
|
||||
@@ -1,4 +1,11 @@
|
||||
<div class="pluginWrapper twitterSharePlugin">
|
||||
<a href="https://twitter.com/share" class="twitter-share-button" data-hashtags="{{ site.title| replace: ' ', '' }}">Tweet</a>
|
||||
</div>
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
|
||||
<script>!function(d,s,id){
|
||||
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
|
||||
if(!d.getElementById(id)){js=d.createElement(s);
|
||||
js.id=id;js.src=p+'://platform.twitter.com/widgets.js';
|
||||
fjs.parentNode.insertBefore(js,fjs);
|
||||
}
|
||||
}(document, 'script', 'twitter-wjs');
|
||||
</script>
|
||||
@@ -21,4 +21,11 @@
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));</script>
|
||||
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
|
||||
<script>!function(d,s,id){
|
||||
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
|
||||
if(!d.getElementById(id)){
|
||||
js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';
|
||||
fjs.parentNode.insertBefore(js,fjs);
|
||||
}
|
||||
}(document, 'script', 'twitter-wjs');
|
||||
</script>
|
||||
Vendored
+9
@@ -843,6 +843,15 @@ class PosixEnv : public Env {
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual uint64_t NowCPUNanos() override {
|
||||
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_AIX)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
|
||||
return static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void SleepForMicroseconds(int micros) override { usleep(micros); }
|
||||
|
||||
virtual Status GetHostName(char* name, uint64_t len) override {
|
||||
|
||||
@@ -528,8 +528,8 @@ struct AdvancedColumnFamilyOptions {
|
||||
|
||||
// If level compaction_style = kCompactionStyleLevel, for each level,
|
||||
// which files are prioritized to be picked to compact.
|
||||
// Default: kByCompensatedSize
|
||||
CompactionPri compaction_pri = kByCompensatedSize;
|
||||
// Default: kMinOverlappingRatio
|
||||
CompactionPri compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
// The options needed to support Universal Style compactions
|
||||
//
|
||||
|
||||
+3
-1
@@ -637,7 +637,6 @@ extern ROCKSDB_LIBRARY_API rocksdb_iterator_t* rocksdb_writebatch_wi_create_iter
|
||||
rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* cf);
|
||||
|
||||
|
||||
/* Block based table options */
|
||||
|
||||
extern ROCKSDB_LIBRARY_API rocksdb_block_based_table_options_t*
|
||||
@@ -729,6 +728,9 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_cuckoo_table_factory(
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_set_options(
|
||||
rocksdb_t* db, int count, const char* const keys[], const char* const values[], char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API 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);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API rocksdb_options_t* rocksdb_options_create();
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_destroy(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_increase_parallelism(
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// 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/env.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class ConcurrentTaskLimiter {
|
||||
public:
|
||||
|
||||
virtual ~ConcurrentTaskLimiter() {}
|
||||
|
||||
// Returns a name that identifies this concurrent task limiter.
|
||||
virtual const std::string& GetName() const = 0;
|
||||
|
||||
// Set max concurrent tasks.
|
||||
// limit = 0 means no new task allowed.
|
||||
// limit < 0 means no limitation.
|
||||
virtual void SetMaxOutstandingTask(int32_t limit) = 0;
|
||||
|
||||
// Reset to unlimited max concurrent task.
|
||||
virtual void ResetMaxOutstandingTask() = 0;
|
||||
|
||||
// Returns current outstanding task count.
|
||||
virtual int32_t GetOutstandingTask() const = 0;
|
||||
};
|
||||
|
||||
// Create a ConcurrentTaskLimiter that can be shared with mulitple CFs
|
||||
// across RocksDB instances to control concurrent tasks.
|
||||
//
|
||||
// @param name: Name of the limiter.
|
||||
// @param limit: max concurrent tasks.
|
||||
// limit = 0 means no new task allowed.
|
||||
// limit < 0 means no limitation.
|
||||
extern ConcurrentTaskLimiter* NewConcurrentTaskLimiter(
|
||||
const std::string& name, int32_t limit);
|
||||
|
||||
} // namespace rocksdb
|
||||
+12
-7
@@ -53,6 +53,9 @@ class WriteBatch;
|
||||
class Env;
|
||||
class EventListener;
|
||||
class TraceWriter;
|
||||
#ifdef ROCKSDB_LITE
|
||||
class CompactionJobInfo;
|
||||
#endif
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
@@ -834,18 +837,20 @@ class DB {
|
||||
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) = 0;
|
||||
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) = 0;
|
||||
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
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) {
|
||||
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) {
|
||||
return CompactFiles(compact_options, DefaultColumnFamily(),
|
||||
input_file_names, output_level, output_path_id,
|
||||
output_file_names);
|
||||
output_file_names, compaction_job_info);
|
||||
}
|
||||
|
||||
// This function will wait until all currently running background processes
|
||||
|
||||
@@ -384,6 +384,9 @@ class Env {
|
||||
return NowMicros() * 1000;
|
||||
}
|
||||
|
||||
// 0 indicates not supported.
|
||||
virtual uint64_t NowCPUNanos() { return 0; }
|
||||
|
||||
// Sleep/delay the thread for the prescribed number of micro-seconds.
|
||||
virtual void SleepForMicroseconds(int micros) = 0;
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@ struct FileOperationInfo {
|
||||
};
|
||||
|
||||
struct FlushJobInfo {
|
||||
// the id of the column family
|
||||
uint32_t cf_id;
|
||||
// the name of the column family
|
||||
std::string cf_name;
|
||||
// the path to the newly created file
|
||||
|
||||
@@ -108,6 +108,23 @@ class MergeOperator {
|
||||
Slice& existing_operand;
|
||||
};
|
||||
|
||||
// This function applies a stack of merge operands in chrionological order
|
||||
// on top of an existing value. There are two ways in which this method is
|
||||
// being used:
|
||||
// a) During Get() operation, it used to calculate the final value of a key
|
||||
// b) During compaction, in order to collapse some operands with the based
|
||||
// value.
|
||||
//
|
||||
// Note: The name of the method is somewhat misleading, as both in the cases
|
||||
// of Get() or compaction it may be called on a subset of operands:
|
||||
// K: 0 +1 +2 +7 +4 +5 2 +1 +2
|
||||
// ^
|
||||
// |
|
||||
// snapshot
|
||||
// In the example above, Get(K) operation will call FullMerge with a base
|
||||
// value of 2 and operands [+1, +2]. Compaction process might decide to
|
||||
// collapse the beginning of the history up to the snapshot by performing
|
||||
// full Merge with base value of 0 and operands [+1, +2, +7, +3].
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const;
|
||||
|
||||
@@ -182,11 +199,11 @@ class MergeOperator {
|
||||
// consistent MergeOperator between DB opens.
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// Determines whether the MergeOperator can be called with just a single
|
||||
// Determines whether the PartialMerge can be called with just a single
|
||||
// merge operand.
|
||||
// Override and return true for allowing a single operand. Both FullMergeV2
|
||||
// and PartialMerge/PartialMergeMulti should be overridden and implemented
|
||||
// correctly to handle a single operand.
|
||||
// Override and return true for allowing a single operand. PartialMerge
|
||||
// and PartialMergeMulti should be overridden and implemented
|
||||
// correctly to properly handle a single operand.
|
||||
virtual bool AllowSingleOperand() const { return false; }
|
||||
|
||||
// Allows to control when to invoke a full merge during Get.
|
||||
|
||||
@@ -34,6 +34,7 @@ class Cache;
|
||||
class CompactionFilter;
|
||||
class CompactionFilterFactory;
|
||||
class Comparator;
|
||||
class ConcurrentTaskLimiter;
|
||||
class Env;
|
||||
enum InfoLogLevel : unsigned char;
|
||||
class SstFileManager;
|
||||
@@ -293,6 +294,14 @@ struct ColumnFamilyOptions : public AdvancedColumnFamilyOptions {
|
||||
// Default: empty
|
||||
std::vector<DbPath> cf_paths;
|
||||
|
||||
// Compaction concurrent thread limiter for the column family.
|
||||
// If non-nullptr, use given concurrent thread limiter to control
|
||||
// the max outstanding compaction tasks. Limiter can be shared with
|
||||
// multiple column families across db instances.
|
||||
//
|
||||
// Default: nullptr
|
||||
std::shared_ptr<ConcurrentTaskLimiter> compaction_thread_limiter = nullptr;
|
||||
|
||||
// Create ColumnFamilyOptions with default values for all fields
|
||||
ColumnFamilyOptions();
|
||||
// Create ColumnFamilyOptions from Options
|
||||
|
||||
@@ -35,6 +35,9 @@ struct PerfContextByLevel {
|
||||
// total nanos spent on reading data from SST files
|
||||
uint64_t get_from_table_nanos;
|
||||
|
||||
uint64_t block_cache_hit_count = 0; // total number of block cache hits
|
||||
uint64_t block_cache_miss_count = 0; // total number of block cache misses
|
||||
|
||||
void Reset(); // reset all performance counters to zero
|
||||
};
|
||||
|
||||
@@ -60,6 +63,12 @@ struct PerfContext {
|
||||
uint64_t block_read_count; // total number of block reads (with IO)
|
||||
uint64_t block_read_byte; // total number of bytes from block reads
|
||||
uint64_t block_read_time; // total nanos spent on block reads
|
||||
uint64_t block_cache_index_hit_count; // total number of index block hits
|
||||
uint64_t index_block_read_count; // total number of index block reads
|
||||
uint64_t block_cache_filter_hit_count; // total number of filter block hits
|
||||
uint64_t filter_block_read_count; // total number of filter block reads
|
||||
uint64_t compression_dict_block_read_count; // total number of compression
|
||||
// dictionary block reads
|
||||
uint64_t block_checksum_time; // total nanos spent on block checksum
|
||||
uint64_t block_decompress_time; // total nanos spent on block decompression
|
||||
|
||||
@@ -201,6 +210,9 @@ struct PerfContext {
|
||||
uint64_t env_lock_file_nanos;
|
||||
uint64_t env_unlock_file_nanos;
|
||||
uint64_t env_new_logger_nanos;
|
||||
|
||||
uint64_t get_cpu_nanos;
|
||||
|
||||
std::map<uint32_t, PerfContextByLevel>* level_to_perf_context = nullptr;
|
||||
bool per_level_perf_context_enabled = false;
|
||||
};
|
||||
|
||||
@@ -17,8 +17,11 @@ enum PerfLevel : unsigned char {
|
||||
kEnableCount = 2, // enable only count stats
|
||||
kEnableTimeExceptForMutex = 3, // Other than count stats, also enable time
|
||||
// stats except for mutexes
|
||||
kEnableTime = 4, // enable count and time stats
|
||||
kOutOfBounds = 5 // N.B. Must always be the last value!
|
||||
// Other than time, also measure CPU time counters. Still don't measure
|
||||
// time (neither wall time nor CPU time) for mutexes.
|
||||
kEnableTimeAndCPUTimeExceptForMutex = 4,
|
||||
kEnableTime = 5, // enable count and time stats
|
||||
kOutOfBounds = 6 // N.B. Must always be the last value!
|
||||
};
|
||||
|
||||
// set the perf stats level for current thread
|
||||
|
||||
@@ -81,11 +81,11 @@ class RateLimiter {
|
||||
// Max bytes can be granted in a single burst
|
||||
virtual int64_t GetSingleBurstBytes() const = 0;
|
||||
|
||||
// Total bytes that go though rate limiter
|
||||
// Total bytes that go through rate limiter
|
||||
virtual int64_t GetTotalBytesThrough(
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const = 0;
|
||||
|
||||
// Total # of requests that go though rate limiter
|
||||
// Total # of requests that go through rate limiter
|
||||
virtual int64_t GetTotalRequests(
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const = 0;
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class SSTDumpTool {
|
||||
public:
|
||||
int Run(int argc, char** argv);
|
||||
int Run(int argc, char** argv, Options options = Options());
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -309,7 +309,7 @@ enum Tickers : uint32_t {
|
||||
// # of bytes in the blob files evicted because of BlobDB is full.
|
||||
BLOB_DB_FIFO_BYTES_EVICTED,
|
||||
|
||||
// These coutners indicate a performance issue in WritePrepared transactions.
|
||||
// These counters indicate a performance issue in WritePrepared transactions.
|
||||
// We should not seem them ticking them much.
|
||||
// # of times prepare_mutex_ is acquired in the fast path.
|
||||
TXN_PREPARE_MUTEX_OVERHEAD,
|
||||
@@ -320,12 +320,19 @@ enum Tickers : uint32_t {
|
||||
// # of times snapshot_mutex_ is acquired in the fast path.
|
||||
TXN_SNAPSHOT_MUTEX_OVERHEAD,
|
||||
|
||||
// Number of keys actually found in MultiGet calls (vs number requested by caller)
|
||||
// Number of keys actually found in MultiGet calls (vs number requested by
|
||||
// caller)
|
||||
// NUMBER_MULTIGET_KEYS_READ gives the number requested by caller
|
||||
NUMBER_MULTIGET_KEYS_FOUND,
|
||||
|
||||
NO_ITERATOR_CREATED, // number of iterators created
|
||||
NO_ITERATOR_DELETED, // number of iterators deleted
|
||||
|
||||
BLOCK_CACHE_COMPRESSION_DICT_MISS,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_HIT,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_ADD,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_BYTES_EVICT,
|
||||
TICKER_ENUM_MAX
|
||||
};
|
||||
|
||||
@@ -430,6 +437,7 @@ struct HistogramData {
|
||||
double max = 0.0;
|
||||
uint64_t count = 0;
|
||||
uint64_t sum = 0;
|
||||
double min = 0.0;
|
||||
};
|
||||
|
||||
enum StatsLevel {
|
||||
|
||||
@@ -62,6 +62,10 @@ struct BlockBasedTableOptions {
|
||||
// TODO(kailiu) Temporarily disable this feature by making the default value
|
||||
// to be false.
|
||||
//
|
||||
// TODO(ajkr) we need to update names of variables controlling meta-block
|
||||
// caching as they should now apply to range tombstone and compression
|
||||
// dictionary meta-blocks, in addition to index and filter meta-blocks.
|
||||
//
|
||||
// Indicating if we'd put index/filter blocks to the block cache.
|
||||
// If not specified, each "table reader" object will pre-load index/filter
|
||||
// block during table initialization.
|
||||
@@ -454,8 +458,8 @@ class TableFactory {
|
||||
// and cache the table object returned.
|
||||
// (2) SstFileDumper (for SST Dump) opens the table and dump the table
|
||||
// contents using the iterator of the table.
|
||||
// (3) DBImpl::IngestExternalFile() calls this function to read the contents of
|
||||
// the sst file it's attempting to add
|
||||
// (3) DBImpl::IngestExternalFile() calls this function to read the contents
|
||||
// of the sst file it's attempting to add
|
||||
//
|
||||
// table_reader_options is a TableReaderOptions which contain all the
|
||||
// needed parameters and configuration to open the table.
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Date tiered database is a wrapper of DB that implements
|
||||
// a simplified DateTieredCompactionStrategy by using multiple column famillies
|
||||
// as time windows.
|
||||
//
|
||||
// DateTieredDB provides an interface similar to DB, but it assumes that user
|
||||
// provides keys with last 8 bytes encoded as timestamp in seconds. DateTieredDB
|
||||
// is assigned with a TTL to declare when data should be deleted.
|
||||
//
|
||||
// DateTieredDB hides column families layer from standard RocksDB instance. It
|
||||
// uses multiple column families to manage time series data, each containing a
|
||||
// specific range of time. Column families are named by its maximum possible
|
||||
// timestamp. A column family is created automatically when data newer than
|
||||
// latest timestamp of all existing column families. The time range of a column
|
||||
// family is configurable by `column_family_interval`. By doing this, we
|
||||
// guarantee that compaction will only happen in a column family.
|
||||
//
|
||||
// DateTieredDB is assigned with a TTL. When all data in a column family are
|
||||
// expired (CF_Timestamp <= CUR_Timestamp - TTL), we directly drop the whole
|
||||
// column family.
|
||||
//
|
||||
// TODO(jhli): This is only a simplified version of DTCS. In a complete DTCS,
|
||||
// time windows can be merged over time, so that older time windows will have
|
||||
// larger time range. Also, compaction are executed only for adjacent SST files
|
||||
// to guarantee there is no time overlap between SST files.
|
||||
|
||||
class DateTieredDB {
|
||||
public:
|
||||
// Open a DateTieredDB whose name is `dbname`.
|
||||
// Similar to DB::Open(), created database object is stored in dbptr.
|
||||
//
|
||||
// Two parameters can be configured: `ttl` to specify the length of time that
|
||||
// keys should exist in the database, and `column_family_interval` to specify
|
||||
// the time range of a column family interval.
|
||||
//
|
||||
// Open a read only database if read only is set as true.
|
||||
// TODO(jhli): Should use an option object that includes ttl and
|
||||
// column_family_interval.
|
||||
static Status Open(const Options& options, const std::string& dbname,
|
||||
DateTieredDB** dbptr, int64_t ttl,
|
||||
int64_t column_family_interval, bool read_only = false);
|
||||
|
||||
explicit DateTieredDB() {}
|
||||
|
||||
virtual ~DateTieredDB() {}
|
||||
|
||||
// Wrapper for Put method. Similar to DB::Put(), but column family to be
|
||||
// inserted is decided by the timestamp in keys, i.e. the last 8 bytes of user
|
||||
// key. If key is already obsolete, it will not be inserted.
|
||||
//
|
||||
// When client put a key value pair in DateTieredDB, it assumes last 8 bytes
|
||||
// of keys are encoded as timestamp. Timestamp is a 64-bit signed integer
|
||||
// encoded as the number of seconds since 1970-01-01 00:00:00 (UTC) (Same as
|
||||
// Env::GetCurrentTime()). Timestamp should be encoded in big endian.
|
||||
virtual Status Put(const WriteOptions& options, const Slice& key,
|
||||
const Slice& val) = 0;
|
||||
|
||||
// Wrapper for Get method. Similar to DB::Get() but column family is decided
|
||||
// by timestamp in keys. If key is already obsolete, it will not be found.
|
||||
virtual Status Get(const ReadOptions& options, const Slice& key,
|
||||
std::string* value) = 0;
|
||||
|
||||
// Wrapper for Delete method. Similar to DB::Delete() but column family is
|
||||
// decided by timestamp in keys. If key is already obsolete, return NotFound
|
||||
// status.
|
||||
virtual Status Delete(const WriteOptions& options, const Slice& key) = 0;
|
||||
|
||||
// Wrapper for KeyMayExist method. Similar to DB::KeyMayExist() but column
|
||||
// family is decided by timestamp in keys. Return false when key is already
|
||||
// obsolete.
|
||||
virtual bool KeyMayExist(const ReadOptions& options, const Slice& key,
|
||||
std::string* value, bool* value_found = nullptr) = 0;
|
||||
|
||||
// Wrapper for Merge method. Similar to DB::Merge() but column family is
|
||||
// decided by timestamp in keys.
|
||||
virtual Status Merge(const WriteOptions& options, const Slice& key,
|
||||
const Slice& value) = 0;
|
||||
|
||||
// Create an iterator that hides low level details. This iterator internally
|
||||
// merge results from all active time series column families. Note that
|
||||
// column families are not deleted until all data are obsolete, so this
|
||||
// iterator can possibly access obsolete key value pairs.
|
||||
virtual Iterator* NewIterator(const ReadOptions& opts) = 0;
|
||||
|
||||
// Explicitly drop column families in which all keys are obsolete. This
|
||||
// process is also inplicitly done in Put() operation.
|
||||
virtual Status DropObsoleteColumnFamilies() = 0;
|
||||
|
||||
static const uint64_t kTSLength = sizeof(int64_t); // size of timestamp
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1,149 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/utilities/stackable_db.h"
|
||||
#include "rocksdb/utilities/json_document.h"
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// IMPORTANT: DocumentDB is a work in progress. It is unstable and we might
|
||||
// change the API without warning. Talk to RocksDB team before using this in
|
||||
// production ;)
|
||||
|
||||
// DocumentDB is a layer on top of RocksDB that provides a very simple JSON API.
|
||||
// When creating a DB, you specify a list of indexes you want to keep on your
|
||||
// data. You can insert a JSON document to the DB, which is automatically
|
||||
// indexed. Every document added to the DB needs to have "_id" field which is
|
||||
// automatically indexed and is an unique primary key. All other indexes are
|
||||
// non-unique.
|
||||
|
||||
// NOTE: field names in the JSON are NOT allowed to start with '$' or
|
||||
// contain '.'. We don't currently enforce that rule, but will start behaving
|
||||
// badly.
|
||||
|
||||
// Cursor is what you get as a result of executing query. To get all
|
||||
// results from a query, call Next() on a Cursor while Valid() returns true
|
||||
class Cursor {
|
||||
public:
|
||||
Cursor() = default;
|
||||
virtual ~Cursor() {}
|
||||
|
||||
virtual bool Valid() const = 0;
|
||||
virtual void Next() = 0;
|
||||
// Lifecycle of the returned JSONDocument is until the next Next() call
|
||||
virtual const JSONDocument& document() const = 0;
|
||||
virtual Status status() const = 0;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
Cursor(const Cursor&);
|
||||
void operator=(const Cursor&);
|
||||
};
|
||||
|
||||
struct DocumentDBOptions {
|
||||
int background_threads = 4;
|
||||
uint64_t memtable_size = 128 * 1024 * 1024; // 128 MB
|
||||
uint64_t cache_size = 1 * 1024 * 1024 * 1024; // 1 GB
|
||||
};
|
||||
|
||||
// TODO(icanadi) Add `JSONDocument* info` parameter to all calls that can be
|
||||
// used by the caller to get more information about the call execution (number
|
||||
// of dropped records, number of updated records, etc.)
|
||||
class DocumentDB : public StackableDB {
|
||||
public:
|
||||
struct IndexDescriptor {
|
||||
// Currently, you can only define an index on a single field. To specify an
|
||||
// index on a field X, set index description to JSON "{X: 1}"
|
||||
// Currently the value needs to be 1, which means ascending.
|
||||
// In the future, we plan to also support indexes on multiple keys, where
|
||||
// you could mix ascending sorting (1) with descending sorting indexes (-1)
|
||||
JSONDocument* description;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Open DocumentDB with specified indexes. The list of indexes has to be
|
||||
// complete, i.e. include all indexes present in the DB, except the primary
|
||||
// key index.
|
||||
// Otherwise, Open() will return an error
|
||||
static Status Open(const DocumentDBOptions& options, const std::string& name,
|
||||
const std::vector<IndexDescriptor>& indexes,
|
||||
DocumentDB** db, bool read_only = false);
|
||||
|
||||
explicit DocumentDB(DB* db) : StackableDB(db) {}
|
||||
|
||||
// Create a new index. It will stop all writes for the duration of the call.
|
||||
// All current documents in the DB are scanned and corresponding index entries
|
||||
// are created
|
||||
virtual Status CreateIndex(const WriteOptions& write_options,
|
||||
const IndexDescriptor& index) = 0;
|
||||
|
||||
// Drop an index. Client is responsible to make sure that index is not being
|
||||
// used by currently executing queries
|
||||
virtual Status DropIndex(const std::string& name) = 0;
|
||||
|
||||
// Insert a document to the DB. The document needs to have a primary key "_id"
|
||||
// which can either be a string or an integer. Otherwise the write will fail
|
||||
// with InvalidArgument.
|
||||
virtual Status Insert(const WriteOptions& options,
|
||||
const JSONDocument& document) = 0;
|
||||
|
||||
// Deletes all documents matching a filter atomically
|
||||
virtual Status Remove(const ReadOptions& read_options,
|
||||
const WriteOptions& write_options,
|
||||
const JSONDocument& query) = 0;
|
||||
|
||||
// Does this sequence of operations:
|
||||
// 1. Find all documents matching a filter
|
||||
// 2. For all documents, atomically:
|
||||
// 2.1. apply the update operators
|
||||
// 2.2. update the secondary indexes
|
||||
//
|
||||
// Currently only $set update operator is supported.
|
||||
// Syntax is: {$set: {key1: value1, key2: value2, etc...}}
|
||||
// This operator will change a document's key1 field to value1, key2 to
|
||||
// value2, etc. New values will be set even if a document didn't have an entry
|
||||
// for the specified key.
|
||||
//
|
||||
// You can not change a primary key of a document.
|
||||
//
|
||||
// Update example: Update({id: {$gt: 5}, $index: id}, {$set: {enabled: true}})
|
||||
virtual Status Update(const ReadOptions& read_options,
|
||||
const WriteOptions& write_options,
|
||||
const JSONDocument& filter,
|
||||
const JSONDocument& updates) = 0;
|
||||
|
||||
// query has to be an array in which every element is an operator. Currently
|
||||
// only $filter operator is supported. Syntax of $filter operator is:
|
||||
// {$filter: {key1: condition1, key2: condition2, etc.}} where conditions can
|
||||
// be either:
|
||||
// 1) a single value in which case the condition is equality condition, or
|
||||
// 2) a defined operators, like {$gt: 4}, which will match all documents that
|
||||
// have key greater than 4.
|
||||
//
|
||||
// Supported operators are:
|
||||
// 1) $gt -- greater than
|
||||
// 2) $gte -- greater than or equal
|
||||
// 3) $lt -- less than
|
||||
// 4) $lte -- less than or equal
|
||||
// If you want the filter to use an index, you need to specify it like this:
|
||||
// {$filter: {...(conditions)..., $index: index_name}}
|
||||
//
|
||||
// Example query:
|
||||
// * [{$filter: {name: John, age: {$gte: 18}, $index: age}}]
|
||||
// will return all Johns whose age is greater or equal to 18 and it will use
|
||||
// index "age" to satisfy the query.
|
||||
virtual Cursor* Query(const ReadOptions& read_options,
|
||||
const JSONDocument& query) = 0;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1,114 +0,0 @@
|
||||
// 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).
|
||||
//
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/utilities/stackable_db.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
//
|
||||
// Configurable options needed for setting up a Geo database
|
||||
//
|
||||
struct GeoDBOptions {
|
||||
// Backup info and error messages will be written to info_log
|
||||
// if non-nullptr.
|
||||
// Default: nullptr
|
||||
Logger* info_log;
|
||||
|
||||
explicit GeoDBOptions(Logger* _info_log = nullptr):info_log(_info_log) { }
|
||||
};
|
||||
|
||||
//
|
||||
// A position in the earth's geoid
|
||||
//
|
||||
class GeoPosition {
|
||||
public:
|
||||
double latitude;
|
||||
double longitude;
|
||||
|
||||
explicit GeoPosition(double la = 0, double lo = 0) :
|
||||
latitude(la), longitude(lo) {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Description of an object on the Geoid. It is located by a GPS location,
|
||||
// and is identified by the id. The value associated with this object is
|
||||
// an opaque string 'value'. Different objects identified by unique id's
|
||||
// can have the same gps-location associated with them.
|
||||
//
|
||||
class GeoObject {
|
||||
public:
|
||||
GeoPosition position;
|
||||
std::string id;
|
||||
std::string value;
|
||||
|
||||
GeoObject() {}
|
||||
|
||||
GeoObject(const GeoPosition& pos, const std::string& i,
|
||||
const std::string& val) :
|
||||
position(pos), id(i), value(val) {
|
||||
}
|
||||
};
|
||||
|
||||
class GeoIterator {
|
||||
public:
|
||||
GeoIterator() = default;
|
||||
virtual ~GeoIterator() {}
|
||||
virtual void Next() = 0;
|
||||
virtual bool Valid() const = 0;
|
||||
virtual const GeoObject& geo_object() = 0;
|
||||
virtual Status status() const = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// Stack your DB with GeoDB to be able to get geo-spatial support
|
||||
//
|
||||
class GeoDB : public StackableDB {
|
||||
public:
|
||||
// GeoDBOptions have to be the same as the ones used in a previous
|
||||
// incarnation of the DB
|
||||
//
|
||||
// GeoDB owns the pointer `DB* db` now. You should not delete it or
|
||||
// use it after the invocation of GeoDB
|
||||
// GeoDB(DB* db, const GeoDBOptions& options) : StackableDB(db) {}
|
||||
GeoDB(DB* db, const GeoDBOptions& /*options*/) : StackableDB(db) {}
|
||||
virtual ~GeoDB() {}
|
||||
|
||||
// Insert a new object into the location database. The object is
|
||||
// uniquely identified by the id. If an object with the same id already
|
||||
// exists in the db, then the old one is overwritten by the new
|
||||
// object being inserted here.
|
||||
virtual Status Insert(const GeoObject& object) = 0;
|
||||
|
||||
// Retrieve the value of the object located at the specified GPS
|
||||
// location and is identified by the 'id'.
|
||||
virtual Status GetByPosition(const GeoPosition& pos,
|
||||
const Slice& id, std::string* value) = 0;
|
||||
|
||||
// Retrieve the value of the object identified by the 'id'. This method
|
||||
// could be potentially slower than GetByPosition
|
||||
virtual Status GetById(const Slice& id, GeoObject* object) = 0;
|
||||
|
||||
// Delete the specified object
|
||||
virtual Status Remove(const Slice& id) = 0;
|
||||
|
||||
// Returns an iterator for the items within a circular radius from the
|
||||
// specified gps location. If 'number_of_values' is specified,
|
||||
// then the iterator is capped to that number of objects.
|
||||
// The radius is specified in 'meters'.
|
||||
virtual GeoIterator* SearchRadial(const GeoPosition& pos,
|
||||
double radius,
|
||||
int number_of_values = INT_MAX) = 0;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1,195 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
// We use JSONDocument for DocumentDB API
|
||||
// Implementation inspired by folly::dynamic, rapidjson and fbson
|
||||
|
||||
namespace fbson {
|
||||
class FbsonValue;
|
||||
class ObjectVal;
|
||||
template <typename T>
|
||||
class FbsonWriterT;
|
||||
class FbsonOutStream;
|
||||
typedef FbsonWriterT<FbsonOutStream> FbsonWriter;
|
||||
} // namespace fbson
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// NOTE: none of this is thread-safe
|
||||
class JSONDocument {
|
||||
public:
|
||||
// return nullptr on parse failure
|
||||
static JSONDocument* ParseJSON(const char* json);
|
||||
|
||||
enum Type {
|
||||
kNull,
|
||||
kArray,
|
||||
kBool,
|
||||
kDouble,
|
||||
kInt64,
|
||||
kObject,
|
||||
kString,
|
||||
};
|
||||
|
||||
/* implicit */ JSONDocument(); // null
|
||||
/* implicit */ JSONDocument(bool b);
|
||||
/* implicit */ JSONDocument(double d);
|
||||
/* implicit */ JSONDocument(int8_t i);
|
||||
/* implicit */ JSONDocument(int16_t i);
|
||||
/* implicit */ JSONDocument(int32_t i);
|
||||
/* implicit */ JSONDocument(int64_t i);
|
||||
/* implicit */ JSONDocument(const std::string& s);
|
||||
/* implicit */ JSONDocument(const char* s);
|
||||
// constructs JSONDocument of specific type with default value
|
||||
explicit JSONDocument(Type _type);
|
||||
|
||||
JSONDocument(const JSONDocument& json_document);
|
||||
|
||||
JSONDocument(JSONDocument&& json_document);
|
||||
|
||||
Type type() const;
|
||||
|
||||
// REQUIRES: IsObject()
|
||||
bool Contains(const std::string& key) const;
|
||||
// REQUIRES: IsObject()
|
||||
// Returns non-owner object
|
||||
JSONDocument operator[](const std::string& key) const;
|
||||
|
||||
// REQUIRES: IsArray() == true || IsObject() == true
|
||||
size_t Count() const;
|
||||
|
||||
// REQUIRES: IsArray()
|
||||
// Returns non-owner object
|
||||
JSONDocument operator[](size_t i) const;
|
||||
|
||||
JSONDocument& operator=(JSONDocument jsonDocument);
|
||||
|
||||
bool IsNull() const;
|
||||
bool IsArray() const;
|
||||
bool IsBool() const;
|
||||
bool IsDouble() const;
|
||||
bool IsInt64() const;
|
||||
bool IsObject() const;
|
||||
bool IsString() const;
|
||||
|
||||
// REQUIRES: IsBool() == true
|
||||
bool GetBool() const;
|
||||
// REQUIRES: IsDouble() == true
|
||||
double GetDouble() const;
|
||||
// REQUIRES: IsInt64() == true
|
||||
int64_t GetInt64() const;
|
||||
// REQUIRES: IsString() == true
|
||||
std::string GetString() const;
|
||||
|
||||
bool operator==(const JSONDocument& rhs) const;
|
||||
|
||||
bool operator!=(const JSONDocument& rhs) const;
|
||||
|
||||
JSONDocument Copy() const;
|
||||
|
||||
bool IsOwner() const;
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
class ItemsIteratorGenerator;
|
||||
|
||||
public:
|
||||
// REQUIRES: IsObject()
|
||||
ItemsIteratorGenerator Items() const;
|
||||
|
||||
// appends serialized object to dst
|
||||
void Serialize(std::string* dst) const;
|
||||
// returns nullptr if Slice doesn't represent valid serialized JSONDocument
|
||||
static JSONDocument* Deserialize(const Slice& src);
|
||||
|
||||
private:
|
||||
friend class JSONDocumentBuilder;
|
||||
|
||||
JSONDocument(fbson::FbsonValue* val, bool makeCopy);
|
||||
|
||||
void InitFromValue(const fbson::FbsonValue* val);
|
||||
|
||||
// iteration on objects
|
||||
class const_item_iterator {
|
||||
private:
|
||||
class Impl;
|
||||
public:
|
||||
typedef std::pair<std::string, JSONDocument> value_type;
|
||||
explicit const_item_iterator(Impl* impl);
|
||||
const_item_iterator(const_item_iterator&&);
|
||||
const_item_iterator& operator++();
|
||||
bool operator!=(const const_item_iterator& other);
|
||||
value_type operator*();
|
||||
~const_item_iterator();
|
||||
private:
|
||||
friend class ItemsIteratorGenerator;
|
||||
std::unique_ptr<Impl> it_;
|
||||
};
|
||||
|
||||
class ItemsIteratorGenerator {
|
||||
public:
|
||||
explicit ItemsIteratorGenerator(const fbson::ObjectVal& object);
|
||||
const_item_iterator begin() const;
|
||||
|
||||
const_item_iterator end() const;
|
||||
|
||||
private:
|
||||
const fbson::ObjectVal& object_;
|
||||
};
|
||||
|
||||
std::unique_ptr<char[]> data_;
|
||||
mutable fbson::FbsonValue* value_;
|
||||
|
||||
// Our serialization format's first byte specifies the encoding version. That
|
||||
// way, we can easily change our format while providing backwards
|
||||
// compatibility. This constant specifies the current version of the
|
||||
// serialization format
|
||||
static const char kSerializationFormatVersion;
|
||||
};
|
||||
|
||||
class JSONDocumentBuilder {
|
||||
public:
|
||||
JSONDocumentBuilder();
|
||||
|
||||
explicit JSONDocumentBuilder(fbson::FbsonOutStream* out);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool WriteStartArray();
|
||||
|
||||
bool WriteEndArray();
|
||||
|
||||
bool WriteStartObject();
|
||||
|
||||
bool WriteEndObject();
|
||||
|
||||
bool WriteKeyValue(const std::string& key, const JSONDocument& value);
|
||||
|
||||
bool WriteJSONDocument(const JSONDocument& value);
|
||||
|
||||
JSONDocument GetJSONDocument();
|
||||
|
||||
~JSONDocumentBuilder();
|
||||
|
||||
private:
|
||||
std::unique_ptr<fbson::FbsonWriter> writer_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1,261 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/utilities/stackable_db.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace spatial {
|
||||
|
||||
// NOTE: SpatialDB is experimental and we might change its API without warning.
|
||||
// Please talk to us before developing against SpatialDB API.
|
||||
//
|
||||
// SpatialDB is a support for spatial indexes built on top of RocksDB.
|
||||
// When creating a new SpatialDB, clients specifies a list of spatial indexes to
|
||||
// build on their data. Each spatial index is defined by the area and
|
||||
// granularity. If you're storing map data, different spatial index
|
||||
// granularities can be used for different zoom levels.
|
||||
//
|
||||
// Each element inserted into SpatialDB has:
|
||||
// * a bounding box, which determines how will the element be indexed
|
||||
// * string blob, which will usually be WKB representation of the polygon
|
||||
// (http://en.wikipedia.org/wiki/Well-known_text)
|
||||
// * feature set, which is a map of key-value pairs, where value can be null,
|
||||
// int, double, bool, string
|
||||
// * a list of indexes to insert the element in
|
||||
//
|
||||
// Each query is executed on a single spatial index. Query guarantees that it
|
||||
// will return all elements intersecting the specified bounding box, but it
|
||||
// might also return some extra non-intersecting elements.
|
||||
|
||||
// Variant is a class that can be many things: null, bool, int, double or string
|
||||
// It is used to store different value types in FeatureSet (see below)
|
||||
struct Variant {
|
||||
// Don't change the values here, they are persisted on disk
|
||||
enum Type {
|
||||
kNull = 0x0,
|
||||
kBool = 0x1,
|
||||
kInt = 0x2,
|
||||
kDouble = 0x3,
|
||||
kString = 0x4,
|
||||
};
|
||||
|
||||
Variant() : type_(kNull) {}
|
||||
/* implicit */ Variant(bool b) : type_(kBool) { data_.b = b; }
|
||||
/* implicit */ Variant(uint64_t i) : type_(kInt) { data_.i = i; }
|
||||
/* implicit */ Variant(double d) : type_(kDouble) { data_.d = d; }
|
||||
/* implicit */ Variant(const std::string& s) : type_(kString) {
|
||||
new (&data_.s) std::string(s);
|
||||
}
|
||||
|
||||
Variant(const Variant& v) : type_(v.type_) { Init(v, data_); }
|
||||
|
||||
Variant& operator=(const Variant& v);
|
||||
|
||||
Variant(Variant&& rhs) : type_(kNull) { *this = std::move(rhs); }
|
||||
|
||||
Variant& operator=(Variant&& v);
|
||||
|
||||
~Variant() { Destroy(type_, data_); }
|
||||
|
||||
Type type() const { return type_; }
|
||||
bool get_bool() const { return data_.b; }
|
||||
uint64_t get_int() const { return data_.i; }
|
||||
double get_double() const { return data_.d; }
|
||||
const std::string& get_string() const { return *GetStringPtr(data_); }
|
||||
|
||||
bool operator==(const Variant& other) const;
|
||||
bool operator!=(const Variant& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
union Data {
|
||||
bool b;
|
||||
uint64_t i;
|
||||
double d;
|
||||
// Current version of MS compiler not C++11 compliant so can not put
|
||||
// std::string
|
||||
// however, even then we still need the rest of the maintenance.
|
||||
char s[sizeof(std::string)];
|
||||
} data_;
|
||||
|
||||
// Avoid type_punned aliasing problem
|
||||
static std::string* GetStringPtr(Data& d) {
|
||||
void* p = d.s;
|
||||
return reinterpret_cast<std::string*>(p);
|
||||
}
|
||||
|
||||
static const std::string* GetStringPtr(const Data& d) {
|
||||
const void* p = d.s;
|
||||
return reinterpret_cast<const std::string*>(p);
|
||||
}
|
||||
|
||||
static void Init(const Variant&, Data&);
|
||||
|
||||
static void Destroy(Type t, Data& d) {
|
||||
if (t == kString) {
|
||||
using std::string;
|
||||
GetStringPtr(d)->~string();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// FeatureSet is a map of key-value pairs. One feature set is associated with
|
||||
// each element in SpatialDB. It can be used to add rich data about the element.
|
||||
class FeatureSet {
|
||||
private:
|
||||
typedef std::unordered_map<std::string, Variant> map;
|
||||
|
||||
public:
|
||||
class iterator {
|
||||
public:
|
||||
/* implicit */ iterator(const map::const_iterator itr) : itr_(itr) {}
|
||||
iterator& operator++() {
|
||||
++itr_;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator& other) { return itr_ != other.itr_; }
|
||||
bool operator==(const iterator& other) { return itr_ == other.itr_; }
|
||||
map::value_type operator*() { return *itr_; }
|
||||
|
||||
private:
|
||||
map::const_iterator itr_;
|
||||
};
|
||||
FeatureSet() = default;
|
||||
|
||||
FeatureSet* Set(const std::string& key, const Variant& value);
|
||||
bool Contains(const std::string& key) const;
|
||||
// REQUIRES: Contains(key)
|
||||
const Variant& Get(const std::string& key) const;
|
||||
iterator Find(const std::string& key) const;
|
||||
|
||||
iterator begin() const { return map_.begin(); }
|
||||
iterator end() const { return map_.end(); }
|
||||
|
||||
void Clear();
|
||||
size_t Size() const { return map_.size(); }
|
||||
|
||||
void Serialize(std::string* output) const;
|
||||
// REQUIRED: empty FeatureSet
|
||||
bool Deserialize(const Slice& input);
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
map map_;
|
||||
};
|
||||
|
||||
// BoundingBox is a helper structure for defining rectangles representing
|
||||
// bounding boxes of spatial elements.
|
||||
template <typename T>
|
||||
struct BoundingBox {
|
||||
T min_x, min_y, max_x, max_y;
|
||||
BoundingBox() = default;
|
||||
BoundingBox(T _min_x, T _min_y, T _max_x, T _max_y)
|
||||
: min_x(_min_x), min_y(_min_y), max_x(_max_x), max_y(_max_y) {}
|
||||
|
||||
bool Intersects(const BoundingBox<T>& a) const {
|
||||
return !(min_x > a.max_x || min_y > a.max_y || a.min_x > max_x ||
|
||||
a.min_y > max_y);
|
||||
}
|
||||
};
|
||||
|
||||
struct SpatialDBOptions {
|
||||
uint64_t cache_size = 1 * 1024 * 1024 * 1024LL; // 1GB
|
||||
int num_threads = 16;
|
||||
bool bulk_load = true;
|
||||
};
|
||||
|
||||
// Cursor is used to return data from the query to the client. To get all the
|
||||
// data from the query, just call Next() while Valid() is true
|
||||
class Cursor {
|
||||
public:
|
||||
Cursor() = default;
|
||||
virtual ~Cursor() {}
|
||||
|
||||
virtual bool Valid() const = 0;
|
||||
// REQUIRES: Valid()
|
||||
virtual void Next() = 0;
|
||||
|
||||
// Lifetime of the underlying storage until the next call to Next()
|
||||
// REQUIRES: Valid()
|
||||
virtual const Slice blob() = 0;
|
||||
// Lifetime of the underlying storage until the next call to Next()
|
||||
// REQUIRES: Valid()
|
||||
virtual const FeatureSet& feature_set() = 0;
|
||||
|
||||
virtual Status status() const = 0;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
Cursor(const Cursor&);
|
||||
void operator=(const Cursor&);
|
||||
};
|
||||
|
||||
// SpatialIndexOptions defines a spatial index that will be built on the data
|
||||
struct SpatialIndexOptions {
|
||||
// Spatial indexes are referenced by names
|
||||
std::string name;
|
||||
// An area that is indexed. If the element is not intersecting with spatial
|
||||
// index's bbox, it will not be inserted into the index
|
||||
BoundingBox<double> bbox;
|
||||
// tile_bits control the granularity of the spatial index. Each dimension of
|
||||
// the bbox will be split into (1 << tile_bits) tiles, so there will be a
|
||||
// total of (1 << tile_bits)^2 tiles. It is recommended to configure a size of
|
||||
// each tile to be approximately the size of the query on that spatial index
|
||||
uint32_t tile_bits;
|
||||
SpatialIndexOptions() {}
|
||||
SpatialIndexOptions(const std::string& _name,
|
||||
const BoundingBox<double>& _bbox, uint32_t _tile_bits)
|
||||
: name(_name), bbox(_bbox), tile_bits(_tile_bits) {}
|
||||
};
|
||||
|
||||
class SpatialDB : public StackableDB {
|
||||
public:
|
||||
// Creates the SpatialDB with specified list of indexes.
|
||||
// REQUIRED: db doesn't exist
|
||||
static Status Create(const SpatialDBOptions& options, const std::string& name,
|
||||
const std::vector<SpatialIndexOptions>& spatial_indexes);
|
||||
|
||||
// Open the existing SpatialDB. The resulting db object will be returned
|
||||
// through db parameter.
|
||||
// REQUIRED: db was created using SpatialDB::Create
|
||||
static Status Open(const SpatialDBOptions& options, const std::string& name,
|
||||
SpatialDB** db, bool read_only = false);
|
||||
|
||||
explicit SpatialDB(DB* db) : StackableDB(db) {}
|
||||
|
||||
// Insert the element into the DB. Element will be inserted into specified
|
||||
// spatial_indexes, based on specified bbox.
|
||||
// REQUIRES: spatial_indexes.size() > 0
|
||||
virtual Status Insert(const WriteOptions& write_options,
|
||||
const BoundingBox<double>& bbox, const Slice& blob,
|
||||
const FeatureSet& feature_set,
|
||||
const std::vector<std::string>& spatial_indexes) = 0;
|
||||
|
||||
// Calling Compact() after inserting a bunch of elements should speed up
|
||||
// reading. This is especially useful if you use SpatialDBOptions::bulk_load
|
||||
// Num threads determines how many threads we'll use for compactions. Setting
|
||||
// this to bigger number will use more IO and CPU, but finish faster
|
||||
virtual Status Compact(int num_threads = 1) = 0;
|
||||
|
||||
// Query the specified spatial_index. Query will return all elements that
|
||||
// intersect bbox, but it may also return some extra elements.
|
||||
virtual Cursor* Query(const ReadOptions& read_options,
|
||||
const BoundingBox<double>& bbox,
|
||||
const std::string& spatial_index) = 0;
|
||||
};
|
||||
|
||||
} // namespace spatial
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -218,12 +218,13 @@ class StackableDB : public DB {
|
||||
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 {
|
||||
return db_->CompactFiles(
|
||||
compact_options, column_family, input_file_names,
|
||||
output_level, output_path_id, output_file_names);
|
||||
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 {
|
||||
return db_->CompactFiles(compact_options, column_family, input_file_names,
|
||||
output_level, output_path_id, output_file_names,
|
||||
compaction_job_info);
|
||||
}
|
||||
|
||||
virtual Status PauseBackgroundWork() override {
|
||||
|
||||
@@ -208,8 +208,10 @@ class Transaction {
|
||||
// Read this key and ensure that this transaction will only
|
||||
// be able to be committed if this key is not written outside this
|
||||
// transaction after it has first been read (or after the snapshot if a
|
||||
// snapshot is set in this transaction). The transaction behavior is the
|
||||
// same regardless of whether the key exists or not.
|
||||
// snapshot is set in this transaction and do_validate is true). If
|
||||
// do_validate is false, ReadOptions::snapshot is expected to be nullptr so
|
||||
// that GetForUpdate returns the latest committed value. The transaction
|
||||
// behavior is the same regardless of whether the key exists or not.
|
||||
//
|
||||
// Note: Currently, this function will return Status::MergeInProgress
|
||||
// if the most recent write to the queried key in this batch is a Merge.
|
||||
@@ -234,27 +236,31 @@ class Transaction {
|
||||
virtual Status GetForUpdate(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key, std::string* value,
|
||||
bool exclusive = true) = 0;
|
||||
bool exclusive = true,
|
||||
const bool do_validate = true) = 0;
|
||||
|
||||
// An overload of the above method that receives a PinnableSlice
|
||||
// For backward compatibility a default implementation is provided
|
||||
virtual Status GetForUpdate(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* pinnable_val,
|
||||
bool exclusive = true) {
|
||||
bool exclusive = true,
|
||||
const bool do_validate = true) {
|
||||
if (pinnable_val == nullptr) {
|
||||
std::string* null_str = nullptr;
|
||||
return GetForUpdate(options, column_family, key, null_str, exclusive);
|
||||
return GetForUpdate(options, column_family, key, null_str, exclusive,
|
||||
do_validate);
|
||||
} else {
|
||||
auto s = GetForUpdate(options, column_family, key,
|
||||
pinnable_val->GetSelf(), exclusive);
|
||||
pinnable_val->GetSelf(), exclusive, do_validate);
|
||||
pinnable_val->PinSelf();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
virtual Status GetForUpdate(const ReadOptions& options, const Slice& key,
|
||||
std::string* value, bool exclusive = true) = 0;
|
||||
std::string* value, bool exclusive = true,
|
||||
const bool do_validate = true) = 0;
|
||||
|
||||
virtual std::vector<Status> MultiGetForUpdate(
|
||||
const ReadOptions& options,
|
||||
@@ -287,6 +293,9 @@ class Transaction {
|
||||
// functions in WriteBatch, but will also do conflict checking on the
|
||||
// keys being written.
|
||||
//
|
||||
// assume_tracked=true expects the key be already tracked. If valid then it
|
||||
// skips ValidateSnapshot. Returns error otherwise.
|
||||
//
|
||||
// If this Transaction was created on an OptimisticTransactionDB, these
|
||||
// functions should always return Status::OK().
|
||||
//
|
||||
@@ -299,28 +308,33 @@ class Transaction {
|
||||
// (See max_write_buffer_number_to_maintain)
|
||||
// or other errors on unexpected failures.
|
||||
virtual Status Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) = 0;
|
||||
const Slice& value, const bool assume_tracked = false) = 0;
|
||||
virtual Status Put(const Slice& key, const Slice& value) = 0;
|
||||
virtual Status Put(ColumnFamilyHandle* column_family, const SliceParts& key,
|
||||
const SliceParts& value) = 0;
|
||||
const SliceParts& value,
|
||||
const bool assume_tracked = false) = 0;
|
||||
virtual Status Put(const SliceParts& key, const SliceParts& value) = 0;
|
||||
|
||||
virtual Status Merge(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) = 0;
|
||||
const Slice& value,
|
||||
const bool assume_tracked = false) = 0;
|
||||
virtual Status Merge(const Slice& key, const Slice& value) = 0;
|
||||
|
||||
virtual Status Delete(ColumnFamilyHandle* column_family,
|
||||
const Slice& key) = 0;
|
||||
virtual Status Delete(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const bool assume_tracked = false) = 0;
|
||||
virtual Status Delete(const Slice& key) = 0;
|
||||
virtual Status Delete(ColumnFamilyHandle* column_family,
|
||||
const SliceParts& key) = 0;
|
||||
const SliceParts& key,
|
||||
const bool assume_tracked = false) = 0;
|
||||
virtual Status Delete(const SliceParts& key) = 0;
|
||||
|
||||
virtual Status SingleDelete(ColumnFamilyHandle* column_family,
|
||||
const Slice& key) = 0;
|
||||
const Slice& key,
|
||||
const bool assume_tracked = false) = 0;
|
||||
virtual Status SingleDelete(const Slice& key) = 0;
|
||||
virtual Status SingleDelete(ColumnFamilyHandle* column_family,
|
||||
const SliceParts& key) = 0;
|
||||
const SliceParts& key,
|
||||
const bool assume_tracked = false) = 0;
|
||||
virtual Status SingleDelete(const SliceParts& key) = 0;
|
||||
|
||||
// PutUntracked() will write a Put to the batch of operations to be committed
|
||||
|
||||
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user