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:
parent
a92bd0a183
commit
db7c687523
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user