Free obsolete memtables outside the dbmutex.

Summary:
Large memory allocations and frees are costly and best done outside the
db-mutex. The memtables are already allocated outside the db-mutex but
they were being freed while holding the db-mutex.
This patch frees obsolete memtables outside the db-mutex.

Test Plan:
make check
db_stress

Unit tests pass, I am in the process of running stress tests.

Reviewers: haobo, igor, emayanke

Reviewed By: haobo

CC: reconnect.grayhat, leveldb

Differential Revision: https://reviews.facebook.net/D14319
This commit is contained in:
Dhruba Borthakur 2013-11-25 11:55:36 -08:00
parent 3ce3658411
commit 27bbef1180
4 changed files with 60 additions and 18 deletions

View file

@ -300,6 +300,9 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
} }
DBImpl::~DBImpl() { DBImpl::~DBImpl() {
std::vector<MemTable*> to_delete;
to_delete.reserve(options_.max_write_buffer_number);
// Wait for background work to finish // Wait for background work to finish
if (flush_on_destroy_ && mem_->GetFirstSequenceNumber() != 0) { if (flush_on_destroy_ && mem_->GetFirstSequenceNumber() != 0) {
FlushMemTable(FlushOptions()); FlushMemTable(FlushOptions());
@ -317,8 +320,14 @@ DBImpl::~DBImpl() {
env_->UnlockFile(db_lock_); env_->UnlockFile(db_lock_);
} }
if (mem_ != nullptr) mem_->Unref(); if (mem_ != nullptr) {
imm_.UnrefAll(); delete mem_->Unref();
}
imm_.UnrefAll(&to_delete);
for (MemTable* m: to_delete) {
delete m;
}
LogFlush(options_.info_log); LogFlush(options_.info_log);
} }
@ -954,7 +963,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number,
// file-systems cause the DB::Open() to fail. // file-systems cause the DB::Open() to fail.
break; break;
} }
mem->Unref(); delete mem->Unref();
mem = nullptr; mem = nullptr;
} }
} }
@ -965,7 +974,9 @@ Status DBImpl::RecoverLogFile(uint64_t log_number,
// file-systems cause the DB::Open() to fail. // file-systems cause the DB::Open() to fail.
} }
if (mem != nullptr && !external_table) mem->Unref(); if (mem != nullptr && !external_table) {
delete mem->Unref();
}
return status; return status;
} }
@ -2480,9 +2491,14 @@ struct IterState {
static void CleanupIteratorState(void* arg1, void* arg2) { static void CleanupIteratorState(void* arg1, void* arg2) {
IterState* state = reinterpret_cast<IterState*>(arg1); IterState* state = reinterpret_cast<IterState*>(arg1);
std::vector<MemTable*> to_delete;
to_delete.reserve(state->mem.size());
state->mu->Lock(); state->mu->Lock();
for (unsigned int i = 0; i < state->mem.size(); i++) { for (unsigned int i = 0; i < state->mem.size(); i++) {
state->mem[i]->Unref(); MemTable* m = state->mem[i]->Unref();
if (m != nullptr) {
to_delete.push_back(m);
}
} }
state->version->Unref(); state->version->Unref();
// delete only the sst obsolete files // delete only the sst obsolete files
@ -2491,6 +2507,9 @@ static void CleanupIteratorState(void* arg1, void* arg2) {
state->db->FindObsoleteFiles(deletion_state, false, true); state->db->FindObsoleteFiles(deletion_state, false, true);
state->mu->Unlock(); state->mu->Unlock();
state->db->PurgeObsoleteFiles(deletion_state); state->db->PurgeObsoleteFiles(deletion_state);
// delete obsolete memtables outside the db-mutex
for (MemTable* m : to_delete) delete m;
delete state; delete state;
} }
} // namespace } // namespace
@ -2558,6 +2577,8 @@ Status DBImpl::GetImpl(const ReadOptions& options,
StopWatch sw(env_, options_.statistics.get(), DB_GET); StopWatch sw(env_, options_.statistics.get(), DB_GET);
SequenceNumber snapshot; SequenceNumber snapshot;
std::vector<MemTable*> to_delete;
to_delete.reserve(options_.max_write_buffer_number);
mutex_.Lock(); mutex_.Lock();
if (options.snapshot != nullptr) { if (options.snapshot != nullptr) {
snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_; snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;
@ -2600,11 +2621,15 @@ Status DBImpl::GetImpl(const ReadOptions& options,
have_stat_update && current->UpdateStats(stats)) { have_stat_update && current->UpdateStats(stats)) {
MaybeScheduleFlushOrCompaction(); MaybeScheduleFlushOrCompaction();
} }
mem->Unref(); MemTable* m = mem->Unref();
imm.UnrefAll(); imm.UnrefAll(&to_delete);
current->Unref(); current->Unref();
mutex_.Unlock(); mutex_.Unlock();
// free up all obsolete memtables outside the mutex
delete m;
for (MemTable* v: to_delete) delete v;
LogFlush(options_.info_log); LogFlush(options_.info_log);
// Note, tickers are atomic now - no lock protection needed any more. // Note, tickers are atomic now - no lock protection needed any more.
RecordTick(options_.statistics.get(), NUMBER_KEYS_READ); RecordTick(options_.statistics.get(), NUMBER_KEYS_READ);
@ -2618,6 +2643,9 @@ std::vector<Status> DBImpl::MultiGet(const ReadOptions& options,
StopWatch sw(env_, options_.statistics.get(), DB_MULTIGET); StopWatch sw(env_, options_.statistics.get(), DB_MULTIGET);
SequenceNumber snapshot; SequenceNumber snapshot;
std::vector<MemTable*> to_delete;
to_delete.reserve(options_.max_write_buffer_number);
mutex_.Lock(); mutex_.Lock();
if (options.snapshot != nullptr) { if (options.snapshot != nullptr) {
snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_; snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;
@ -2679,11 +2707,15 @@ std::vector<Status> DBImpl::MultiGet(const ReadOptions& options,
have_stat_update && current->UpdateStats(stats)) { have_stat_update && current->UpdateStats(stats)) {
MaybeScheduleFlushOrCompaction(); MaybeScheduleFlushOrCompaction();
} }
mem->Unref(); MemTable* m = mem->Unref();
imm.UnrefAll(); imm.UnrefAll(&to_delete);
current->Unref(); current->Unref();
mutex_.Unlock(); mutex_.Unlock();
// free up all obsolete memtables outside the mutex
delete m;
for (MemTable* v: to_delete) delete v;
LogFlush(options_.info_log); LogFlush(options_.info_log);
RecordTick(options_.statistics.get(), NUMBER_MULTIGET_CALLS); RecordTick(options_.statistics.get(), NUMBER_MULTIGET_CALLS);
RecordTick(options_.statistics.get(), NUMBER_MULTIGET_KEYS_READ, numKeys); RecordTick(options_.statistics.get(), NUMBER_MULTIGET_KEYS_READ, numKeys);

View file

@ -39,16 +39,20 @@ class MemTable {
int numlevel = 7, int numlevel = 7,
const Options& options = Options()); const Options& options = Options());
~MemTable();
// Increase reference count. // Increase reference count.
void Ref() { ++refs_; } void Ref() { ++refs_; }
// Drop reference count. Delete if no more references exist. // Drop reference count.
void Unref() { // If the refcount goes to zero return this memtable, otherwise return null
MemTable* Unref() {
--refs_; --refs_;
assert(refs_ >= 0); assert(refs_ >= 0);
if (refs_ <= 0) { if (refs_ <= 0) {
delete this; return this;
} }
return nullptr;
} }
// Returns an estimate of the number of bytes of data in use by this // Returns an estimate of the number of bytes of data in use by this
@ -129,7 +133,6 @@ class MemTable {
void MarkImmutable() { table_->MarkReadOnly(); } void MarkImmutable() { table_->MarkReadOnly(); }
private: private:
~MemTable(); // Private since only Unref() should be used to delete it
friend class MemTableIterator; friend class MemTableIterator;
friend class MemTableBackwardIterator; friend class MemTableBackwardIterator;
friend class MemTableList; friend class MemTableList;

View file

@ -28,10 +28,15 @@ void MemTableList::RefAll() {
} }
} }
// Drop reference count on all underling memtables // Drop reference count on all underling memtables. If the
void MemTableList::UnrefAll() { // refcount of an underlying memtable drops to zero, then
// return it in to_delete vector.
void MemTableList::UnrefAll(std::vector<MemTable*>* to_delete) {
for (auto &memtable : memlist_) { for (auto &memtable : memlist_) {
memtable->Unref(); MemTable* m = memtable->Unref();
if (m != nullptr) {
to_delete->push_back(m);
}
} }
} }

View file

@ -44,8 +44,10 @@ class MemTableList {
// Increase reference count on all underling memtables // Increase reference count on all underling memtables
void RefAll(); void RefAll();
// Drop reference count on all underling memtables // Drop reference count on all underling memtables. If the refcount
void UnrefAll(); // on an underlying memtable drops to zero, then return it in
// to_delete vector.
void UnrefAll(std::vector<MemTable*>* to_delete);
// Returns the total number of memtables in the list // Returns the total number of memtables in the list
int size(); int size();