Fix a data race related to memtable trimming (#6187)

Summary:
https://github.com/facebook/rocksdb/pull/6177 introduced a data race
involving `MemTableList::InstallNewVersion` and `MemTableList::NumFlushed`.
The patch fixes this by caching whether the current version has any
memtable history (i.e. flushed memtables that are kept around for
transaction conflict checking) in an `std::atomic<bool>` member called
`current_has_history_`, similarly to how `current_memory_usage_excluding_last_`
is handled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6187

Test Plan:
```
make clean
COMPILE_WITH_TSAN=1 make db_test -j24
./db_test
```

Differential Revision: D19084059

Pulled By: ltamasi

fbshipit-source-id: 327a5af9700fb7102baea2cc8903c085f69543b9
This commit is contained in:
Levi Tamasi 2019-12-16 13:13:42 -08:00 committed by Facebook Github Bot
parent a92bd0a183
commit db7c687523
3 changed files with 36 additions and 15 deletions

View File

@ -503,7 +503,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
cfd->GetName().c_str(), m->file_number_, mem_id); cfd->GetName().c_str(), m->file_number_, mem_id);
assert(m->file_number_ > 0); assert(m->file_number_ > 0);
current_->Remove(m, to_delete); current_->Remove(m, to_delete);
UpdateMemoryUsageExcludingLast(); UpdateCachedValuesFromMemTableListVersion();
ResetTrimHistoryNeeded(); ResetTrimHistoryNeeded();
++mem_id; ++mem_id;
} }
@ -544,14 +544,14 @@ void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete) {
if (num_flush_not_started_ == 1) { if (num_flush_not_started_ == 1) {
imm_flush_needed.store(true, std::memory_order_release); imm_flush_needed.store(true, std::memory_order_release);
} }
UpdateMemoryUsageExcludingLast(); UpdateCachedValuesFromMemTableListVersion();
ResetTrimHistoryNeeded(); ResetTrimHistoryNeeded();
} }
void MemTableList::TrimHistory(autovector<MemTable*>* to_delete, size_t usage) { void MemTableList::TrimHistory(autovector<MemTable*>* to_delete, size_t usage) {
InstallNewVersion(); InstallNewVersion();
current_->TrimHistory(to_delete, usage); current_->TrimHistory(to_delete, usage);
UpdateMemoryUsageExcludingLast(); UpdateCachedValuesFromMemTableListVersion();
ResetTrimHistoryNeeded(); ResetTrimHistoryNeeded();
} }
@ -567,17 +567,24 @@ size_t MemTableList::ApproximateUnflushedMemTablesMemoryUsage() {
size_t MemTableList::ApproximateMemoryUsage() { return current_memory_usage_; } size_t MemTableList::ApproximateMemoryUsage() { return current_memory_usage_; }
size_t MemTableList::ApproximateMemoryUsageExcludingLast() const { size_t MemTableList::ApproximateMemoryUsageExcludingLast() const {
size_t usage = const size_t usage =
current_memory_usage_excluding_last_.load(std::memory_order_relaxed); current_memory_usage_excluding_last_.load(std::memory_order_relaxed);
return usage; return usage;
} }
// Update current_memory_usage_excluding_last_, need to call whenever state bool MemTableList::HasHistory() const {
// changes for MemtableListVersion (whenever InstallNewVersion() is called) const bool has_history = current_has_history_.load(std::memory_order_relaxed);
void MemTableList::UpdateMemoryUsageExcludingLast() { return has_history;
size_t total_memtable_size = current_->ApproximateMemoryUsageExcludingLast(); }
void MemTableList::UpdateCachedValuesFromMemTableListVersion() {
const size_t total_memtable_size =
current_->ApproximateMemoryUsageExcludingLast();
current_memory_usage_excluding_last_.store(total_memtable_size, current_memory_usage_excluding_last_.store(total_memtable_size,
std::memory_order_relaxed); std::memory_order_relaxed);
const bool has_history = current_->HasHistory();
current_has_history_.store(has_history, std::memory_order_relaxed);
} }
uint64_t MemTableList::ApproximateOldestKeyTime() const { uint64_t MemTableList::ApproximateOldestKeyTime() const {
@ -707,7 +714,7 @@ Status InstallMemtableAtomicFlushResults(
cfds[i]->GetName().c_str(), m->GetFileNumber(), cfds[i]->GetName().c_str(), m->GetFileNumber(),
mem_id); mem_id);
imm->current_->Remove(m, to_delete); imm->current_->Remove(m, to_delete);
imm->UpdateMemoryUsageExcludingLast(); imm->UpdateCachedValuesFromMemTableListVersion();
imm->ResetTrimHistoryNeeded(); imm->ResetTrimHistoryNeeded();
} }
} }
@ -757,7 +764,7 @@ void MemTableList::RemoveOldMemTables(uint64_t log_number,
} }
} }
UpdateMemoryUsageExcludingLast(); UpdateCachedValuesFromMemTableListVersion();
ResetTrimHistoryNeeded(); ResetTrimHistoryNeeded();
} }

View File

@ -164,6 +164,10 @@ class MemTableListVersion {
// memory usage above or equal to max_write_buffer_size_to_maintain_ // memory usage above or equal to max_write_buffer_size_to_maintain_
size_t ApproximateMemoryUsageExcludingLast() const; size_t ApproximateMemoryUsageExcludingLast() const;
// Whether this version contains flushed memtables that are only kept around
// for transaction conflict checking.
bool HasHistory() const { return !memlist_history_.empty(); }
bool MemtableLimitExceeded(size_t usage); bool MemtableLimitExceeded(size_t usage);
// Immutable MemTables that have not yet been flushed. // Immutable MemTables that have not yet been flushed.
@ -211,7 +215,8 @@ class MemTableList {
commit_in_progress_(false), commit_in_progress_(false),
flush_requested_(false), flush_requested_(false),
current_memory_usage_(0), current_memory_usage_(0),
current_memory_usage_excluding_last_(0) { current_memory_usage_excluding_last_(0),
current_has_history_(false) {
current_->Ref(); current_->Ref();
} }
@ -266,11 +271,16 @@ class MemTableList {
// Returns an estimate of the number of bytes of data in use. // Returns an estimate of the number of bytes of data in use.
size_t ApproximateMemoryUsage(); size_t ApproximateMemoryUsage();
// Returns the cached current_memory_usage_excluding_last_ value // Returns the cached current_memory_usage_excluding_last_ value.
size_t ApproximateMemoryUsageExcludingLast() const; size_t ApproximateMemoryUsageExcludingLast() const;
// Update current_memory_usage_excluding_last_ from MemtableListVersion // Returns the cached current_has_history_ value.
void UpdateMemoryUsageExcludingLast(); bool HasHistory() const;
// Updates current_memory_usage_excluding_last_ and current_has_history_
// from MemTableListVersion. Must be called whenever InstallNewVersion is
// called.
void UpdateCachedValuesFromMemTableListVersion();
// `usage` is the current size of the mutable Memtable. When // `usage` is the current size of the mutable Memtable. When
// max_write_buffer_size_to_maintain is used, total size of mutable and // max_write_buffer_size_to_maintain is used, total size of mutable and
@ -388,7 +398,11 @@ class MemTableList {
// The current memory usage. // The current memory usage.
size_t current_memory_usage_; size_t current_memory_usage_;
// Cached value of current_->ApproximateMemoryUsageExcludingLast().
std::atomic<size_t> current_memory_usage_excluding_last_; std::atomic<size_t> current_memory_usage_excluding_last_;
// Cached value of current_->HasHistory().
std::atomic<bool> current_has_history_;
}; };
// Installs memtable atomic flush results. // Installs memtable atomic flush results.

View File

@ -1795,7 +1795,7 @@ class MemTableInserter : public WriteBatch::Handler {
MemTableList* const imm = cfd->imm(); MemTableList* const imm = cfd->imm();
assert(imm); assert(imm);
if (imm->NumFlushed() > 0) { if (imm->HasHistory()) {
const MemTable* const mem = cfd->mem(); const MemTable* const mem = cfd->mem();
assert(mem); assert(mem);