mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8608d75d85 | ||
|
|
75c83c5b61 | ||
|
|
939ffdc206 | ||
|
|
c56ad3c60a | ||
|
|
f9c6a87d18 | ||
|
|
8bd665331a | ||
|
|
9da3585891 | ||
|
|
d21b2a9699 | ||
|
|
43aee72181 | ||
|
|
8956288860 | ||
|
|
f2228962c5 | ||
|
|
eef93446a3 | ||
|
|
51ebe09f9a | ||
|
|
531a5f88a1 | ||
|
|
10196d7edc | ||
|
|
90e245697f | ||
|
|
c871142988 | ||
|
|
4c41e51c07 | ||
|
|
00803d619c | ||
|
|
83031e7343 | ||
|
|
296b47db25 | ||
|
|
b1f62be10e | ||
|
|
d89483098f | ||
|
|
c861fb390d | ||
|
|
fd00f39f97 | ||
|
|
bb75092574 | ||
|
|
8972dd1ffa | ||
|
|
139778dfb3 | ||
|
|
728e5f5750 | ||
|
|
48cd7a3aae | ||
|
|
0be89e87fd |
No files matched your search
@@ -326,12 +326,23 @@ CHECK_CXX_SOURCE_COMPILES("
|
||||
#endif
|
||||
int main() {
|
||||
static __thread int tls;
|
||||
(void)tls;
|
||||
}
|
||||
" HAVE_THREAD_LOCAL)
|
||||
if(HAVE_THREAD_LOCAL)
|
||||
add_definitions(-DROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
endif()
|
||||
|
||||
option(WITH_IOSTATS_CONTEXT "Enable IO stats context" ON)
|
||||
if (NOT WITH_IOSTATS_CONTEXT)
|
||||
add_definitions(-DNIOSTATS_CONTEXT)
|
||||
endif()
|
||||
|
||||
option(WITH_PERF_CONTEXT "Enable perf context" ON)
|
||||
if (NOT WITH_PERF_CONTEXT)
|
||||
add_definitions(-DNPERF_CONTEXT)
|
||||
endif()
|
||||
|
||||
option(FAIL_ON_WARNINGS "Treat compile warnings as errors" ON)
|
||||
if(FAIL_ON_WARNINGS)
|
||||
if(MSVC)
|
||||
|
||||
+25
-2
@@ -1,9 +1,24 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 6.20.3 (05/05/2021)
|
||||
### Bug Fixes
|
||||
* Fixed a bug where `GetLiveFiles()` output included a non-existent file called "OPTIONS-000000". Backups and checkpoints, which use `GetLiveFiles()`, failed on DBs impacted by this bug. Read-write DBs were impacted when the latest OPTIONS file failed to write and `fail_if_options_file_error == false`. Read-only DBs were impacted when no OPTIONS files existed.
|
||||
|
||||
## 6.20.2 (04/23/2021)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in handling file rename error in distributed/network file systems when the server succeeds but client returns error. The bug can cause CURRENT file to point to non-existing MANIFEST file, thus DB cannot be opened.
|
||||
* Fixed a bug where ingested files were written with incorrect boundary key metadata. In rare cases this could have led to a level's files being wrongly ordered and queries for the boundary keys returning wrong results.
|
||||
* Fixed a data race between insertion into memtables and the retrieval of the DB properties `rocksdb.cur-size-active-mem-table`, `rocksdb.cur-size-all-mem-tables`, and `rocksdb.size-all-mem-tables`.
|
||||
* Fixed the false-positive alert when recovering from the WAL file. Avoid reporting "SST file is ahead of WAL" on a newly created empty column family, if the previous WAL file is corrupted.
|
||||
|
||||
### Behavior Changes
|
||||
* Due to the fix of false-postive alert of "SST file is ahead of WAL", all the CFs with no SST file (CF empty) will bypass the consistency check. We fixed a false-positive, but introduced a very rare true-negative which will be triggered in the following conditions: A CF with some delete operations in the last a few queries which will result in an empty CF (those are flushed to SST file and a compaction triggered which combines this file and all other SST files and generates an empty CF, or there is another reason to write a manifest entry for this CF after a flush that generates no SST file from an empty CF). The deletion entries are logged in a WAL and this WAL was corrupted, while the CF's log number points to the next WAL (due to the flush). Therefore, the DB can only recover to the point without these trailing deletions and cause the inconsistent DB status.
|
||||
|
||||
## 6.20.0 (04/16/2021)
|
||||
### Behavior Changes
|
||||
* `ColumnFamilyOptions::sample_for_compression` now takes effect for creation of all block-based tables. Previously it only took effect for block-based tables created by flush.
|
||||
* `CompactFiles()` can no longer compact files from lower level to up level, which has the risk to corrupt DB (details: #8063). The validation is also added to all compactions.
|
||||
* Fixed some cases in which DB::OpenForReadOnly() could write to the filesystem. If you want a Logger with a read-only DB, you must now set DBOptions::info_log yourself, such as using CreateLoggerFromOptions().
|
||||
* get_iostats_context() will never return nullptr. If thread-local support is not available, and user does not opt-out iostats context, then compilation will fail. The same applies to perf context as well.
|
||||
|
||||
### Bug Fixes
|
||||
* Use thread-safe `strerror_r()` to get error messages.
|
||||
@@ -11,6 +26,9 @@
|
||||
* Made BackupEngine thread-safe and added documentation comments to clarify what is safe for multiple BackupEngine objects accessing the same backup directory.
|
||||
* Fixed crash (divide by zero) when compression dictionary is applied to a file containing only range tombstones.
|
||||
* Fixed a backward iteration bug with partitioned filter enabled: not including the prefix of the last key of the previous filter partition in current filter partition can cause wrong iteration result.
|
||||
* Fixed a bug that allowed `DBOptions::max_open_files` to be set with a non-negative integer with `ColumnFamilyOptions::compaction_style = kCompactionStyleFIFO`.
|
||||
* Fixed a bug in handling file rename error in distributed/network file systems when the server succeeds but client returns error. The bug can cause CURRENT file to point to non-existing MANIFEST file, thus DB cannot be opened.
|
||||
* Fixed a data race between insertion into memtables and the retrieval of the DB properties `rocksdb.cur-size-active-mem-table`, `rocksdb.cur-size-all-mem-tables`, and `rocksdb.size-all-mem-tables`.
|
||||
|
||||
### Performance Improvements
|
||||
* On ARM platform, use `yield` instead of `wfe` to relax cpu to gain better performance.
|
||||
@@ -18,10 +36,15 @@
|
||||
### Public API change
|
||||
* Added `TableProperties::slow_compression_estimated_data_size` and `TableProperties::fast_compression_estimated_data_size`. When `ColumnFamilyOptions::sample_for_compression > 0`, they estimate what `TableProperties::data_size` would have been if the "fast" or "slow" (see `ColumnFamilyOptions::sample_for_compression` API doc for definitions) compression had been used instead.
|
||||
* Update DB::StartIOTrace and remove Env object from the arguments as its redundant and DB already has Env object that is passed down to IOTracer::StartIOTrace
|
||||
* For new integrated BlobDB, add support for blob files for backup/restore like table files. Because of current limitations, blob files always use the kLegacyCrc32cAndFileSize naming scheme, and incremental backups must read and checksum all blob files in a DB, even for files that are already backed up.
|
||||
* Added `FlushReason::kWalFull`, which is reported when a memtable is flushed due to the WAL reaching its size limit; those flushes were previously reported as `FlushReason::kWriteBufferManager`. Also, changed the reason for flushes triggered by the write buffer manager to `FlushReason::kWriteBufferManager`; they were previously reported as `FlushReason::kWriteBufferFull`.
|
||||
* Extend file_checksum_dump ldb command and DB::GetLiveFilesChecksumInfo API for IntegratedBlobDB and get checksum of blob files along with SST files.
|
||||
|
||||
### New Features
|
||||
* Added the ability to open BackupEngine backups as read-only DBs, using BackupInfo::name_for_open and env_for_open provided by BackupEngine::GetBackupInfo() with include_file_details=true.
|
||||
* Added BackupEngine support for integrated BlobDB, with blob files shared between backups when table files are shared. Because of current limitations, blob files always use the kLegacyCrc32cAndFileSize naming scheme, and incremental backups must read and checksum all blob files in a DB, even for files that are already backed up.
|
||||
* Added an optional output parameter to BackupEngine::CreateNewBackup(WithMetadata) to return the BackupID of the new backup.
|
||||
* Added BackupEngine::GetBackupInfo / GetLatestBackupInfo for querying individual backups.
|
||||
* Made the Ribbon filter a long-term supported feature in terms of the SST schema(compatible with version >= 6.15.0) though the API for enabling it is expected to change.
|
||||
|
||||
## 6.19.0 (03/21/2021)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -55,50 +55,25 @@ DEBUG_LEVEL?=1
|
||||
# Set the default LIB_MODE to static
|
||||
LIB_MODE?=static
|
||||
|
||||
ifeq ($(MAKECMDGOALS),dbg)
|
||||
# OBJ_DIR is where the object files reside. Default to the current directory
|
||||
OBJ_DIR?=.
|
||||
|
||||
# Check the MAKECMDGOALS to set the DEBUG_LEVEL and LIB_MODE appropriately
|
||||
|
||||
ifneq ($(filter clean release install, $(MAKECMDGOALS)),)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
ifneq ($(filter dbg, $(MAKECMDGOALS)),)
|
||||
DEBUG_LEVEL=2
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),clean)
|
||||
else ifneq ($(filter shared_lib install-shared, $(MAKECMDGOALS)),)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),release)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),shared_lib)
|
||||
LIB_MODE=shared
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),install-shared)
|
||||
LIB_MODE=shared
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),static_lib)
|
||||
else ifneq ($(filter static_lib install-static, $(MAKECMDGOALS)),)
|
||||
DEBUG_LEVEL=0
|
||||
LIB_MODE=static
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),install-static)
|
||||
DEBUG_LEVEL=0
|
||||
LIB_MODE=static
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),install)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(findstring jtest, $(MAKECMDGOALS)),)
|
||||
else ifneq ($(filter jtest rocksdbjava%, $(MAKECMDGOALS)),)
|
||||
OBJ_DIR=jl
|
||||
LIB_MODE=shared
|
||||
endif
|
||||
|
||||
ifneq ($(findstring rocksdbjava, $(MAKECMDGOALS)),)
|
||||
LIB_MODE=shared
|
||||
ifneq ($(findstring rocksdbjavastatic, $(MAKECMDGOALS)),)
|
||||
OBJ_DIR=jls
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
@@ -107,8 +82,6 @@ ifneq ($(findstring rocksdbjava, $(MAKECMDGOALS)),)
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticpublish)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
else
|
||||
OBJ_DIR=jl
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -491,7 +464,6 @@ CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverl
|
||||
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
OBJ_DIR?=.
|
||||
LIB_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(LIB_SOURCES))
|
||||
LIB_OBJECTS += $(patsubst %.cc, $(OBJ_DIR)/%.o, $(ROCKSDB_PLUGIN_SOURCES))
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
@@ -651,8 +623,8 @@ else
|
||||
LIBRARY=$(STATIC_LIBRARY)
|
||||
TEST_LIBRARY=$(STATIC_TEST_LIBRARY)
|
||||
TOOLS_LIBRARY=$(STATIC_TOOLS_LIBRARY)
|
||||
STRESS_LIBRARY=$(STATIC_STRESS_LIBRARY)
|
||||
endif
|
||||
STRESS_LIBRARY=$(STATIC_STRESS_LIBRARY)
|
||||
|
||||
ROCKSDB_MAJOR = $(shell egrep "ROCKSDB_MAJOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_MINOR = $(shell egrep "ROCKSDB_MINOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
@@ -1246,7 +1218,7 @@ $(STATIC_TOOLS_LIBRARY): $(TOOL_OBJECTS)
|
||||
$(AM_V_AR)rm -f $@ $(SHARED_TOOLS_LIBRARY)
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
$(STATIC_STRESS_LIBRARY): $(ANALYZE_OBJECTS) $(STRESS_OBJECTS)
|
||||
$(STATIC_STRESS_LIBRARY): $(ANALYZE_OBJECTS) $(STRESS_OBJECTS) $(TESTUTIL)
|
||||
$(AM_V_AR)rm -f $@ $(SHARED_STRESS_LIBRARY)
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
@@ -1258,7 +1230,7 @@ $(SHARED_TOOLS_LIBRARY): $(TOOL_OBJECTS) $(SHARED1)
|
||||
$(AM_V_AR)rm -f $@ $(STATIC_TOOLS_LIBRARY)
|
||||
$(AM_SHARE)
|
||||
|
||||
$(SHARED_STRESS_LIBRARY): $(ANALYZE_OBJECTS) $(STRESS_OBJECTS) $(SHARED_TOOLS_LIBRARY) $(SHARED1)
|
||||
$(SHARED_STRESS_LIBRARY): $(ANALYZE_OBJECTS) $(STRESS_OBJECTS) $(TESTUTIL) $(SHARED_TOOLS_LIBRARY) $(SHARED1)
|
||||
$(AM_V_AR)rm -f $@ $(STATIC_STRESS_LIBRARY)
|
||||
$(AM_SHARE)
|
||||
|
||||
@@ -1292,7 +1264,7 @@ memtablerep_bench: $(OBJ_DIR)/memtable/memtablerep_bench.o $(LIBRARY)
|
||||
filter_bench: $(OBJ_DIR)/util/filter_bench.o $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_stress: $(OBJ_DIR)/db_stress_tool/db_stress.o $(STRESS_LIBRARY) $(TOOLS_LIBRARY) $(TESTUTIL) $(LIBRARY)
|
||||
db_stress: $(OBJ_DIR)/db_stress_tool/db_stress.o $(STRESS_LIBRARY) $(TOOLS_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
write_stress: $(OBJ_DIR)/tools/write_stress.o $(LIBRARY)
|
||||
|
||||
@@ -340,6 +340,7 @@ cpp_library(
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/crc32c_arm64.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/file_checksum_helper.cc",
|
||||
"util/hash.cc",
|
||||
@@ -649,6 +650,7 @@ cpp_library(
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/crc32c_arm64.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/file_checksum_helper.cc",
|
||||
"util/hash.cc",
|
||||
|
||||
@@ -306,7 +306,6 @@ Status BlobFileBuilder::CloseBlobFile() {
|
||||
" total blobs, %" PRIu64 " total bytes",
|
||||
column_family_name_.c_str(), job_id_, blob_file_number,
|
||||
blob_count_, blob_bytes_);
|
||||
|
||||
if (blob_callback_) {
|
||||
s = blob_callback_->OnBlobFileCompleted(blob_file_paths_->back());
|
||||
}
|
||||
|
||||
@@ -16,6 +16,14 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class BlobFileCompletionCallback {
|
||||
public:
|
||||
#ifdef ROCKSDB_LITE
|
||||
BlobFileCompletionCallback(SstFileManager* /*sst_file_manager*/,
|
||||
InstrumentedMutex* /*mutex*/,
|
||||
ErrorHandler* /*error_handler*/) {}
|
||||
Status OnBlobFileCompleted(const std::string& /*file_name*/) {
|
||||
return Status::OK();
|
||||
}
|
||||
#else
|
||||
BlobFileCompletionCallback(SstFileManager* sst_file_manager,
|
||||
InstrumentedMutex* mutex,
|
||||
ErrorHandler* error_handler)
|
||||
@@ -25,8 +33,6 @@ class BlobFileCompletionCallback {
|
||||
|
||||
Status OnBlobFileCompleted(const std::string& file_name) {
|
||||
Status s;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager_);
|
||||
if (sfm) {
|
||||
// Report new blob files to SstFileManagerImpl
|
||||
@@ -39,9 +45,6 @@ class BlobFileCompletionCallback {
|
||||
error_handler_->SetBGError(s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)file_name;
|
||||
#endif // ROCKSDB_LITE
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -49,5 +52,6 @@ class BlobFileCompletionCallback {
|
||||
SstFileManager* sst_file_manager_;
|
||||
InstrumentedMutex* mutex_;
|
||||
ErrorHandler* error_handler_;
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -2682,6 +2682,59 @@ unsigned char rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(
|
||||
return opt->rep.skip_checking_sst_file_sizes_on_db_open;
|
||||
}
|
||||
|
||||
/* Blob Options Settings */
|
||||
void rocksdb_options_set_enable_blob_files(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.enable_blob_files = val;
|
||||
}
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_blob_files;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_min_blob_size(rocksdb_options_t* opt, uint64_t val) {
|
||||
opt->rep.min_blob_size = val;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_min_blob_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.min_blob_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_blob_file_size(rocksdb_options_t* opt, uint64_t val) {
|
||||
opt->rep.blob_file_size = val;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_blob_file_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.blob_file_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_blob_compression_type(rocksdb_options_t* opt,
|
||||
int val) {
|
||||
opt->rep.blob_compression_type = static_cast<CompressionType>(val);
|
||||
}
|
||||
|
||||
int rocksdb_options_get_blob_compression_type(rocksdb_options_t* opt) {
|
||||
return opt->rep.blob_compression_type;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_blob_gc(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.enable_blob_garbage_collection = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_enable_blob_gc(rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_blob_garbage_collection;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_blob_gc_age_cutoff(rocksdb_options_t* opt,
|
||||
double val) {
|
||||
opt->rep.blob_garbage_collection_age_cutoff = val;
|
||||
}
|
||||
|
||||
double rocksdb_options_get_blob_gc_age_cutoff(rocksdb_options_t* opt) {
|
||||
return opt->rep.blob_garbage_collection_age_cutoff;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_num_levels(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.num_levels = n;
|
||||
}
|
||||
@@ -4112,6 +4165,10 @@ void rocksdb_cache_destroy(rocksdb_cache_t* cache) {
|
||||
delete cache;
|
||||
}
|
||||
|
||||
void rocksdb_cache_disown_data(rocksdb_cache_t* cache) {
|
||||
cache->rep->DisownData();
|
||||
}
|
||||
|
||||
void rocksdb_cache_set_capacity(rocksdb_cache_t* cache, size_t capacity) {
|
||||
cache->rep->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
+20
@@ -1759,6 +1759,25 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_atomic_flush(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_atomic_flush(o));
|
||||
|
||||
/* Blob Options */
|
||||
rocksdb_options_set_enable_blob_files(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_blob_files(o));
|
||||
|
||||
rocksdb_options_set_min_blob_size(o, 29);
|
||||
CheckCondition(29 == rocksdb_options_get_min_blob_size(o));
|
||||
|
||||
rocksdb_options_set_blob_file_size(o, 30);
|
||||
CheckCondition(30 == rocksdb_options_get_blob_file_size(o));
|
||||
|
||||
rocksdb_options_set_blob_compression_type(o, 4);
|
||||
CheckCondition(4 == rocksdb_options_get_blob_compression_type(o));
|
||||
|
||||
rocksdb_options_set_enable_blob_gc(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_blob_gc(o));
|
||||
|
||||
rocksdb_options_set_blob_gc_age_cutoff(o, 0.75);
|
||||
CheckCondition(0.75 == rocksdb_options_get_blob_gc_age_cutoff(o));
|
||||
|
||||
// Create a copy that should be equal to the original.
|
||||
rocksdb_options_t* copy;
|
||||
copy = rocksdb_options_create_copy(o);
|
||||
@@ -2869,6 +2888,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_readoptions_destroy(roptions);
|
||||
rocksdb_writeoptions_destroy(woptions);
|
||||
rocksdb_compactoptions_destroy(coptions);
|
||||
rocksdb_cache_disown_data(cache);
|
||||
rocksdb_cache_destroy(cache);
|
||||
rocksdb_comparator_destroy(cmp);
|
||||
rocksdb_dbpath_destroy(dbpath);
|
||||
|
||||
@@ -1355,6 +1355,12 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
"[0.0, 1.0].");
|
||||
}
|
||||
|
||||
if (cf_options.compaction_style == kCompactionStyleFIFO &&
|
||||
db_options.max_open_files != -1 && cf_options.ttl > 0) {
|
||||
return Status::NotSupported(
|
||||
"FIFO compaction only supported with max_open_files = -1.");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -810,6 +810,7 @@ class TestingContextCustomFilterPolicy
|
||||
TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
for (bool fifo : {true, false}) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_open_files = fifo ? -1 : options.max_open_files;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.compaction_style =
|
||||
fifo ? kCompactionStyleFIFO : kCompactionStyleLevel;
|
||||
@@ -820,6 +821,7 @@ TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
table_options.format_version = 5;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
TryReopen(options);
|
||||
CreateAndReopenWithCF({fifo ? "abe" : "bob"}, options);
|
||||
|
||||
const int maxKey = 10000;
|
||||
|
||||
@@ -98,7 +98,14 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
ret.emplace_back(CurrentFileName(""));
|
||||
ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
|
||||
// The OPTIONS file number is zero in read-write mode when OPTIONS file
|
||||
// writing failed and the DB was configured with
|
||||
// `fail_if_options_file_error == false`. In read-only mode the OPTIONS file
|
||||
// number is zero when no OPTIONS file exist at all. In those cases we do not
|
||||
// record any OPTIONS file in the live file list.
|
||||
if (versions_->options_file_number() != 0) {
|
||||
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
|
||||
}
|
||||
|
||||
// find length of manifest file while holding the mutex lock
|
||||
*manifest_file_size = versions_->manifest_file_size();
|
||||
|
||||
@@ -1605,7 +1605,7 @@ class DBImpl : public DB {
|
||||
Status SwitchWAL(WriteContext* write_context);
|
||||
|
||||
// REQUIRES: mutex locked and in write thread.
|
||||
Status HandleWriteBufferFull(WriteContext* write_context);
|
||||
Status HandleWriteBufferManagerFlush(WriteContext* write_context);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status PreprocessWrite(const WriteOptions& write_options, bool* need_log_sync,
|
||||
|
||||
@@ -2582,6 +2582,8 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:Start:1");
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:Start:2");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(bg_flush_scheduled_);
|
||||
|
||||
@@ -943,7 +943,7 @@ Status DBImpl::DeleteUnreferencedSstFiles() {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (largest_file_number > next_file_number) {
|
||||
if (largest_file_number >= next_file_number) {
|
||||
versions_->next_file_number_.store(largest_file_number + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -285,6 +285,9 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Creating manifest 1 \n");
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
{
|
||||
if (fs_->FileExists(manifest, IOOptions(), nullptr).ok()) {
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr).PermitUncheckedError();
|
||||
}
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
FileOptions file_options = fs_->OptimizeForManifestWrite(file_options_);
|
||||
s = NewWritableFile(fs_.get(), manifest, &file, file_options);
|
||||
@@ -314,7 +317,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
manifest.substr(manifest.find_last_of("/\\") + 1));
|
||||
}
|
||||
} else {
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr);
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr).PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -1134,11 +1137,29 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords)) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->GetLogNumber() > corrupted_wal_number) {
|
||||
// One special case cause cfd->GetLogNumber() > corrupted_wal_number but
|
||||
// the CF is still consistent: If a new column family is created during
|
||||
// the flush and the WAL sync fails at the same time, the new CF points to
|
||||
// the new WAL but the old WAL is curropted. Since the new CF is empty, it
|
||||
// is still consistent. We add the check of CF sst file size to avoid the
|
||||
// false positive alert.
|
||||
|
||||
// Note that, the check of (cfd->GetLiveSstFilesSize() > 0) may leads to
|
||||
// the ignorance of a very rare inconsistency case caused in data
|
||||
// canclation. One CF is empty due to KV deletion. But those operations
|
||||
// are in the WAL. If the WAL is corrupted, the status of this CF might
|
||||
// not be consistent with others. However, the consistency check will be
|
||||
// bypassed due to empty CF.
|
||||
// TODO: a better and complete implementation is needed to ensure strict
|
||||
// consistency check in WAL recovery including hanlding the tailing
|
||||
// issues.
|
||||
if (cfd->GetLogNumber() > corrupted_wal_number &&
|
||||
cfd->GetLiveSstFilesSize() > 0) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Column family inconsistency: SST file contains data"
|
||||
" beyond the point of corruption.");
|
||||
return Status::Corruption("SST file is ahead of WALs");
|
||||
return Status::Corruption("SST file is ahead of WALs in CF " +
|
||||
cfd->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1383,7 +1404,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
mutable_cf_options.compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery, &io_s,
|
||||
io_tracer_, &event_logger_, job_id, Env::IO_HIGH,
|
||||
nullptr /* table_properties */, -1 /* level */, current_time,
|
||||
nullptr /* table_properties */, 0 /* level */, current_time,
|
||||
0 /* oldest_key_time */, write_hint, 0 /* file_creation_time */,
|
||||
db_id_, db_session_id_, nullptr /*full_history_ts_low*/,
|
||||
&blob_callback_);
|
||||
|
||||
@@ -937,7 +937,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
// be flushed. We may end up with flushing much more DBs than needed. It's
|
||||
// suboptimal but still correct.
|
||||
WaitForPendingWrites();
|
||||
status = HandleWriteBufferFull(write_context);
|
||||
status = HandleWriteBufferManagerFlush(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !trim_history_scheduler_.Empty())) {
|
||||
@@ -1348,20 +1348,20 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWalFull);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWalFull);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr);
|
||||
Status status;
|
||||
@@ -1373,7 +1373,7 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
// suboptimal but still correct.
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Flushing column family with oldest memtable entry. Write buffer is "
|
||||
"Flushing column family with oldest memtable entry. Write buffers are "
|
||||
"using %" ROCKSDB_PRIszt " bytes out of a total of %" ROCKSDB_PRIszt ".",
|
||||
write_buffer_manager_->memory_usage(),
|
||||
write_buffer_manager_->buffer_size());
|
||||
@@ -1434,13 +1434,13 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -1626,6 +1626,7 @@ TEST_F(DBPropertiesTest, EstimateOldestKeyTime) {
|
||||
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.ttl = 300;
|
||||
options.max_open_files = -1;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
@@ -561,6 +561,7 @@ TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFilesWithGC) {
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
sfm->WaitForEmptyTrash();
|
||||
|
||||
ASSERT_EQ(Get(first_key), first_value);
|
||||
ASSERT_EQ(Get(second_key), second_value);
|
||||
@@ -593,6 +594,7 @@ TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFilesWithGC) {
|
||||
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
sfm->WaitForEmptyTrash();
|
||||
ASSERT_EQ(files_deleted, 5);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 5);
|
||||
|
||||
|
||||
+19
-13
@@ -908,6 +908,9 @@ TEST_F(DBTest, FlushSchedule) {
|
||||
static_cast<int64_t>(options.write_buffer_size);
|
||||
options.max_write_buffer_number = 2;
|
||||
options.write_buffer_size = 120 * 1024;
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason = FlushReason::kWriteBufferFull;
|
||||
options.listeners.push_back(flush_listener);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<port::Thread> threads;
|
||||
|
||||
@@ -3504,17 +3507,21 @@ TEST_F(DBTest, FIFOCompactionStyleWithCompactionAndDelete) {
|
||||
}
|
||||
|
||||
// Check that FIFO-with-TTL is not supported with max_open_files != -1.
|
||||
// Github issue #8014
|
||||
TEST_F(DBTest, FIFOCompactionWithTTLAndMaxOpenFilesTest) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.create_if_missing = true;
|
||||
options.ttl = 600; // seconds
|
||||
|
||||
// TTL is now supported with max_open_files != -1.
|
||||
options.max_open_files = 100;
|
||||
options = CurrentOptions(options);
|
||||
ASSERT_OK(TryReopen(options));
|
||||
// TTL is not supported with max_open_files != -1.
|
||||
options.max_open_files = 0;
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
options.max_open_files = 100;
|
||||
ASSERT_TRUE(TryReopen(options).IsNotSupported());
|
||||
|
||||
// TTL is supported with unlimited max_open_files
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
@@ -6694,20 +6701,19 @@ TEST_F(DBTest, MemoryUsageWithMaxWriteBufferSizeToMaintain) {
|
||||
Reopen(options);
|
||||
Random rnd(301);
|
||||
bool memory_limit_exceeded = false;
|
||||
uint64_t size_all_mem_table = 0;
|
||||
uint64_t cur_active_mem = 0;
|
||||
|
||||
ColumnFamilyData* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())->cfd();
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
std::string value = rnd.RandomString(1000);
|
||||
ASSERT_OK(Put("keykey_" + std::to_string(i), value));
|
||||
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
ASSERT_TRUE(db_->GetIntProperty(db_->DefaultColumnFamily(),
|
||||
DB::Properties::kSizeAllMemTables,
|
||||
&size_all_mem_table));
|
||||
ASSERT_TRUE(db_->GetIntProperty(db_->DefaultColumnFamily(),
|
||||
DB::Properties::kCurSizeActiveMemTable,
|
||||
&cur_active_mem));
|
||||
const uint64_t cur_active_mem = cfd->mem()->ApproximateMemoryUsage();
|
||||
const uint64_t size_all_mem_table =
|
||||
cur_active_mem + cfd->imm()->ApproximateMemoryUsage();
|
||||
|
||||
// Errors out if memory usage keeps on increasing beyond the limit.
|
||||
// Once memory limit exceeds, memory_limit_exceeded is set and if
|
||||
|
||||
+132
@@ -344,6 +344,10 @@ class DBTestSharedWriteBufferAcrossCFs
|
||||
TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
Options options = CurrentOptions();
|
||||
options.arena_block_size = 4096;
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
options.listeners.push_back(flush_listener);
|
||||
// Don't trip the listener at shutdown.
|
||||
options.avoid_flush_during_shutdown = true;
|
||||
|
||||
// Avoid undeterministic value by malloc_usable_size();
|
||||
// Force arena block size to 1
|
||||
@@ -387,6 +391,7 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
|
||||
// Create some data and flush "default" and "nikitich" so that they
|
||||
// are newer CFs created.
|
||||
flush_listener->expected_flush_reason = FlushReason::kManualFlush;
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1), wo));
|
||||
Flush(3);
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1), wo));
|
||||
@@ -397,6 +402,7 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(1));
|
||||
|
||||
flush_listener->expected_flush_reason = FlushReason::kWriteBufferManager;
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(30000), wo));
|
||||
if (cost_cache_) {
|
||||
ASSERT_GE(cache->GetUsage(), 256 * 1024);
|
||||
@@ -521,6 +527,10 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
std::string dbname2 = test::PerThreadDBPath("db_shared_wb_db2");
|
||||
Options options = CurrentOptions();
|
||||
options.arena_block_size = 4096;
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
options.listeners.push_back(flush_listener);
|
||||
// Don't trip the listener at shutdown.
|
||||
options.avoid_flush_during_shutdown = true;
|
||||
// Avoid undeterministic value by malloc_usable_size();
|
||||
// Force arena block size to 1
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -558,6 +568,7 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
};
|
||||
|
||||
// Trigger a flush on cf2
|
||||
flush_listener->expected_flush_reason = FlushReason::kWriteBufferManager;
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(70000), wo));
|
||||
wait_flush();
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(20000), wo));
|
||||
@@ -5428,6 +5439,98 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
class RenameCurrentTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
RenameCurrentTest()
|
||||
: DBTestBase("rename_current_test", /*env_do_fsync=*/true),
|
||||
sync_point_(GetParam()) {}
|
||||
|
||||
~RenameCurrentTest() override {}
|
||||
|
||||
void SetUp() override {
|
||||
env_->no_file_overwrite_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
env_->no_file_overwrite_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void SetupSyncPoints() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [&](void* arg) {
|
||||
Status* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected IO error.");
|
||||
});
|
||||
}
|
||||
|
||||
const std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DistributedFS, RenameCurrentTest,
|
||||
::testing::Values("SetCurrentFile:BeforeRename",
|
||||
"SetCurrentFile:AfterRename"));
|
||||
|
||||
TEST_P(RenameCurrentTest, Open) {
|
||||
Destroy(last_options_);
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
SetupSyncPoints();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
TEST_P(RenameCurrentTest, Flush) {
|
||||
Destroy(last_options_);
|
||||
Options options = GetDefaultOptions();
|
||||
options.max_manifest_file_size = 1;
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("key", "value"));
|
||||
SetupSyncPoints();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_NOK(Flush());
|
||||
|
||||
ASSERT_NOK(Put("foo", "value"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ("value", Get("key"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
}
|
||||
|
||||
TEST_P(RenameCurrentTest, Compaction) {
|
||||
Destroy(last_options_);
|
||||
Options options = GetDefaultOptions();
|
||||
options.max_manifest_file_size = 1;
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("a", "a_value"));
|
||||
ASSERT_OK(Put("c", "c_value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("b", "b_value"));
|
||||
ASSERT_OK(Put("d", "d_value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SetupSyncPoints();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
|
||||
ASSERT_NOK(Put("foo", "value"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ("d_value", Get("d"));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// WAL recovery mode is WALRecoveryMode::kPointInTimeRecovery.
|
||||
@@ -5455,6 +5558,35 @@ TEST_F(DBTest2, PointInTimeRecoveryWithIOErrorWhileReadingWal) {
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PointInTimeRecoveryWithSyncFailureInCFCreation) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCallFlush:Start:1",
|
||||
"PointInTimeRecoveryWithSyncFailureInCFCreation:1"},
|
||||
{"PointInTimeRecoveryWithSyncFailureInCFCreation:2",
|
||||
"DBImpl::BackgroundCallFlush:Start:2"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateColumnFamilies({"test1"}, Options());
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
|
||||
// Creating a CF when a flush is going on, log is synced but the
|
||||
// closed log file is not synced and corrupted.
|
||||
port::Thread flush_thread([&]() { ASSERT_NOK(Flush()); });
|
||||
TEST_SYNC_POINT("PointInTimeRecoveryWithSyncFailureInCFCreation:1");
|
||||
CreateColumnFamilies({"test2"}, Options());
|
||||
env_->corrupt_in_sync_ = true;
|
||||
TEST_SYNC_POINT("PointInTimeRecoveryWithSyncFailureInCFCreation:2");
|
||||
flush_thread.join();
|
||||
env_->corrupt_in_sync_ = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Reopening the DB should not corrupt anything
|
||||
Options options = CurrentOptions();
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
ReopenWithColumnFamilies({"default", "test1", "test2"}, options);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
@@ -44,6 +44,7 @@ SpecialEnv::SpecialEnv(Env* base, bool time_elapse_only_sleep)
|
||||
manifest_sync_error_.store(false, std::memory_order_release);
|
||||
manifest_write_error_.store(false, std::memory_order_release);
|
||||
log_write_error_.store(false, std::memory_order_release);
|
||||
no_file_overwrite_.store(false, std::memory_order_release);
|
||||
random_file_open_counter_.store(0, std::memory_order_relaxed);
|
||||
delete_count_.store(0, std::memory_order_relaxed);
|
||||
num_open_wal_file_.store(0);
|
||||
@@ -487,6 +488,7 @@ Options DBTestBase::GetOptions(
|
||||
}
|
||||
case kFIFOCompaction: {
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.max_open_files = -1;
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithPrefixHashIndex: {
|
||||
|
||||
@@ -393,6 +393,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
Status Sync() override {
|
||||
++env_->sync_counter_;
|
||||
if (env_->corrupt_in_sync_) {
|
||||
Append(std::string(33000, ' '));
|
||||
return Status::IOError("Ingested Sync Failure");
|
||||
}
|
||||
if (env_->skip_fsync_) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
@@ -440,6 +444,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
std::unique_ptr<WritableFile> base_;
|
||||
};
|
||||
|
||||
if (no_file_overwrite_.load(std::memory_order_acquire) &&
|
||||
target()->FileExists(f).ok()) {
|
||||
return Status::NotSupported("SpecialEnv::no_file_overwrite_ is true.");
|
||||
}
|
||||
|
||||
if (non_writeable_rate_.load(std::memory_order_acquire) > 0) {
|
||||
uint32_t random_number;
|
||||
{
|
||||
@@ -687,6 +696,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
// If true, returns Status::NotSupported for file overwrite.
|
||||
std::atomic<bool> no_file_overwrite_;
|
||||
|
||||
// Number of WAL files that are still open for write.
|
||||
std::atomic<int> num_open_wal_file_;
|
||||
|
||||
@@ -709,6 +721,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// If true, all fsync to files and directories are skipped.
|
||||
bool skip_fsync_ = false;
|
||||
|
||||
// If true, ingest the corruption to file during sync.
|
||||
bool corrupt_in_sync_ = false;
|
||||
|
||||
std::atomic<uint32_t> non_writeable_rate_;
|
||||
|
||||
std::atomic<uint32_t> new_writable_count_;
|
||||
@@ -761,6 +776,17 @@ class OnFileDeletionListener : public EventListener {
|
||||
size_t matched_count_;
|
||||
std::string expected_file_name_;
|
||||
};
|
||||
|
||||
class FlushCounterListener : public EventListener {
|
||||
public:
|
||||
std::atomic<int> count{0};
|
||||
std::atomic<FlushReason> expected_flush_reason{FlushReason::kOthers};
|
||||
|
||||
void OnFlushBegin(DB* /*db*/, const FlushJobInfo& flush_job_info) override {
|
||||
count++;
|
||||
ASSERT_EQ(expected_flush_reason.load(), flush_job_info.flush_reason);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// A test merge operator mimics put but also fails if one of merge operands is
|
||||
|
||||
+2
-13
@@ -1801,19 +1801,8 @@ TEST_P(DBWALTestWithParamsVaryingRecoveryMode,
|
||||
// avoid_flush_during_recovery=true.
|
||||
// Flush should trigger if max_total_wal_size is reached.
|
||||
TEST_F(DBWALTest, RestoreTotalLogSizeAfterRecoverWithoutFlush) {
|
||||
class TestFlushListener : public EventListener {
|
||||
public:
|
||||
std::atomic<int> count{0};
|
||||
|
||||
TestFlushListener() = default;
|
||||
|
||||
void OnFlushBegin(DB* /*db*/, const FlushJobInfo& flush_job_info) override {
|
||||
count++;
|
||||
ASSERT_EQ(FlushReason::kWriteBufferManager, flush_job_info.flush_reason);
|
||||
}
|
||||
};
|
||||
std::shared_ptr<TestFlushListener> test_listener =
|
||||
std::make_shared<TestFlushListener>();
|
||||
auto test_listener = std::make_shared<FlushCounterListener>();
|
||||
test_listener->expected_flush_reason = FlushReason::kWalFull;
|
||||
|
||||
constexpr size_t kKB = 1024;
|
||||
constexpr size_t kMB = 1024 * 1024;
|
||||
|
||||
@@ -1542,6 +1542,44 @@ TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileAfterDBPut) {
|
||||
// Repro https://github.com/facebook/rocksdb/issues/6245.
|
||||
// Flush three files to L0. Ingest one more file to trigger L0->L1 compaction
|
||||
// via trivial move. The bug happened when L1 files were incorrectly sorted
|
||||
// resulting in an old value for "k" returned by `Get()`.
|
||||
Options options = CurrentOptions();
|
||||
|
||||
ASSERT_OK(Put("k", "a"));
|
||||
Flush();
|
||||
ASSERT_OK(Put("k", "a"));
|
||||
Flush();
|
||||
ASSERT_OK(Put("k", "a"));
|
||||
Flush();
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a
|
||||
// file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
ASSERT_OK(sst_file_writer.Put("k", "b"));
|
||||
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
IngestExternalFileOptions ifo;
|
||||
s = db_->IngestExternalFile({file1}, ifo);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(Get("k"), "b");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
||||
testing::Values(std::make_tuple(true, true),
|
||||
std::make_tuple(true, false),
|
||||
|
||||
@@ -367,9 +367,32 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
super_version, force_global_seqno, cfd_->ioptions()->compaction_style,
|
||||
last_seqno, &f, &assigned_seqno);
|
||||
}
|
||||
|
||||
// Modify the smallest/largest internal key to include the sequence number
|
||||
// that we just learned. Only overwrite sequence number zero. There could
|
||||
// be a nonzero sequence number already to indicate a range tombstone's
|
||||
// exclusive endpoint.
|
||||
ParsedInternalKey smallest_parsed, largest_parsed;
|
||||
if (status.ok()) {
|
||||
status = ParseInternalKey(*f.smallest_internal_key.rep(),
|
||||
&smallest_parsed, false /* log_err_key */);
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = ParseInternalKey(*f.largest_internal_key.rep(), &largest_parsed,
|
||||
false /* log_err_key */);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (smallest_parsed.sequence == 0) {
|
||||
UpdateInternalKey(f.smallest_internal_key.rep(), assigned_seqno,
|
||||
smallest_parsed.type);
|
||||
}
|
||||
if (largest_parsed.sequence == 0) {
|
||||
UpdateInternalKey(f.largest_internal_key.rep(), assigned_seqno,
|
||||
largest_parsed.type);
|
||||
}
|
||||
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, assigned_seqno);
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
|
||||
&assigned_seqno);
|
||||
|
||||
@@ -75,6 +75,8 @@ const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
return "Manual Flush";
|
||||
case FlushReason::kErrorRecovery:
|
||||
return "Error Recovery";
|
||||
case FlushReason::kWalFull:
|
||||
return "WAL Full";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
@@ -751,21 +751,24 @@ bool InternalStats::HandleBackgroundErrors(uint64_t* value, DBImpl* /*db*/,
|
||||
bool InternalStats::HandleCurSizeActiveMemTable(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current size of the active memtable
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage();
|
||||
// Using ApproximateMemoryUsageFast to avoid the need for synchronization
|
||||
*value = cfd_->mem()->ApproximateMemoryUsageFast();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCurSizeAllMemTables(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current size of the active memtable + immutable memtables
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage() +
|
||||
// Using ApproximateMemoryUsageFast to avoid the need for synchronization
|
||||
*value = cfd_->mem()->ApproximateMemoryUsageFast() +
|
||||
cfd_->imm()->ApproximateUnflushedMemTablesMemoryUsage();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleSizeAllMemTables(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage() +
|
||||
// Using ApproximateMemoryUsageFast to avoid the need for synchronization
|
||||
*value = cfd_->mem()->ApproximateMemoryUsageFast() +
|
||||
cfd_->imm()->ApproximateMemoryUsage();
|
||||
return true;
|
||||
}
|
||||
|
||||
+64
-7
@@ -4083,6 +4083,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
uint64_t new_manifest_file_size = 0;
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
IOStatus manifest_io_status;
|
||||
{
|
||||
FileOptions opt_file_opts = fs_->OptimizeForManifestWrite(file_options_);
|
||||
mu->Unlock();
|
||||
@@ -4134,6 +4135,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
s = WriteCurrentStateToManifest(curr_state, wal_additions,
|
||||
descriptor_log_.get(), io_s);
|
||||
} else {
|
||||
manifest_io_status = io_s;
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
@@ -4171,11 +4173,13 @@ Status VersionSet::ProcessManifestWrites(
|
||||
io_s = descriptor_log_->AddRecord(record);
|
||||
if (!io_s.ok()) {
|
||||
s = io_s;
|
||||
manifest_io_status = io_s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
io_s = SyncManifest(db_options_, descriptor_log_->file());
|
||||
manifest_io_status = io_s;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", &io_s);
|
||||
}
|
||||
@@ -4188,6 +4192,9 @@ Status VersionSet::ProcessManifestWrites(
|
||||
|
||||
// If we just created a new descriptor file, install it by writing a
|
||||
// new CURRENT file that points to it.
|
||||
if (s.ok()) {
|
||||
assert(manifest_io_status.ok());
|
||||
}
|
||||
if (s.ok() && new_descriptor_log) {
|
||||
io_s = SetCurrentFile(fs_.get(), dbname_, pending_manifest_file_number_,
|
||||
db_directory);
|
||||
@@ -4303,11 +4310,41 @@ Status VersionSet::ProcessManifestWrites(
|
||||
for (auto v : versions) {
|
||||
delete v;
|
||||
}
|
||||
if (manifest_io_status.ok()) {
|
||||
manifest_file_number_ = pending_manifest_file_number_;
|
||||
manifest_file_size_ = new_manifest_file_size;
|
||||
}
|
||||
// If manifest append failed for whatever reason, the file could be
|
||||
// corrupted. So we need to force the next version update to start a
|
||||
// new manifest file.
|
||||
descriptor_log_.reset();
|
||||
if (new_descriptor_log) {
|
||||
// If manifest operations failed, then we know the CURRENT file still
|
||||
// points to the original MANIFEST. Therefore, we can safely delete the
|
||||
// new MANIFEST.
|
||||
// If manifest operations succeeded, and we are here, then it is possible
|
||||
// that renaming tmp file to CURRENT failed.
|
||||
//
|
||||
// On local POSIX-compliant FS, the CURRENT must point to the original
|
||||
// MANIFEST. We can delete the new MANIFEST for simplicity, but we can also
|
||||
// keep it. Future recovery will ignore this MANIFEST. It's also ok for the
|
||||
// process not to crash and continue using the db. Any future LogAndApply()
|
||||
// call will switch to a new MANIFEST and update CURRENT, still ignoring
|
||||
// this one.
|
||||
//
|
||||
// On non-local FS, it is
|
||||
// possible that the rename operation succeeded on the server (remote)
|
||||
// side, but the client somehow returns a non-ok status to RocksDB. Note
|
||||
// that this does not violate atomicity. Should we delete the new MANIFEST
|
||||
// successfully, a subsequent recovery attempt will likely see the CURRENT
|
||||
// pointing to the new MANIFEST, thus fail. We will not be able to open the
|
||||
// DB again. Therefore, if manifest operations succeed, we should keep the
|
||||
// the new MANIFEST. If the process proceeds, any future LogAndApply() call
|
||||
// will switch to a new MANIFEST and update CURRENT. If user tries to
|
||||
// re-open the DB,
|
||||
// a) CURRENT points to the new MANIFEST, and the new MANIFEST is present.
|
||||
// b) CURRENT points to the original MANIFEST, and the original MANIFEST
|
||||
// also exists.
|
||||
if (new_descriptor_log && !manifest_io_status.ok()) {
|
||||
ROCKS_LOG_INFO(db_options_->info_log,
|
||||
"Deleting manifest %" PRIu64 " current manifest %" PRIu64
|
||||
"\n",
|
||||
@@ -4937,7 +4974,7 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
|
||||
}
|
||||
|
||||
// Get the checksum information including the checksum and checksum function
|
||||
// name of all SST files in VersionSet. Store the information in
|
||||
// name of all SST and blob files in VersionSet. Store the information in
|
||||
// FileChecksumList which contains a map from file number to its checksum info.
|
||||
// If DB is not running, make sure call VersionSet::Recover() to load the file
|
||||
// metadata from Manifest to VersionSet before calling this function.
|
||||
@@ -4954,6 +4991,7 @@ Status VersionSet::GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) {
|
||||
if (cfd->IsDropped() || !cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
/* SST files */
|
||||
for (int level = 0; level < cfd->NumberLevels(); level++) {
|
||||
for (const auto& file :
|
||||
cfd->current()->storage_info()->LevelFiles(level)) {
|
||||
@@ -4961,17 +4999,36 @@ Status VersionSet::GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) {
|
||||
file->file_checksum,
|
||||
file->file_checksum_func_name);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Blob files */
|
||||
const auto& blob_files = cfd->current()->storage_info()->GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const uint64_t blob_file_number = pair.first;
|
||||
const auto& meta = pair.second;
|
||||
|
||||
assert(meta);
|
||||
assert(blob_file_number == meta->GetBlobFileNumber());
|
||||
|
||||
std::string checksum_value = meta->GetChecksumValue();
|
||||
std::string checksum_method = meta->GetChecksumMethod();
|
||||
assert(checksum_value.empty() == checksum_method.empty());
|
||||
if (meta->GetChecksumMethod().empty()) {
|
||||
checksum_value = kUnknownFileChecksum;
|
||||
checksum_method = kUnknownFileChecksumFuncName;
|
||||
}
|
||||
|
||||
s = checksum_list->InsertOneFileChecksum(blob_file_number, checksum_value,
|
||||
checksum_method);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,7 @@ DECLARE_bool(best_efforts_recovery);
|
||||
DECLARE_bool(skip_verifydb);
|
||||
DECLARE_bool(enable_compaction_filter);
|
||||
DECLARE_bool(paranoid_file_checks);
|
||||
DECLARE_bool(fail_if_options_file_error);
|
||||
DECLARE_uint64(batch_protection_bytes_per_key);
|
||||
|
||||
DECLARE_uint64(user_timestamp_size);
|
||||
|
||||
@@ -28,7 +28,9 @@ class DbStressEnvWrapper : public EnvWrapper {
|
||||
f.find(".restore") != std::string::npos) {
|
||||
return target()->DeleteFile(f);
|
||||
}
|
||||
return Status::OK();
|
||||
// Rename the file instead of deletion to keep the history, and
|
||||
// at the same time it is not visible to RocksDB.
|
||||
return target()->RenameFile(f, f + "_renamed_");
|
||||
}
|
||||
|
||||
// If true, all manifest files will not be delted in DeleteFile().
|
||||
|
||||
@@ -792,6 +792,10 @@ DEFINE_bool(paranoid_file_checks, true,
|
||||
"After writing every SST file, reopen it and read all the keys "
|
||||
"and validate checksums");
|
||||
|
||||
DEFINE_bool(fail_if_options_file_error, false,
|
||||
"Fail operations that fail to detect or properly persist options "
|
||||
"file.");
|
||||
|
||||
DEFINE_uint64(batch_protection_bytes_per_key, 0,
|
||||
"If nonzero, enables integrity protection in `WriteBatch` at the "
|
||||
"specified number of bytes per key. Currently the only supported "
|
||||
@@ -808,4 +812,8 @@ DEFINE_uint64(user_timestamp_size, 0,
|
||||
"Number of bytes for a user-defined timestamp. Currently, only "
|
||||
"8-byte is supported");
|
||||
|
||||
DEFINE_int32(open_metadata_write_fault_one_in, 0,
|
||||
"On non-zero, enables fault injection on file metadata write "
|
||||
"during DB reopen.");
|
||||
|
||||
#endif // GFLAGS
|
||||
@@ -30,6 +30,7 @@ DECLARE_int32(compaction_thread_pool_adjust_interval);
|
||||
DECLARE_int32(continuous_verification_interval);
|
||||
DECLARE_int32(read_fault_one_in);
|
||||
DECLARE_int32(write_fault_one_in);
|
||||
DECLARE_int32(open_metadata_write_fault_one_in);
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class StressTest;
|
||||
|
||||
@@ -28,9 +28,9 @@ StressTest::StressTest()
|
||||
FLAGS_bloom_bits >= 0
|
||||
? FLAGS_use_ribbon_filter
|
||||
? NewExperimentalRibbonFilterPolicy(FLAGS_bloom_bits)
|
||||
: FLAGS_use_block_based_filter
|
||||
? NewBloomFilterPolicy(FLAGS_bloom_bits, true)
|
||||
: NewBloomFilterPolicy(FLAGS_bloom_bits, false)
|
||||
: FLAGS_use_block_based_filter
|
||||
? NewBloomFilterPolicy(FLAGS_bloom_bits, true)
|
||||
: NewBloomFilterPolicy(FLAGS_bloom_bits, false)
|
||||
: nullptr),
|
||||
db_(nullptr),
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -2104,9 +2104,14 @@ void StressTest::PrintEnv() const {
|
||||
static_cast<int>(FLAGS_level_compaction_dynamic_level_bytes));
|
||||
fprintf(stdout, "Read fault one in : %d\n", FLAGS_read_fault_one_in);
|
||||
fprintf(stdout, "Write fault one in : %d\n", FLAGS_write_fault_one_in);
|
||||
fprintf(stdout, "Open metadata write fault one in:\n");
|
||||
fprintf(stdout, " %d\n",
|
||||
FLAGS_open_metadata_write_fault_one_in);
|
||||
fprintf(stdout, "Sync fault injection : %d\n", FLAGS_sync_fault_injection);
|
||||
fprintf(stdout, "Best efforts recovery : %d\n",
|
||||
static_cast<int>(FLAGS_best_efforts_recovery));
|
||||
fprintf(stdout, "Fail if OPTIONS file error: %d\n",
|
||||
static_cast<int>(FLAGS_fail_if_options_file_error));
|
||||
fprintf(stdout, "User timestamp size bytes : %d\n",
|
||||
static_cast<int>(FLAGS_user_timestamp_size));
|
||||
|
||||
@@ -2325,14 +2330,14 @@ void StressTest::Open() {
|
||||
|
||||
options_.best_efforts_recovery = FLAGS_best_efforts_recovery;
|
||||
options_.paranoid_file_checks = FLAGS_paranoid_file_checks;
|
||||
options_.fail_if_options_file_error = FLAGS_fail_if_options_file_error;
|
||||
|
||||
if ((options_.enable_blob_files || options_.enable_blob_garbage_collection ||
|
||||
FLAGS_allow_setting_blob_options_dynamically) &&
|
||||
(FLAGS_use_merge || FLAGS_backup_one_in > 0 ||
|
||||
FLAGS_best_efforts_recovery)) {
|
||||
(FLAGS_use_merge || FLAGS_best_efforts_recovery)) {
|
||||
fprintf(stderr,
|
||||
"Integrated BlobDB is currently incompatible with Merge, "
|
||||
"backup/restore, and best-effort recovery\n");
|
||||
"and best-effort recovery\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -2410,33 +2415,78 @@ void StressTest::Open() {
|
||||
new DbStressListener(FLAGS_db, options_.db_paths, cf_descriptors));
|
||||
options_.create_missing_column_families = true;
|
||||
if (!FLAGS_use_txn) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// StackableDB-based BlobDB
|
||||
if (FLAGS_use_blob_db) {
|
||||
blob_db::BlobDBOptions blob_db_options;
|
||||
blob_db_options.min_blob_size = FLAGS_blob_db_min_blob_size;
|
||||
blob_db_options.bytes_per_sync = FLAGS_blob_db_bytes_per_sync;
|
||||
blob_db_options.blob_file_size = FLAGS_blob_db_file_size;
|
||||
blob_db_options.enable_garbage_collection = FLAGS_blob_db_enable_gc;
|
||||
blob_db_options.garbage_collection_cutoff = FLAGS_blob_db_gc_cutoff;
|
||||
|
||||
blob_db::BlobDB* blob_db = nullptr;
|
||||
s = blob_db::BlobDB::Open(options_, blob_db_options, FLAGS_db,
|
||||
cf_descriptors, &column_families_, &blob_db);
|
||||
if (s.ok()) {
|
||||
db_ = blob_db;
|
||||
}
|
||||
} else
|
||||
#endif // !ROCKSDB_LITE
|
||||
{
|
||||
if (db_preload_finished_.load() && FLAGS_read_only) {
|
||||
s = DB::OpenForReadOnly(DBOptions(options_), FLAGS_db, cf_descriptors,
|
||||
&column_families_, &db_);
|
||||
} else {
|
||||
s = DB::Open(DBOptions(options_), FLAGS_db, cf_descriptors,
|
||||
&column_families_, &db_);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// Determine whether we need to ingest file metadata write failures
|
||||
// during DB reopen. If it does, enable it.
|
||||
// Only ingest metadata error if it is reopening, as initial open
|
||||
// failure doesn't need to be handled.
|
||||
// TODO cover transaction DB is not covered in this fault test too.
|
||||
bool ingest_meta_error =
|
||||
FLAGS_open_metadata_write_fault_one_in &&
|
||||
fault_fs_guard
|
||||
->FileExists(FLAGS_db + "/CURRENT", IOOptions(), nullptr)
|
||||
.ok();
|
||||
if (ingest_meta_error) {
|
||||
fault_fs_guard->EnableMetadataWriteErrorInjection();
|
||||
fault_fs_guard->SetRandomMetadataWriteError(
|
||||
FLAGS_open_metadata_write_fault_one_in);
|
||||
}
|
||||
while (true) {
|
||||
#endif // NDEBUG
|
||||
#ifndef ROCKSDB_LITE
|
||||
// StackableDB-based BlobDB
|
||||
if (FLAGS_use_blob_db) {
|
||||
blob_db::BlobDBOptions blob_db_options;
|
||||
blob_db_options.min_blob_size = FLAGS_blob_db_min_blob_size;
|
||||
blob_db_options.bytes_per_sync = FLAGS_blob_db_bytes_per_sync;
|
||||
blob_db_options.blob_file_size = FLAGS_blob_db_file_size;
|
||||
blob_db_options.enable_garbage_collection = FLAGS_blob_db_enable_gc;
|
||||
blob_db_options.garbage_collection_cutoff = FLAGS_blob_db_gc_cutoff;
|
||||
|
||||
blob_db::BlobDB* blob_db = nullptr;
|
||||
s = blob_db::BlobDB::Open(options_, blob_db_options, FLAGS_db,
|
||||
cf_descriptors, &column_families_,
|
||||
&blob_db);
|
||||
if (s.ok()) {
|
||||
db_ = blob_db;
|
||||
}
|
||||
} else
|
||||
#endif // !ROCKSDB_LITE
|
||||
{
|
||||
if (db_preload_finished_.load() && FLAGS_read_only) {
|
||||
s = DB::OpenForReadOnly(DBOptions(options_), FLAGS_db,
|
||||
cf_descriptors, &column_families_, &db_);
|
||||
} else {
|
||||
s = DB::Open(DBOptions(options_), FLAGS_db, cf_descriptors,
|
||||
&column_families_, &db_);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (ingest_meta_error) {
|
||||
fault_fs_guard->DisableMetadataWriteErrorInjection();
|
||||
if (!s.ok()) {
|
||||
// After failure to opening a DB due to IO error, retry should
|
||||
// successfully open the DB with correct data if no IO error shows
|
||||
// up.
|
||||
ingest_meta_error = false;
|
||||
|
||||
Random rand(static_cast<uint32_t>(FLAGS_seed));
|
||||
if (rand.OneIn(2)) {
|
||||
fault_fs_guard->DeleteFilesCreatedAfterLastDirSync(IOOptions(),
|
||||
nullptr);
|
||||
}
|
||||
if (rand.OneIn(3)) {
|
||||
fault_fs_guard->DropUnsyncedFileData();
|
||||
} else if (rand.OneIn(2)) {
|
||||
fault_fs_guard->DropRandomUnsyncedFileData(&rand);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
TransactionDBOptions txn_db_options;
|
||||
|
||||
@@ -98,7 +98,7 @@ int db_stress_tool(int argc, char** argv) {
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (FLAGS_read_fault_one_in || FLAGS_sync_fault_injection ||
|
||||
FLAGS_write_fault_one_in) {
|
||||
FLAGS_write_fault_one_in || FLAGS_open_metadata_write_fault_one_in) {
|
||||
FaultInjectionTestFS* fs =
|
||||
new FaultInjectionTestFS(raw_env->GetFileSystem());
|
||||
fault_fs_guard.reset(fs);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: (Call For Contribution) Make Universal Compaction More Incremental
|
||||
layout: post
|
||||
author: sdong
|
||||
category: blog
|
||||
---
|
||||
|
||||
### Motivation
|
||||
|
||||
Universal Compaction is an important compaction style, but few changes were made after we made the structure multi-leveled. Yet the major restriction of always compacting full sorted run is not relaxed. Compared to Leveled Compaction, where we usually only compile several SST files together, in universal compaction, we frequently compact GBs of data. Two issues with this gap: 1. it makes it harder to unify universal and leveled compaction; 2. periodically data is fully compacted, and in the mean time space is doubled. To ease the problem, we can break the restriction and do similar as leveled compaction, and bring it closer to unified compaction.
|
||||
|
||||
We call for help for making following improvements.
|
||||
|
||||
|
||||
### How Universal Compaction Works
|
||||
|
||||
In universal, whole levels are compacted together to satisfy two conditions (See [wiki page](https://github.com/facebook/rocksdb/wiki/Universal-Compaction) for more details):
|
||||
|
||||
1. total size / bottommost level size > a threshold, or
|
||||
2. total number of sorted runs (non-0 levels + L0 files) is within a threshold
|
||||
|
||||
1 is to limit extra space overhead used for dead data and 2 is for read performance.
|
||||
|
||||
If 1 is triggered, likely a full compaction will be triggered. If 2 is triggered, RocksDB compact some sorted runs to bring the number down. It does it by using a simple heuristic so that less writes needed for that purpose over time: it starts from compacting smaller files, but if total size to compact is similar to or larger than size of the next level, it will take that level together, as soon on (whether it is the best heuristic is another question and we’ve never seriously looked at it).
|
||||
|
||||
### How We Can Improve?
|
||||
|
||||
Let’s start from condition 1. Here we do full compaction but is not necessary. A simple optimization would be to compact so that just enough files are merged into the bottommost level (Lmax) to satisfy condition 1. It would work if we only need to pick some files from Lmax-1, or if it is cheaper over time, we can pick some files from other levels too.
|
||||
|
||||
Then condition 2. If we finish condition 1, there might be holes in some ranges in older levels. These holes might make it possible that only by compacting some sub ranges, we can fix the LSM-tree for condition 2. RocksDB can take single files into consideration and apply more sophisticated heuristic.
|
||||
|
||||
This new approach makes universal compaction closer to leveled compaction. The operation for 1 is closer to how Leveled compaction triggeres Lmax-1 to Lmax compaction. And 2 can potentially be implemented as something similar to level picking in Leveled Compaction. In fact, all those file picking can co-existing in one single compaction style and there isn’t fundamental conflicts to that.
|
||||
|
||||
### Limitation
|
||||
|
||||
There are two limitations:
|
||||
|
||||
* Periodic automatic full compaction is unpleasant but at the same time is pleasant in another way. Some users might uses it to reason that everything is periodically collapsed so dead data is gone and old data is rewritten. We need to make sure periodic compaction works to continue with that.
|
||||
* L0 to the first non-L0 level compaction is the first time data is partitioned in LSM-tree so that incremental compaction by range is possible. We might need to do more of these compactions in order to make incremental possible, which will increase compaction slightly.
|
||||
* Compacting subset of a level would introduce some extra overhead for unaligned files, just as in leveled compaction. More SST boundary cutting heuristic can reduce this overhead but it will be there.
|
||||
|
||||
But I believe the benefits would outweight the limitations. Reducing temporary space doubling and moving towards to unified compaction would be important achievements.
|
||||
|
||||
### Interested in Help?
|
||||
|
||||
Compaction is the core of LSM-tree, but its improvements are far overdue. If you are a user of universal compaction and would be able to benefit from those improvements, we will be happy to work with you on speeding up the project and bring them to RocksDB sooner. Feel free to communicate with us in [this issue](https://github.com/facebook/rocksdb/issues/8181).
|
||||
@@ -383,10 +383,12 @@ IOStatus SetCurrentFile(FileSystem* fs, const std::string& dbname,
|
||||
contents.remove_prefix(dbname.size() + 1);
|
||||
std::string tmp = TempFileName(dbname, descriptor_number);
|
||||
IOStatus s = WriteStringToFile(fs, contents.ToString() + "\n", tmp, true);
|
||||
TEST_SYNC_POINT_CALLBACK("SetCurrentFile:BeforeRename", &s);
|
||||
if (s.ok()) {
|
||||
TEST_KILL_RANDOM("SetCurrentFile:0", rocksdb_kill_odds * REDUCE_ODDS2);
|
||||
s = fs->RenameFile(tmp, CurrentFileName(dbname), IOOptions(), nullptr);
|
||||
TEST_KILL_RANDOM("SetCurrentFile:1", rocksdb_kill_odds * REDUCE_ODDS2);
|
||||
TEST_SYNC_POINT_CALLBACK("SetCurrentFile:AfterRename", &s);
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (directory_to_fsync != nullptr) {
|
||||
|
||||
@@ -1068,6 +1068,37 @@ extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
/* Blob Options Settings */
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_enable_blob_files(
|
||||
rocksdb_options_t* opt, unsigned char val);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_min_blob_size(
|
||||
rocksdb_options_t* opt, uint64_t val);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_min_blob_size(rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_blob_file_size(
|
||||
rocksdb_options_t* opt, uint64_t val);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_blob_file_size(rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_blob_compression_type(
|
||||
rocksdb_options_t* opt, int val);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_blob_compression_type(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_enable_blob_gc(
|
||||
rocksdb_options_t* opt, unsigned char val);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_gc(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_blob_gc_age_cutoff(
|
||||
rocksdb_options_t* opt, double val);
|
||||
extern ROCKSDB_LIBRARY_API double rocksdb_options_get_blob_gc_age_cutoff(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
/* returns a pointer to a malloc()-ed, null terminated string */
|
||||
extern ROCKSDB_LIBRARY_API char* rocksdb_options_statistics_get_string(
|
||||
rocksdb_options_t* opt);
|
||||
@@ -1712,6 +1743,8 @@ extern ROCKSDB_LIBRARY_API unsigned char rocksdb_flushoptions_get_wait(
|
||||
extern ROCKSDB_LIBRARY_API rocksdb_cache_t* rocksdb_cache_create_lru(
|
||||
size_t capacity);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_cache_destroy(rocksdb_cache_t* cache);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_cache_disown_data(
|
||||
rocksdb_cache_t* cache);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_cache_set_capacity(
|
||||
rocksdb_cache_t* cache, size_t capacity);
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
|
||||
@@ -1368,7 +1368,7 @@ class DB {
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* /*metadata*/) {}
|
||||
|
||||
// Return a list of all table file checksum info.
|
||||
// Return a list of all table and blob files checksum info.
|
||||
// Note: This function might be of limited use because it cannot be
|
||||
// synchronized with GetLiveFiles.
|
||||
virtual Status GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) = 0;
|
||||
|
||||
@@ -216,16 +216,18 @@ class FilterPolicy {
|
||||
extern const FilterPolicy* NewBloomFilterPolicy(
|
||||
double bits_per_key, bool use_block_based_builder = false);
|
||||
|
||||
// An EXPERIMENTAL new Bloom alternative that saves about 30% space
|
||||
// compared to Bloom filters, with about 3-4x construction time and
|
||||
// similar query times. For example, if you pass in 10 for
|
||||
// An new Bloom alternative that saves about 30% space compared to
|
||||
// Bloom filters, with about 3-4x construction CPU time and similar
|
||||
// query times. For example, if you pass in 10 for
|
||||
// bloom_equivalent_bits_per_key, you'll get the same 0.95% FP rate
|
||||
// as Bloom filter but only using about 7 bits per key. (This
|
||||
// way of configuring the new filter is considered experimental
|
||||
// and/or transitional, so is expected to go away.)
|
||||
// and/or transitional, so is expected to be replaced with a new API.
|
||||
// The constructed filters will be given long-term support.)
|
||||
//
|
||||
// Ribbon filters are ignored by previous versions of RocksDB, as if
|
||||
// no filter was used.
|
||||
// Ribbon filters are compatible with RocksDB >= 6.15.0. Earlier
|
||||
// versions reading the data will behave as if no filter was used
|
||||
// (degraded performance until compaction rebuilds filters).
|
||||
//
|
||||
// Note: this policy can generate Bloom filters in some cases.
|
||||
// For very small filters (well under 1KB), Bloom fallback is by
|
||||
|
||||
@@ -50,7 +50,15 @@ struct IOStatsContext {
|
||||
uint64_t cpu_read_nanos;
|
||||
};
|
||||
|
||||
// Get Thread-local IOStatsContext object pointer
|
||||
// If RocksDB is compiled with -DNIOSTATS_CONTEXT, then a pointer to a global,
|
||||
// non-thread-local IOStatsContext object will be returned. Attempts to update
|
||||
// this object will be ignored, and reading from it will also be no-op.
|
||||
// Otherwise,
|
||||
// a) if thread-local is supported on the platform, then a pointer to
|
||||
// a thread-local IOStatsContext object will be returned.
|
||||
// b) if thread-local is NOT supported, then compilation will fail.
|
||||
//
|
||||
// This function never returns nullptr.
|
||||
IOStatsContext* get_iostats_context();
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -118,6 +118,7 @@ enum class FlushReason : int {
|
||||
// When set the flush reason to kErrorRecoveryRetryFlush, SwitchMemtable
|
||||
// will not be called to avoid many small immutable memtables.
|
||||
kErrorRecoveryRetryFlush = 0xc,
|
||||
kWalFull = 0xd,
|
||||
};
|
||||
|
||||
// TODO: In the future, BackgroundErrorReason will only be used to indicate
|
||||
|
||||
@@ -230,8 +230,15 @@ struct PerfContext {
|
||||
bool per_level_perf_context_enabled = false;
|
||||
};
|
||||
|
||||
// Get Thread-local PerfContext object pointer
|
||||
// if defined(NPERF_CONTEXT), then the pointer is not thread-local
|
||||
// If RocksDB is compiled with -DNPERF_CONTEXT, then a pointer to a global,
|
||||
// non-thread-local PerfContext object will be returned. Attempts to update
|
||||
// this object will be ignored, and reading from it will also be no-op.
|
||||
// Otherwise,
|
||||
// a) if thread-local is supported on the platform, then a pointer to
|
||||
// a thread-local PerfContext object will be returned.
|
||||
// b) if thread-local is NOT supported, then compilation will fail.
|
||||
//
|
||||
// This function never returns nullptr.
|
||||
PerfContext* get_perf_context();
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -371,6 +371,21 @@ class BackupEngineReadOnlyBase {
|
||||
public:
|
||||
virtual ~BackupEngineReadOnlyBase() {}
|
||||
|
||||
// Returns info about the latest good backup in backup_info, or NotFound
|
||||
// no good backup exists.
|
||||
// Setting include_file_details=true provides information about each
|
||||
// backed-up file in BackupInfo::file_details and more.
|
||||
virtual Status GetLatestBackupInfo(
|
||||
BackupInfo* backup_info, bool include_file_details = false) const = 0;
|
||||
|
||||
// Returns info about a specific backup in backup_info, or NotFound
|
||||
// or Corruption status if the requested backup id does not exist or is
|
||||
// known corrupt.
|
||||
// Setting include_file_details=true provides information about each
|
||||
// backed-up file in BackupInfo::file_details and more.
|
||||
virtual Status GetBackupInfo(BackupID backup_id, BackupInfo* backup_info,
|
||||
bool include_file_details = false) const = 0;
|
||||
|
||||
// Returns info about backups in backup_info
|
||||
// Setting include_file_details=true provides information about each
|
||||
// backed-up file in BackupInfo::file_details and more.
|
||||
@@ -439,7 +454,7 @@ class BackupEngineAppendOnlyBase {
|
||||
// same as CreateNewBackup, but stores extra application metadata.
|
||||
virtual Status CreateNewBackupWithMetadata(
|
||||
const CreateBackupOptions& options, DB* db,
|
||||
const std::string& app_metadata) = 0;
|
||||
const std::string& app_metadata, BackupID* new_backup_id = nullptr) = 0;
|
||||
|
||||
// keep here for backward compatibility.
|
||||
virtual Status CreateNewBackupWithMetadata(
|
||||
@@ -451,9 +466,12 @@ class BackupEngineAppendOnlyBase {
|
||||
return CreateNewBackupWithMetadata(options, db, app_metadata);
|
||||
}
|
||||
|
||||
// Captures the state of the database by creating a new (latest) backup
|
||||
virtual Status CreateNewBackup(const CreateBackupOptions& options, DB* db) {
|
||||
return CreateNewBackupWithMetadata(options, db, "");
|
||||
// Captures the state of the database by creating a new (latest) backup.
|
||||
// On success (OK status), the BackupID of the new backup is saved to
|
||||
// *new_backup_id when not nullptr.
|
||||
virtual Status CreateNewBackup(const CreateBackupOptions& options, DB* db,
|
||||
BackupID* new_backup_id = nullptr) {
|
||||
return CreateNewBackupWithMetadata(options, db, "", new_backup_id);
|
||||
}
|
||||
|
||||
// keep here for backward compatibility.
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
#define ROCKSDB_MAJOR 6
|
||||
#define ROCKSDB_MINOR 19
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_MINOR 20
|
||||
#define ROCKSDB_PATCH 3
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -1479,8 +1479,8 @@ public class RocksDBTest {
|
||||
assertThat(livefiles.manifestFileSize).isEqualTo(57);
|
||||
assertThat(livefiles.files.size()).isEqualTo(3);
|
||||
assertThat(livefiles.files.get(0)).isEqualTo("/CURRENT");
|
||||
assertThat(livefiles.files.get(1)).isEqualTo("/MANIFEST-000003");
|
||||
assertThat(livefiles.files.get(2)).isEqualTo("/OPTIONS-000006");
|
||||
assertThat(livefiles.files.get(1)).isEqualTo("/MANIFEST-000004");
|
||||
assertThat(livefiles.files.get(2)).isEqualTo("/OPTIONS-000007");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,23 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
#ifdef ROCKSDB_SUPPORT_THREAD_LOCAL
|
||||
#ifdef NIOSTATS_CONTEXT
|
||||
// Should not be used because the counters are not thread-safe.
|
||||
// Put here just to make get_iostats_context() simple without ifdef.
|
||||
static IOStatsContext iostats_context;
|
||||
#elif defined(ROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
__thread IOStatsContext iostats_context;
|
||||
#else
|
||||
#error \
|
||||
"No thread-local support. Disable iostats context with -DNIOSTATS_CONTEXT."
|
||||
#endif
|
||||
|
||||
IOStatsContext* get_iostats_context() {
|
||||
#ifdef ROCKSDB_SUPPORT_THREAD_LOCAL
|
||||
return &iostats_context;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void IOStatsContext::Reset() {
|
||||
#ifndef NIOSTATS_CONTEXT
|
||||
thread_pool_id = Env::Priority::TOTAL;
|
||||
bytes_read = 0;
|
||||
bytes_written = 0;
|
||||
@@ -35,6 +39,7 @@ void IOStatsContext::Reset() {
|
||||
logger_nanos = 0;
|
||||
cpu_write_nanos = 0;
|
||||
cpu_read_nanos = 0;
|
||||
#endif //! NIOSTATS_CONTEXT
|
||||
}
|
||||
|
||||
#define IOSTATS_CONTEXT_OUTPUT(counter) \
|
||||
@@ -43,6 +48,10 @@ void IOStatsContext::Reset() {
|
||||
}
|
||||
|
||||
std::string IOStatsContext::ToString(bool exclude_zero_counters) const {
|
||||
#ifdef NIOSTATS_CONTEXT
|
||||
(void)exclude_zero_counters;
|
||||
return "";
|
||||
#else
|
||||
std::ostringstream ss;
|
||||
IOSTATS_CONTEXT_OUTPUT(thread_pool_id);
|
||||
IOSTATS_CONTEXT_OUTPUT(bytes_read);
|
||||
@@ -61,6 +70,7 @@ std::string IOStatsContext::ToString(bool exclude_zero_counters) const {
|
||||
std::string str = ss.str();
|
||||
str.erase(str.find_last_not_of(", ") + 1);
|
||||
return str;
|
||||
#endif //! NIOSTATS_CONTEXT
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "monitoring/perf_step_timer.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
|
||||
#ifdef ROCKSDB_SUPPORT_THREAD_LOCAL
|
||||
#if defined(ROCKSDB_SUPPORT_THREAD_LOCAL) && !defined(NIOSTATS_CONTEXT)
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
extern __thread IOStatsContext iostats_context;
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -44,7 +44,7 @@ extern __thread IOStatsContext iostats_context;
|
||||
PerfLevel::kEnableTimeAndCPUTimeExceptForMutex); \
|
||||
iostats_step_timer_##metric.Start();
|
||||
|
||||
#else // ROCKSDB_SUPPORT_THREAD_LOCAL
|
||||
#else // ROCKSDB_SUPPORT_THREAD_LOCAL && !NIOSTATS_CONTEXT
|
||||
|
||||
#define IOSTATS_ADD(metric, value)
|
||||
#define IOSTATS_ADD_IF_POSITIVE(metric, value)
|
||||
@@ -57,4 +57,4 @@ extern __thread IOStatsContext iostats_context;
|
||||
#define IOSTATS_TIMER_GUARD(metric)
|
||||
#define IOSTATS_CPU_TIMER_GUARD(metric, clock) static_cast<void>(clock)
|
||||
|
||||
#endif // ROCKSDB_SUPPORT_THREAD_LOCAL
|
||||
#endif // ROCKSDB_SUPPORT_THREAD_LOCAL && !NIOSTATS_CONTEXT
|
||||
@@ -9,26 +9,22 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
#if defined(NPERF_CONTEXT) || !defined(ROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
#if defined(NPERF_CONTEXT)
|
||||
// Should not be used because the counters are not thread-safe.
|
||||
// Put here just to make get_perf_context() simple without ifdef.
|
||||
PerfContext perf_context;
|
||||
#else
|
||||
#elif defined(ROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
#if defined(OS_SOLARIS)
|
||||
__thread PerfContext perf_context_;
|
||||
#else
|
||||
__thread PerfContext perf_context;
|
||||
#else // OS_SOLARIS
|
||||
thread_local PerfContext perf_context;
|
||||
#endif
|
||||
#endif // OS_SOLARIS
|
||||
#else
|
||||
#error "No thread-local support. Disable perf context with -DNPERF_CONTEXT."
|
||||
#endif
|
||||
|
||||
PerfContext* get_perf_context() {
|
||||
#if defined(NPERF_CONTEXT) || !defined(ROCKSDB_SUPPORT_THREAD_LOCAL)
|
||||
return &perf_context;
|
||||
#else
|
||||
#if defined(OS_SOLARIS)
|
||||
return &perf_context_;
|
||||
#else
|
||||
return &perf_context;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
PerfContext::~PerfContext() {
|
||||
|
||||
+2
-1
@@ -682,7 +682,8 @@ IOStatus WinFileSystem::GetChildren(const std::string& dir,
|
||||
// which appear only on some platforms
|
||||
const bool ignore =
|
||||
((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
|
||||
(strcmp(data.cFileName, ".") == 0 || strcmp(data.cFileName, "..") == 0);
|
||||
(RX_FNCMP(data.cFileName, ".") == 0 ||
|
||||
RX_FNCMP(data.cFileName, "..") == 0);
|
||||
if (!ignore) {
|
||||
auto x = RX_FILESTRING(data.cFileName, RX_FNLEN(data.cFileName));
|
||||
result->push_back(FN_TO_RX(x));
|
||||
|
||||
@@ -355,6 +355,7 @@ extern void SetCpuPriority(ThreadId id, CpuPriority priority);
|
||||
#define RX_FILESTRING std::wstring
|
||||
#define RX_FN(a) ROCKSDB_NAMESPACE::port::utf8_to_utf16(a)
|
||||
#define FN_TO_RX(a) ROCKSDB_NAMESPACE::port::utf16_to_utf8(a)
|
||||
#define RX_FNCMP(a, b) ::wcscmp(a, RX_FN(b).c_str())
|
||||
#define RX_FNLEN(a) ::wcslen(a)
|
||||
|
||||
#define RX_DeleteFile DeleteFileW
|
||||
@@ -379,6 +380,7 @@ extern void SetCpuPriority(ThreadId id, CpuPriority priority);
|
||||
#define RX_FILESTRING std::string
|
||||
#define RX_FN(a) a
|
||||
#define FN_TO_RX(a) a
|
||||
#define RX_FNCMP(a, b) strcmp(a, b)
|
||||
#define RX_FNLEN(a) strlen(a)
|
||||
|
||||
#define RX_DeleteFile DeleteFileA
|
||||
|
||||
@@ -205,6 +205,7 @@ LIB_SOURCES = \
|
||||
util/compression_context_cache.cc \
|
||||
util/concurrent_task_limiter_impl.cc \
|
||||
util/crc32c.cc \
|
||||
util/crc32c_arm64.cc \
|
||||
util/dynamic_bloom.cc \
|
||||
util/hash.cc \
|
||||
util/murmurhash.cc \
|
||||
@@ -275,11 +276,6 @@ LIB_SOURCES = \
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc \
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc \
|
||||
|
||||
ifeq ($(ARMCRC_SOURCE),1)
|
||||
LIB_SOURCES +=\
|
||||
util/crc32c_arm64.cc
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -maltivec -xc /dev/null 2>&1))
|
||||
LIB_SOURCES_ASM =\
|
||||
util/crc32c_ppc_asm.S
|
||||
|
||||
+17
-1
@@ -23,6 +23,9 @@
|
||||
#include <mach/mach_host.h>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <condition_variable>
|
||||
@@ -2638,7 +2641,7 @@ class Benchmark {
|
||||
fprintf(stderr, "RocksDB: version %d.%d\n",
|
||||
kMajorVersion, kMinorVersion);
|
||||
|
||||
#if defined(__linux) || defined(__APPLE__)
|
||||
#if defined(__linux) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||
time_t now = time(nullptr);
|
||||
char buf[52];
|
||||
// Lint complains about ctime() usage, so replace it with ctime_r(). The
|
||||
@@ -2696,6 +2699,19 @@ class Benchmark {
|
||||
fprintf(stderr, "CPU: %d * %s\n", h.max_cpus, cpu_type.c_str());
|
||||
fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
int ncpus;
|
||||
size_t len = sizeof(ncpus);
|
||||
int mib[2] = {CTL_HW, HW_NCPU};
|
||||
if (sysctl(mib, 2, &ncpus, &len, nullptr, 0) == 0) {
|
||||
char cpu_type[16];
|
||||
len = sizeof(cpu_type) - 1;
|
||||
mib[1] = HW_MACHINE;
|
||||
if (sysctl(mib, 2, cpu_type, &len, nullptr, 0) == 0) cpu_type[len] = 0;
|
||||
|
||||
fprintf(stderr, "CPU: %d * %s\n", ncpus, cpu_type);
|
||||
// no programmatic way to get the cache line size except on PPC
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ default_params = {
|
||||
"enable_pipelined_write": lambda: random.randint(0, 1),
|
||||
"enable_compaction_filter": lambda: random.choice([0, 0, 0, 1]),
|
||||
"expected_values_path": lambda: setup_expected_values_file(),
|
||||
"fail_if_options_file_error": lambda: random.randint(0, 1),
|
||||
"flush_one_in": 1000000,
|
||||
"file_checksum_impl": lambda: random.choice(["none", "crc32c", "xxh64", "big"]),
|
||||
"get_live_files_one_in": 1000000,
|
||||
@@ -137,6 +138,7 @@ default_params = {
|
||||
"max_key_len": 3,
|
||||
"key_len_percent_dist": "1,30,69",
|
||||
"read_fault_one_in": lambda: random.choice([0, 1000]),
|
||||
"open_metadata_write_fault_one_in": lambda: random.choice([0, 8]),
|
||||
"sync_fault_injection": False,
|
||||
"get_property_one_in": 1000000,
|
||||
"paranoid_file_checks": lambda: random.choice([0, 1, 1, 1]),
|
||||
@@ -279,7 +281,6 @@ blob_params = {
|
||||
"blob_garbage_collection_age_cutoff": lambda: random.choice([0.0, 0.25, 0.5, 0.75, 1.0]),
|
||||
# The following are currently incompatible with the integrated BlobDB
|
||||
"use_merge": 0,
|
||||
"backup_one_in": 0,
|
||||
}
|
||||
|
||||
ts_params = {
|
||||
|
||||
@@ -349,6 +349,85 @@ TEST_F(LdbCmdTest, DumpFileChecksumNoChecksum) {
|
||||
ASSERT_OK(fct_helper_ac.VerifyChecksumInManifest(live_files));
|
||||
}
|
||||
|
||||
TEST_F(LdbCmdTest, BlobDBDumpFileChecksumNoChecksum) {
|
||||
Env* base_env = TryLoadCustomOrDefaultEnv();
|
||||
std::unique_ptr<Env> env(NewMemEnv(base_env));
|
||||
Options opts;
|
||||
opts.env = env.get();
|
||||
opts.create_if_missing = true;
|
||||
opts.enable_blob_files = true;
|
||||
|
||||
DB* db = nullptr;
|
||||
std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test");
|
||||
ASSERT_OK(DB::Open(opts, dbname, &db));
|
||||
|
||||
WriteOptions wopts;
|
||||
FlushOptions fopts;
|
||||
fopts.wait = true;
|
||||
Random rnd(test::RandomSeed());
|
||||
for (int i = 0; i < 200; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
for (int i = 100; i < 300; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
for (int i = 200; i < 400; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
for (int i = 300; i < 400; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
|
||||
char arg1[] = "./ldb";
|
||||
std::string arg2_str = "--db=" + dbname;
|
||||
char arg3[] = "file_checksum_dump";
|
||||
char* argv[] = {arg1, const_cast<char*>(arg2_str.c_str()), arg3};
|
||||
|
||||
ASSERT_EQ(0,
|
||||
LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
|
||||
|
||||
// Verify each sst and blob file checksum value and checksum name
|
||||
FileChecksumTestHelper fct_helper(opts, db, dbname);
|
||||
ASSERT_OK(fct_helper.VerifyEachFileChecksum());
|
||||
|
||||
// Manually trigger compaction
|
||||
std::ostringstream oss_b_buf;
|
||||
oss_b_buf << std::setfill('0') << std::setw(8) << std::fixed << 0;
|
||||
std::ostringstream oss_e_buf;
|
||||
oss_e_buf << std::setfill('0') << std::setw(8) << std::fixed << 399;
|
||||
std::string b_buf = oss_b_buf.str();
|
||||
std::string e_buf = oss_e_buf.str();
|
||||
Slice begin(b_buf);
|
||||
Slice end(e_buf);
|
||||
|
||||
CompactRangeOptions options;
|
||||
ASSERT_OK(db->CompactRange(options, &begin, &end));
|
||||
// Verify each sst file checksum after compaction
|
||||
FileChecksumTestHelper fct_helper_ac(opts, db, dbname);
|
||||
ASSERT_OK(fct_helper_ac.VerifyEachFileChecksum());
|
||||
|
||||
ASSERT_EQ(0,
|
||||
LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(LdbCmdTest, DumpFileChecksumCRC32) {
|
||||
Env* base_env = TryLoadCustomOrDefaultEnv();
|
||||
std::unique_ptr<Env> env(NewMemEnv(base_env));
|
||||
@@ -430,6 +509,85 @@ TEST_F(LdbCmdTest, DumpFileChecksumCRC32) {
|
||||
ASSERT_OK(fct_helper_ac.VerifyChecksumInManifest(live_files));
|
||||
}
|
||||
|
||||
TEST_F(LdbCmdTest, BlobDBDumpFileChecksumCRC32) {
|
||||
Env* base_env = TryLoadCustomOrDefaultEnv();
|
||||
std::unique_ptr<Env> env(NewMemEnv(base_env));
|
||||
Options opts;
|
||||
opts.env = env.get();
|
||||
opts.create_if_missing = true;
|
||||
opts.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
opts.enable_blob_files = true;
|
||||
|
||||
DB* db = nullptr;
|
||||
std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test");
|
||||
ASSERT_OK(DB::Open(opts, dbname, &db));
|
||||
|
||||
WriteOptions wopts;
|
||||
FlushOptions fopts;
|
||||
fopts.wait = true;
|
||||
Random rnd(test::RandomSeed());
|
||||
for (int i = 0; i < 100; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
for (int i = 50; i < 150; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
for (int i = 100; i < 200; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
for (int i = 150; i < 250; i++) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(8) << std::fixed << i;
|
||||
std::string v = rnd.RandomString(100);
|
||||
ASSERT_OK(db->Put(wopts, oss.str(), v));
|
||||
}
|
||||
ASSERT_OK(db->Flush(fopts));
|
||||
|
||||
char arg1[] = "./ldb";
|
||||
std::string arg2_str = "--db=" + dbname;
|
||||
char arg3[] = "file_checksum_dump";
|
||||
char* argv[] = {arg1, const_cast<char*>(arg2_str.c_str()), arg3};
|
||||
|
||||
ASSERT_EQ(0,
|
||||
LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
|
||||
|
||||
// Verify each sst and blob file checksum value and checksum name
|
||||
FileChecksumTestHelper fct_helper(opts, db, dbname);
|
||||
ASSERT_OK(fct_helper.VerifyEachFileChecksum());
|
||||
|
||||
// Manually trigger compaction
|
||||
std::ostringstream oss_b_buf;
|
||||
oss_b_buf << std::setfill('0') << std::setw(8) << std::fixed << 0;
|
||||
std::ostringstream oss_e_buf;
|
||||
oss_e_buf << std::setfill('0') << std::setw(8) << std::fixed << 249;
|
||||
std::string b_buf = oss_b_buf.str();
|
||||
std::string e_buf = oss_e_buf.str();
|
||||
Slice begin(b_buf);
|
||||
Slice end(e_buf);
|
||||
|
||||
CompactRangeOptions options;
|
||||
ASSERT_OK(db->CompactRange(options, &begin, &end));
|
||||
// Verify each sst file checksum after compaction
|
||||
FileChecksumTestHelper fct_helper_ac(opts, db, dbname);
|
||||
ASSERT_OK(fct_helper_ac.VerifyEachFileChecksum());
|
||||
|
||||
ASSERT_EQ(0,
|
||||
LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr));
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(LdbCmdTest, OptionParsing) {
|
||||
// test parsing flags
|
||||
Options opts;
|
||||
|
||||
@@ -129,7 +129,8 @@ class BackupEngineImpl {
|
||||
~BackupEngineImpl();
|
||||
|
||||
Status CreateNewBackupWithMetadata(const CreateBackupOptions& options, DB* db,
|
||||
const std::string& app_metadata);
|
||||
const std::string& app_metadata,
|
||||
BackupID* new_backup_id_ptr);
|
||||
|
||||
Status PurgeOldBackups(uint32_t num_backups_to_keep);
|
||||
|
||||
@@ -144,6 +145,9 @@ class BackupEngineImpl {
|
||||
void GetBackupInfo(std::vector<BackupInfo>* backup_info,
|
||||
bool include_file_details) const;
|
||||
|
||||
Status GetBackupInfo(BackupID backup_id, BackupInfo* backup_info,
|
||||
bool include_file_details = false) const;
|
||||
|
||||
void GetCorruptedBackups(std::vector<BackupID>* corrupt_backup_ids) const;
|
||||
|
||||
Status RestoreDBFromBackup(const RestoreOptions& options, BackupID backup_id,
|
||||
@@ -460,6 +464,10 @@ class BackupEngineImpl {
|
||||
mutable std::shared_ptr<Env> env_for_open_;
|
||||
}; // BackupMeta
|
||||
|
||||
void SetBackupInfoFromBackupMeta(BackupID id, const BackupMeta& meta,
|
||||
BackupInfo* backup_info,
|
||||
bool include_file_details) const;
|
||||
|
||||
inline std::string GetAbsolutePath(
|
||||
const std::string &relative_path = "") const {
|
||||
assert(relative_path.size() == 0 || relative_path[0] != '/');
|
||||
@@ -802,9 +810,11 @@ class BackupEngineImplThreadSafe : public BackupEngine,
|
||||
|
||||
using BackupEngine::CreateNewBackupWithMetadata;
|
||||
Status CreateNewBackupWithMetadata(const CreateBackupOptions& options, DB* db,
|
||||
const std::string& app_metadata) override {
|
||||
const std::string& app_metadata,
|
||||
BackupID* new_backup_id) override {
|
||||
WriteLock lock(&mutex_);
|
||||
return impl_.CreateNewBackupWithMetadata(options, db, app_metadata);
|
||||
return impl_.CreateNewBackupWithMetadata(options, db, app_metadata,
|
||||
new_backup_id);
|
||||
}
|
||||
|
||||
Status PurgeOldBackups(uint32_t num_backups_to_keep) override {
|
||||
@@ -827,6 +837,19 @@ class BackupEngineImplThreadSafe : public BackupEngine,
|
||||
return impl_.GarbageCollect();
|
||||
}
|
||||
|
||||
Status GetLatestBackupInfo(BackupInfo* backup_info,
|
||||
bool include_file_details = false) const override {
|
||||
ReadLock lock(&mutex_);
|
||||
return impl_.GetBackupInfo(kLatestBackupIDMarker, backup_info,
|
||||
include_file_details);
|
||||
}
|
||||
|
||||
Status GetBackupInfo(BackupID backup_id, BackupInfo* backup_info,
|
||||
bool include_file_details = false) const override {
|
||||
ReadLock lock(&mutex_);
|
||||
return impl_.GetBackupInfo(backup_id, backup_info, include_file_details);
|
||||
}
|
||||
|
||||
void GetBackupInfo(std::vector<BackupInfo>* backup_info,
|
||||
bool include_file_details) const override {
|
||||
ReadLock lock(&mutex_);
|
||||
@@ -1181,8 +1204,8 @@ Status BackupEngineImpl::Initialize() {
|
||||
}
|
||||
|
||||
Status BackupEngineImpl::CreateNewBackupWithMetadata(
|
||||
const CreateBackupOptions& options, DB* db,
|
||||
const std::string& app_metadata) {
|
||||
const CreateBackupOptions& options, DB* db, const std::string& app_metadata,
|
||||
BackupID* new_backup_id_ptr) {
|
||||
assert(initialized_);
|
||||
assert(!read_only_);
|
||||
if (app_metadata.size() > kMaxAppMetaSize) {
|
||||
@@ -1417,6 +1440,9 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
|
||||
// in the LATEST_BACKUP file
|
||||
latest_backup_id_ = new_backup_id;
|
||||
latest_valid_backup_id_ = new_backup_id;
|
||||
if (new_backup_id_ptr) {
|
||||
*new_backup_id_ptr = new_backup_id;
|
||||
}
|
||||
ROCKS_LOG_INFO(options_.info_log, "Backup DONE. All is good");
|
||||
|
||||
// backup_speed is in byte/second
|
||||
@@ -1546,30 +1572,61 @@ Status BackupEngineImpl::DeleteBackupNoGC(BackupID backup_id) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void BackupEngineImpl::SetBackupInfoFromBackupMeta(
|
||||
BackupID id, const BackupMeta& meta, BackupInfo* backup_info,
|
||||
bool include_file_details) const {
|
||||
*backup_info = BackupInfo(id, meta.GetTimestamp(), meta.GetSize(),
|
||||
meta.GetNumberFiles(), meta.GetAppMetadata());
|
||||
if (include_file_details) {
|
||||
auto& file_details = backup_info->file_details;
|
||||
file_details.reserve(meta.GetFiles().size());
|
||||
for (auto& file_ptr : meta.GetFiles()) {
|
||||
BackupFileInfo& finfo = *file_details.emplace(file_details.end());
|
||||
finfo.relative_filename = file_ptr->filename;
|
||||
finfo.size = file_ptr->size;
|
||||
}
|
||||
backup_info->name_for_open = GetAbsolutePath(GetPrivateFileRel(id));
|
||||
backup_info->name_for_open.pop_back(); // remove trailing '/'
|
||||
backup_info->env_for_open = meta.GetEnvForOpen();
|
||||
}
|
||||
}
|
||||
|
||||
Status BackupEngineImpl::GetBackupInfo(BackupID backup_id,
|
||||
BackupInfo* backup_info,
|
||||
bool include_file_details) const {
|
||||
assert(initialized_);
|
||||
if (backup_id == kLatestBackupIDMarker) {
|
||||
// Note: Read latest_valid_backup_id_ inside of lock
|
||||
backup_id = latest_valid_backup_id_;
|
||||
}
|
||||
auto corrupt_itr = corrupt_backups_.find(backup_id);
|
||||
if (corrupt_itr != corrupt_backups_.end()) {
|
||||
return Status::Corruption(corrupt_itr->second.first.ToString());
|
||||
}
|
||||
auto backup_itr = backups_.find(backup_id);
|
||||
if (backup_itr == backups_.end()) {
|
||||
return Status::NotFound("Backup not found");
|
||||
}
|
||||
auto& backup = backup_itr->second;
|
||||
if (backup->Empty()) {
|
||||
return Status::NotFound("Backup not found");
|
||||
}
|
||||
|
||||
SetBackupInfoFromBackupMeta(backup_id, *backup, backup_info,
|
||||
include_file_details);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void BackupEngineImpl::GetBackupInfo(std::vector<BackupInfo>* backup_info,
|
||||
bool include_file_details) const {
|
||||
assert(initialized_);
|
||||
backup_info->reserve(backups_.size());
|
||||
backup_info->resize(backups_.size());
|
||||
size_t i = 0;
|
||||
for (auto& backup : backups_) {
|
||||
const BackupMeta& meta = *backup.second;
|
||||
if (!meta.Empty()) {
|
||||
backup_info->push_back(BackupInfo(backup.first, meta.GetTimestamp(),
|
||||
meta.GetSize(), meta.GetNumberFiles(),
|
||||
meta.GetAppMetadata()));
|
||||
BackupInfo& binfo = backup_info->back();
|
||||
if (include_file_details) {
|
||||
auto& file_details = binfo.file_details;
|
||||
file_details.reserve(meta.GetFiles().size());
|
||||
for (auto& file_ptr : meta.GetFiles()) {
|
||||
BackupFileInfo& finfo = *file_details.emplace(file_details.end());
|
||||
finfo.relative_filename = file_ptr->filename;
|
||||
finfo.size = file_ptr->size;
|
||||
}
|
||||
binfo.name_for_open =
|
||||
GetAbsolutePath(GetPrivateFileRel(binfo.backup_id));
|
||||
binfo.name_for_open.pop_back(); // remove trailing '/'
|
||||
binfo.env_for_open = meta.GetEnvForOpen();
|
||||
}
|
||||
SetBackupInfoFromBackupMeta(backup.first, meta, &backup_info->at(i++),
|
||||
include_file_details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2646,17 +2646,17 @@ TEST_F(BackupableDBTest, OpenBackupAsReadOnlyDB) {
|
||||
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), /*flush*/ false));
|
||||
db_.reset(); // CloseDB
|
||||
DestroyDB(dbname_, options_);
|
||||
std::vector<BackupInfo> backup_info;
|
||||
BackupInfo backup_info;
|
||||
// First, check that we get empty fields without include_file_details
|
||||
backup_engine_->GetBackupInfo(&backup_info, /*with file details*/ false);
|
||||
ASSERT_EQ(backup_info.size(), 2);
|
||||
ASSERT_EQ(backup_info[0].name_for_open, "");
|
||||
ASSERT_FALSE(backup_info[0].env_for_open);
|
||||
ASSERT_OK(backup_engine_->GetBackupInfo(/*id*/ 1U, &backup_info,
|
||||
/*with file details*/ false));
|
||||
ASSERT_EQ(backup_info.name_for_open, "");
|
||||
ASSERT_FALSE(backup_info.env_for_open);
|
||||
|
||||
// Now for the real test
|
||||
backup_info.clear();
|
||||
backup_engine_->GetBackupInfo(&backup_info, /*with file details*/ true);
|
||||
ASSERT_EQ(backup_info.size(), 2);
|
||||
backup_info = BackupInfo();
|
||||
ASSERT_OK(backup_engine_->GetBackupInfo(/*id*/ 1U, &backup_info,
|
||||
/*with file details*/ true));
|
||||
|
||||
// Caution: DBOptions only holds a raw pointer to Env, so something else
|
||||
// must keep it alive.
|
||||
@@ -2668,9 +2668,9 @@ TEST_F(BackupableDBTest, OpenBackupAsReadOnlyDB) {
|
||||
opts.create_if_missing = false;
|
||||
opts.info_log.reset();
|
||||
|
||||
opts.env = backup_info[0].env_for_open.get();
|
||||
std::string name = backup_info[0].name_for_open;
|
||||
backup_info.clear();
|
||||
opts.env = backup_info.env_for_open.get();
|
||||
std::string name = backup_info.name_for_open;
|
||||
backup_info = BackupInfo();
|
||||
ASSERT_OK(DB::OpenForReadOnly(opts, name, &db));
|
||||
|
||||
AssertExists(db, 0, 100);
|
||||
@@ -2680,13 +2680,13 @@ TEST_F(BackupableDBTest, OpenBackupAsReadOnlyDB) {
|
||||
db = nullptr;
|
||||
|
||||
// Case 2: Keeping BackupInfo alive rather than BackupEngine also suffices
|
||||
backup_engine_->GetBackupInfo(&backup_info, /*with file details*/ true);
|
||||
ASSERT_EQ(backup_info.size(), 2);
|
||||
ASSERT_OK(backup_engine_->GetBackupInfo(/*id*/ 2U, &backup_info,
|
||||
/*with file details*/ true));
|
||||
CloseBackupEngine();
|
||||
opts.create_if_missing = true; // check also OK (though pointless)
|
||||
opts.env = backup_info[1].env_for_open.get();
|
||||
name = backup_info[1].name_for_open;
|
||||
// Note: keeping backup_info[1] alive
|
||||
opts.env = backup_info.env_for_open.get();
|
||||
name = backup_info.name_for_open;
|
||||
// Note: keeping backup_info alive
|
||||
ASSERT_OK(DB::OpenForReadOnly(opts, name, &db));
|
||||
|
||||
AssertExists(db, 0, 200);
|
||||
@@ -2716,19 +2716,19 @@ TEST_F(BackupableDBTest, GarbageCollectionBeforeBackup) {
|
||||
OpenDBAndBackupEngine(true);
|
||||
|
||||
ASSERT_OK(backup_chroot_env_->CreateDirIfMissing(backupdir_ + "/shared"));
|
||||
std::string file_five = backupdir_ + "/shared/000008.sst";
|
||||
std::string file_five = backupdir_ + "/shared/000009.sst";
|
||||
std::string file_five_contents = "I'm not really a sst file";
|
||||
// this depends on the fact that 00008.sst is the first file created by the DB
|
||||
// this depends on the fact that 00009.sst is the first file created by the DB
|
||||
ASSERT_OK(file_manager_->WriteToFile(file_five, file_five_contents));
|
||||
|
||||
FillDB(db_.get(), 0, 100);
|
||||
// backup overwrites file 000008.sst
|
||||
// backup overwrites file 000009.sst
|
||||
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
|
||||
|
||||
std::string new_file_five_contents;
|
||||
ASSERT_OK(ReadFileToString(backup_chroot_env_.get(), file_five,
|
||||
&new_file_five_contents));
|
||||
// file 000008.sst was overwritten
|
||||
// file 000009.sst was overwritten
|
||||
ASSERT_TRUE(new_file_five_contents != file_five_contents);
|
||||
|
||||
CloseDBAndBackupEngine();
|
||||
@@ -2848,17 +2848,32 @@ TEST_F(BackupableDBTest, BackupWithMetadata) {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
const std::string metadata = std::to_string(i);
|
||||
FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
|
||||
ASSERT_OK(
|
||||
backup_engine_->CreateNewBackupWithMetadata(db_.get(), metadata, true));
|
||||
// Here also test CreateNewBackupWithMetadata with CreateBackupOptions
|
||||
// and outputting saved BackupID.
|
||||
CreateBackupOptions opts;
|
||||
opts.flush_before_backup = true;
|
||||
BackupID new_id = 0;
|
||||
ASSERT_OK(backup_engine_->CreateNewBackupWithMetadata(opts, db_.get(),
|
||||
metadata, &new_id));
|
||||
ASSERT_EQ(new_id, static_cast<BackupID>(i + 1));
|
||||
}
|
||||
CloseDBAndBackupEngine();
|
||||
|
||||
OpenDBAndBackupEngine();
|
||||
std::vector<BackupInfo> backup_infos;
|
||||
backup_engine_->GetBackupInfo(&backup_infos);
|
||||
ASSERT_EQ(5, backup_infos.size());
|
||||
{ // Verify in bulk BackupInfo
|
||||
std::vector<BackupInfo> backup_infos;
|
||||
backup_engine_->GetBackupInfo(&backup_infos);
|
||||
ASSERT_EQ(5, backup_infos.size());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_EQ(std::to_string(i), backup_infos[i].app_metadata);
|
||||
}
|
||||
}
|
||||
// Also verify in individual BackupInfo
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_EQ(std::to_string(i), backup_infos[i].app_metadata);
|
||||
BackupInfo backup_info;
|
||||
ASSERT_OK(backup_engine_->GetBackupInfo(static_cast<BackupID>(i + 1),
|
||||
&backup_info));
|
||||
ASSERT_EQ(std::to_string(i), backup_info.app_metadata);
|
||||
}
|
||||
CloseDBAndBackupEngine();
|
||||
DestroyDB(dbname_, options_);
|
||||
@@ -3285,6 +3300,8 @@ TEST_F(BackupableDBTest, CreateWhenLatestBackupCorrupted) {
|
||||
// succeed even when latest backup is corrupted.
|
||||
const int kNumKeys = 5000;
|
||||
OpenDBAndBackupEngine(true /* destroy_old_data */);
|
||||
BackupInfo backup_info;
|
||||
ASSERT_TRUE(backup_engine_->GetLatestBackupInfo(&backup_info).IsNotFound());
|
||||
FillDB(db_.get(), 0 /* from */, kNumKeys);
|
||||
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(),
|
||||
true /* flush_before_backup */));
|
||||
@@ -3293,12 +3310,26 @@ TEST_F(BackupableDBTest, CreateWhenLatestBackupCorrupted) {
|
||||
CloseDBAndBackupEngine();
|
||||
|
||||
OpenDBAndBackupEngine();
|
||||
ASSERT_TRUE(backup_engine_->GetLatestBackupInfo(&backup_info).IsNotFound());
|
||||
|
||||
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(),
|
||||
true /* flush_before_backup */));
|
||||
|
||||
ASSERT_TRUE(backup_engine_->GetLatestBackupInfo(&backup_info).ok());
|
||||
ASSERT_EQ(2, backup_info.backup_id);
|
||||
|
||||
std::vector<BackupInfo> backup_infos;
|
||||
backup_engine_->GetBackupInfo(&backup_infos);
|
||||
ASSERT_EQ(1, backup_infos.size());
|
||||
ASSERT_EQ(2, backup_infos[0].backup_id);
|
||||
|
||||
// Verify individual GetBackupInfo by ID
|
||||
ASSERT_TRUE(backup_engine_->GetBackupInfo(0U, &backup_info).IsNotFound());
|
||||
ASSERT_TRUE(backup_engine_->GetBackupInfo(1U, &backup_info).IsCorruption());
|
||||
ASSERT_TRUE(backup_engine_->GetBackupInfo(2U, &backup_info).ok());
|
||||
ASSERT_TRUE(backup_engine_->GetBackupInfo(3U, &backup_info).IsNotFound());
|
||||
ASSERT_TRUE(
|
||||
backup_engine_->GetBackupInfo(999999U, &backup_info).IsNotFound());
|
||||
}
|
||||
|
||||
TEST_F(BackupableDBTest, WriteOnlyEngineNoSharedFileDeletion) {
|
||||
@@ -3456,7 +3487,11 @@ TEST_F(BackupableDBTest, BackgroundThreadCpuPriority) {
|
||||
CreateBackupOptions options;
|
||||
options.decrease_background_thread_cpu_priority = true;
|
||||
options.background_thread_cpu_priority = CpuPriority::kIdle;
|
||||
ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get()));
|
||||
|
||||
// Also check output backup_id with CreateNewBackup
|
||||
BackupID new_id = 0;
|
||||
ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get(), &new_id));
|
||||
ASSERT_EQ(new_id, 5U);
|
||||
|
||||
ASSERT_EQ(priority, CpuPriority::kNormal);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class CheckpointTest : public testing::Test {
|
||||
@@ -793,6 +794,50 @@ TEST_F(CheckpointTest, CheckpointWithUnsyncedDataDropped) {
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(CheckpointTest, CheckpointOptionsFileFailedToPersist) {
|
||||
// Regression test for a bug where checkpoint failed on a DB where persisting
|
||||
// OPTIONS file failed and the DB was opened with
|
||||
// `fail_if_options_file_error == false`.
|
||||
Options options = CurrentOptions();
|
||||
options.fail_if_options_file_error = false;
|
||||
auto fault_fs = std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
|
||||
|
||||
// Setup `FaultInjectionTestFS` and `SyncPoint` callbacks to fail one
|
||||
// operation when inside the OPTIONS file persisting code.
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
fault_fs->SetRandomMetadataWriteError(1 /* one_in */);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PersistRocksDBOptions:start", [fault_fs](void* /* arg */) {
|
||||
fault_fs->EnableMetadataWriteErrorInjection();
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FaultInjectionTestFS::InjectMetadataWriteError:Injected",
|
||||
[fault_fs](void* /* arg */) {
|
||||
fault_fs->DisableMetadataWriteErrorInjection();
|
||||
});
|
||||
options.env = fault_fs_env.get();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
Checkpoint* checkpoint;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
|
||||
ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_));
|
||||
delete checkpoint;
|
||||
|
||||
// Make sure it's usable.
|
||||
options.env = env_;
|
||||
DB* snapshot_db;
|
||||
ASSERT_OK(DB::Open(options, snapshot_name_, &snapshot_db));
|
||||
ReadOptions read_opts;
|
||||
std::string get_result;
|
||||
ASSERT_OK(snapshot_db->Get(read_opts, "key1", &get_result));
|
||||
ASSERT_EQ("val1", get_result);
|
||||
delete snapshot_db;
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(CheckpointTest, CheckpointReadOnlyDB) {
|
||||
ASSERT_OK(Put("foo", "foo_value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
+130
-27
@@ -22,6 +22,7 @@
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "port/lang.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/random.h"
|
||||
@@ -87,8 +88,21 @@ IOStatus TestFSDirectory::Fsync(const IOOptions& options, IODebugContext* dbg) {
|
||||
if (!fs_->IsFilesystemActive()) {
|
||||
return fs_->GetError();
|
||||
}
|
||||
{
|
||||
IOStatus in_s = fs_->InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
fs_->SyncDir(dirname_);
|
||||
return dir_->Fsync(options, dbg);
|
||||
IOStatus s = dir_->Fsync(options, dbg);
|
||||
{
|
||||
IOStatus in_s = fs_->InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
TestFSWritableFile::TestFSWritableFile(const std::string& fname,
|
||||
@@ -159,6 +173,12 @@ IOStatus TestFSWritableFile::Close(const IOOptions& options,
|
||||
if (!fs_->IsFilesystemActive()) {
|
||||
return fs_->GetError();
|
||||
}
|
||||
{
|
||||
IOStatus in_s = fs_->InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
writable_file_opened_ = false;
|
||||
IOStatus io_s;
|
||||
io_s = target_->Append(state_.buffer_, options, dbg);
|
||||
@@ -170,6 +190,10 @@ IOStatus TestFSWritableFile::Close(const IOOptions& options,
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
fs_->WritableFileClosed(state_);
|
||||
IOStatus in_s = fs_->InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -294,6 +318,12 @@ IOStatus FaultInjectionTestFS::NewWritableFile(
|
||||
if (!IsFilesystemActive()) {
|
||||
return GetError();
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
if (IsFilesystemDirectWritable()) {
|
||||
return target()->NewWritableFile(fname, file_opts, result, dbg);
|
||||
}
|
||||
@@ -305,11 +335,19 @@ IOStatus FaultInjectionTestFS::NewWritableFile(
|
||||
// WritableFileWriter* file is opened
|
||||
// again then it will be truncated - so forget our saved state.
|
||||
UntrackFile(fname);
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = TestFSGetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = TestFSGetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -323,6 +361,12 @@ IOStatus FaultInjectionTestFS::ReopenWritableFile(
|
||||
if (IsFilesystemDirectWritable()) {
|
||||
return target()->ReopenWritableFile(fname, file_opts, result, dbg);
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
IOStatus io_s = target()->ReopenWritableFile(fname, file_opts, result, dbg);
|
||||
if (io_s.ok()) {
|
||||
result->reset(
|
||||
@@ -330,11 +374,19 @@ IOStatus FaultInjectionTestFS::ReopenWritableFile(
|
||||
// WritableFileWriter* file is opened
|
||||
// again then it will be truncated - so forget our saved state.
|
||||
UntrackFile(fname);
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = TestFSGetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = TestFSGetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -348,17 +400,31 @@ IOStatus FaultInjectionTestFS::NewRandomRWFile(
|
||||
if (IsFilesystemDirectWritable()) {
|
||||
return target()->NewRandomRWFile(fname, file_opts, result, dbg);
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
IOStatus io_s = target()->NewRandomRWFile(fname, file_opts, result, dbg);
|
||||
if (io_s.ok()) {
|
||||
result->reset(new TestFSRandomRWFile(fname, std::move(*result), this));
|
||||
// WritableFileWriter* file is opened
|
||||
// again then it will be truncated - so forget our saved state.
|
||||
UntrackFile(fname);
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = TestFSGetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = TestFSGetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -385,9 +451,21 @@ IOStatus FaultInjectionTestFS::DeleteFile(const std::string& f,
|
||||
if (!IsFilesystemActive()) {
|
||||
return GetError();
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
IOStatus io_s = FileSystemWrapper::DeleteFile(f, options, dbg);
|
||||
if (io_s.ok()) {
|
||||
UntrackFile(f);
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -399,21 +477,33 @@ IOStatus FaultInjectionTestFS::RenameFile(const std::string& s,
|
||||
if (!IsFilesystemActive()) {
|
||||
return GetError();
|
||||
}
|
||||
{
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
IOStatus io_s = FileSystemWrapper::RenameFile(s, t, options, dbg);
|
||||
|
||||
if (io_s.ok()) {
|
||||
MutexLock l(&mutex_);
|
||||
if (db_file_state_.find(s) != db_file_state_.end()) {
|
||||
db_file_state_[t] = db_file_state_[s];
|
||||
db_file_state_.erase(s);
|
||||
}
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
if (db_file_state_.find(s) != db_file_state_.end()) {
|
||||
db_file_state_[t] = db_file_state_[s];
|
||||
db_file_state_.erase(s);
|
||||
}
|
||||
|
||||
auto sdn = TestFSGetDirAndName(s);
|
||||
auto tdn = TestFSGetDirAndName(t);
|
||||
if (dir_to_new_files_since_last_sync_[sdn.first].erase(sdn.second) != 0) {
|
||||
auto& tlist = dir_to_new_files_since_last_sync_[tdn.first];
|
||||
assert(tlist.find(tdn.second) == tlist.end());
|
||||
tlist.insert(tdn.second);
|
||||
auto sdn = TestFSGetDirAndName(s);
|
||||
auto tdn = TestFSGetDirAndName(t);
|
||||
if (dir_to_new_files_since_last_sync_[sdn.first].erase(sdn.second) != 0) {
|
||||
auto& tlist = dir_to_new_files_since_last_sync_[tdn.first];
|
||||
assert(tlist.find(tdn.second) == tlist.end());
|
||||
tlist.insert(tdn.second);
|
||||
}
|
||||
}
|
||||
IOStatus in_s = InjectMetadataWriteError();
|
||||
if (!in_s.ok()) {
|
||||
return in_s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,6 +708,19 @@ IOStatus FaultInjectionTestFS::InjectWriteError(const std::string& file_name) {
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
IOStatus FaultInjectionTestFS::InjectMetadataWriteError() {
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
if (!enable_metadata_write_error_injection_ ||
|
||||
!metadata_write_error_one_in_ ||
|
||||
!write_error_rand_.OneIn(metadata_write_error_one_in_)) {
|
||||
return IOStatus::OK();
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("FaultInjectionTestFS::InjectMetadataWriteError:Injected");
|
||||
return IOStatus::IOError();
|
||||
}
|
||||
|
||||
void FaultInjectionTestFS::PrintFaultBacktrace() {
|
||||
#if defined(OS_LINUX)
|
||||
ErrorContext* ctx =
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "file/filename.h"
|
||||
#include "include/rocksdb/file_system.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/thread_local.h"
|
||||
@@ -174,7 +174,10 @@ class FaultInjectionTestFS : public FileSystemWrapper {
|
||||
filesystem_writable_(false),
|
||||
thread_local_error_(new ThreadLocalPtr(DeleteThreadLocalErrorContext)),
|
||||
enable_write_error_injection_(false),
|
||||
enable_metadata_write_error_injection_(false),
|
||||
write_error_rand_(0),
|
||||
write_error_one_in_(0),
|
||||
metadata_write_error_one_in_(0),
|
||||
ingest_data_corruption_before_write_(false) {}
|
||||
virtual ~FaultInjectionTestFS() { error_.PermitUncheckedError(); }
|
||||
|
||||
@@ -361,10 +364,18 @@ class FaultInjectionTestFS : public FileSystemWrapper {
|
||||
write_error_allowed_types_ = types;
|
||||
}
|
||||
|
||||
void SetRandomMetadataWriteError(int one_in) {
|
||||
MutexLock l(&mutex_);
|
||||
metadata_write_error_one_in_ = one_in;
|
||||
}
|
||||
|
||||
// Inject an write error with randomlized parameter and the predefined
|
||||
// error type. Only the allowed file types will inject the write error
|
||||
IOStatus InjectWriteError(const std::string& file_name);
|
||||
|
||||
// Ingest error to metadata operations.
|
||||
IOStatus InjectMetadataWriteError();
|
||||
|
||||
// Inject an error. For a READ operation, a status of IOError(), a
|
||||
// corruption in the contents of scratch, or truncation of slice
|
||||
// are the types of error with equal probability. For OPEN,
|
||||
@@ -397,6 +408,11 @@ class FaultInjectionTestFS : public FileSystemWrapper {
|
||||
enable_write_error_injection_ = true;
|
||||
}
|
||||
|
||||
void EnableMetadataWriteErrorInjection() {
|
||||
MutexLock l(&mutex_);
|
||||
enable_metadata_write_error_injection_ = true;
|
||||
}
|
||||
|
||||
void DisableWriteErrorInjection() {
|
||||
MutexLock l(&mutex_);
|
||||
enable_write_error_injection_ = false;
|
||||
@@ -410,6 +426,11 @@ class FaultInjectionTestFS : public FileSystemWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
void DisableMetadataWriteErrorInjection() {
|
||||
MutexLock l(&mutex_);
|
||||
enable_metadata_write_error_injection_ = false;
|
||||
}
|
||||
|
||||
// We capture a backtrace every time a fault is injected, for debugging
|
||||
// purposes. This call prints the backtrace to stderr and frees the
|
||||
// saved callstack
|
||||
@@ -456,8 +477,10 @@ class FaultInjectionTestFS : public FileSystemWrapper {
|
||||
|
||||
std::unique_ptr<ThreadLocalPtr> thread_local_error_;
|
||||
bool enable_write_error_injection_;
|
||||
bool enable_metadata_write_error_injection_;
|
||||
Random write_error_rand_;
|
||||
int write_error_one_in_;
|
||||
int metadata_write_error_one_in_;
|
||||
std::vector<FileType> write_error_allowed_types_;
|
||||
bool ingest_data_corruption_before_write_;
|
||||
ChecksumType checksum_handoff_func_tpye_;
|
||||
|
||||
@@ -145,8 +145,10 @@ TEST_F(MemoryTest, MemTableAndTableReadersTotal) {
|
||||
std::vector<uint64_t> usage_by_type;
|
||||
std::vector<std::vector<ColumnFamilyHandle*>> vec_handles;
|
||||
const int kNumDBs = 10;
|
||||
// These key/value sizes ensure each KV has its own memtable. Note that the
|
||||
// minimum write_buffer_size allowed is 64 KB.
|
||||
const int kKeySize = 100;
|
||||
const int kValueSize = 500;
|
||||
const int kValueSize = 1 << 16;
|
||||
Options opt;
|
||||
opt.create_if_missing = true;
|
||||
opt.create_missing_column_families = true;
|
||||
|
||||
@@ -54,7 +54,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate1) {
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
old_options.level_compaction_dynamic_level_bytes = is_dynamic1_;
|
||||
}
|
||||
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
old_options.max_open_files = -1;
|
||||
}
|
||||
old_options.level0_file_num_compaction_trigger = 3;
|
||||
old_options.write_buffer_size = 64 * 1024;
|
||||
old_options.target_file_size_base = 128 * 1024;
|
||||
@@ -92,6 +94,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate1) {
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
new_options.level_compaction_dynamic_level_bytes = is_dynamic2_;
|
||||
}
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
new_options.max_open_files = -1;
|
||||
}
|
||||
new_options.target_file_size_base = 256 * 1024;
|
||||
new_options.num_levels = level2_;
|
||||
new_options.max_bytes_for_level_base = 150 * 1024;
|
||||
@@ -123,6 +128,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate2) {
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
old_options.level_compaction_dynamic_level_bytes = is_dynamic2_;
|
||||
}
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
old_options.max_open_files = -1;
|
||||
}
|
||||
old_options.level0_file_num_compaction_trigger = 3;
|
||||
old_options.write_buffer_size = 64 * 1024;
|
||||
old_options.target_file_size_base = 128 * 1024;
|
||||
@@ -161,6 +169,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate2) {
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
new_options.level_compaction_dynamic_level_bytes = is_dynamic1_;
|
||||
}
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
new_options.max_open_files = -1;
|
||||
}
|
||||
new_options.target_file_size_base = 256 * 1024;
|
||||
new_options.num_levels = level1_;
|
||||
new_options.max_bytes_for_level_base = 150 * 1024;
|
||||
@@ -191,7 +202,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate3) {
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
old_options.level_compaction_dynamic_level_bytes = is_dynamic1_;
|
||||
}
|
||||
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
old_options.max_open_files = -1;
|
||||
}
|
||||
old_options.level0_file_num_compaction_trigger = 3;
|
||||
old_options.write_buffer_size = 64 * 1024;
|
||||
old_options.target_file_size_base = 128 * 1024;
|
||||
@@ -235,6 +248,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate3) {
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
new_options.level_compaction_dynamic_level_bytes = is_dynamic2_;
|
||||
}
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
new_options.max_open_files = -1;
|
||||
}
|
||||
new_options.target_file_size_base = 256 * 1024;
|
||||
new_options.num_levels = level2_;
|
||||
new_options.max_bytes_for_level_base = 150 * 1024;
|
||||
@@ -266,6 +282,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate4) {
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
old_options.level_compaction_dynamic_level_bytes = is_dynamic2_;
|
||||
}
|
||||
if (old_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
old_options.max_open_files = -1;
|
||||
}
|
||||
old_options.level0_file_num_compaction_trigger = 3;
|
||||
old_options.write_buffer_size = 64 * 1024;
|
||||
old_options.target_file_size_base = 128 * 1024;
|
||||
@@ -310,6 +329,9 @@ TEST_P(DBOptionChangeMigrationTests, Migrate4) {
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleLevel) {
|
||||
new_options.level_compaction_dynamic_level_bytes = is_dynamic1_;
|
||||
}
|
||||
if (new_options.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
|
||||
new_options.max_open_files = -1;
|
||||
}
|
||||
new_options.target_file_size_base = 256 * 1024;
|
||||
new_options.num_levels = level1_;
|
||||
new_options.max_bytes_for_level_base = 150 * 1024;
|
||||
|
||||
Reference in new issue
Block a user