Simplify cleanup of dead (refcount == 0) column families

This commit is contained in:
Igor Canadi 2014-04-07 14:21:25 -07:00
parent e48348d196
commit b42ceb9598
4 changed files with 38 additions and 43 deletions

View File

@ -428,6 +428,20 @@ void ColumnFamilySet::Lock() {
void ColumnFamilySet::Unlock() { spin_lock_.clear(std::memory_order_release); }
// REQUIRES: DB mutex held
void ColumnFamilySet::FreeDeadColumnFamilies() {
autovector<ColumnFamilyData*> to_delete;
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
if (cfd->refs_ == 0) {
to_delete.push_back(cfd);
}
}
for (auto cfd : to_delete) {
// this is very rare, so it's not a problem that we do it under a mutex
delete cfd;
}
}
// under a DB mutex
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
auto cfd_iter = column_family_data_.find(cfd->GetID());

View File

@ -91,8 +91,7 @@ struct SuperVersion {
SuperVersion() = default;
~SuperVersion();
SuperVersion* Ref();
// Returns true if this was the last reference and caller should
// call Clenaup() and delete the object
bool Unref();
// call these two methods with db mutex held
@ -133,8 +132,9 @@ class ColumnFamilyData {
void Ref() { ++refs_; }
// will just decrease reference count to 0, but will not delete it. returns
// true if the ref count was decreased to zero and needs to be cleaned up by
// the caller
// true if the ref count was decreased to zero. in that case, it can be
// deleted by the caller immediatelly, or later, by calling
// FreeDeadColumnFamilies()
bool Unref() {
assert(refs_ > 0);
return --refs_ == 0;
@ -343,6 +343,10 @@ class ColumnFamilySet {
void Lock();
void Unlock();
// REQUIRES: DB mutex held
// Don't call while iterating over ColumnFamilySet
void FreeDeadColumnFamilies();
private:
friend class ColumnFamilyData;
// helper function that gets called from cfd destructor

View File

@ -67,23 +67,19 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
if (flush_memtable) {
// flush all dirty data to disk.
autovector<ColumnFamilyData*> to_delete;
Status status;
for (auto cfd : *versions_->GetColumnFamilySet()) {
cfd->Ref();
mutex_.Unlock();
status = FlushMemTable(cfd, FlushOptions());
mutex_.Lock();
if (cfd->Unref()) {
to_delete.push_back(cfd);
}
cfd->Unref();
if (!status.ok()) {
break;
}
}
for (auto cfd : to_delete) {
delete cfd;
}
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
if (!status.ok()) {
mutex_.Unlock();
Log(options_.info_log, "Cannot Flush data %s\n",

View File

@ -404,21 +404,16 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
DBImpl::~DBImpl() {
mutex_.Lock();
if (flush_on_destroy_) {
autovector<ColumnFamilyData*> to_delete;
for (auto cfd : *versions_->GetColumnFamilySet()) {
if (cfd->mem()->GetFirstSequenceNumber() != 0) {
cfd->Ref();
mutex_.Unlock();
FlushMemTable(cfd, FlushOptions());
mutex_.Lock();
if (cfd->Unref()) {
to_delete.push_back(cfd);
}
cfd->Unref();
}
}
for (auto cfd : to_delete) {
delete cfd;
}
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
}
// Wait for background work to finish
@ -1941,7 +1936,6 @@ Status DBImpl::BackgroundFlush(bool* madeProgress,
// flushing one column family reports a failure, we will continue flushing
// other column families. however, call_status will be a failure in that case.
Status call_status;
autovector<ColumnFamilyData*> to_delete;
// refcounting in iteration
for (auto cfd : *versions_->GetColumnFamilySet()) {
cfd->Ref();
@ -1958,13 +1952,9 @@ Status DBImpl::BackgroundFlush(bool* madeProgress,
if (call_status.ok() && !flush_status.ok()) {
call_status = flush_status;
}
if (cfd->Unref()) {
to_delete.push_back(cfd);
}
}
for (auto cfd : to_delete) {
delete cfd;
cfd->Unref();
}
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
return call_status;
}
@ -2105,6 +2095,8 @@ void DBImpl::BackgroundCallCompaction() {
MaybeScheduleLogDBDeployStats();
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
// Previous compaction may have produced too many files in a level,
// So reschedule another compaction if we made progress in the
// last compaction.
@ -2139,7 +2131,6 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress,
}
// FLUSH preempts compaction
autovector<ColumnFamilyData*> to_delete;
Status flush_stat;
for (auto cfd : *versions_->GetColumnFamilySet()) {
while (cfd->imm()->IsFlushPending()) {
@ -2151,9 +2142,7 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress,
cfd->Ref();
flush_stat = FlushMemTableToOutputFile(cfd, madeProgress, deletion_state,
log_buffer);
if (cfd->Unref()) {
to_delete.push_back(cfd);
}
cfd->Unref();
if (!flush_stat.ok()) {
if (is_manual) {
manual_compaction_->status = flush_stat;
@ -2161,18 +2150,9 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress,
manual_compaction_->in_progress = false;
manual_compaction_ = nullptr;
}
break;
return flush_stat;
}
}
if (!flush_stat.ok()) {
break;
}
}
for (auto cfd : to_delete) {
delete cfd;
}
if (!flush_stat.ok()) {
return flush_stat;
}
unique_ptr<Compaction> c;
@ -3840,22 +3820,23 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) {
}
Status status;
autovector<ColumnFamilyData*> to_delete;
// refcounting cfd in iteration
bool dead_cfd = false;
for (auto cfd : *versions_->GetColumnFamilySet()) {
cfd->Ref();
// May temporarily unlock and wait.
status = MakeRoomForWrite(cfd, my_batch == nullptr);
if (cfd->Unref()) {
to_delete.push_back(cfd);
dead_cfd = true;
}
if (!status.ok()) {
break;
}
}
for (auto cfd : to_delete) {
delete cfd;
if (dead_cfd) {
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
}
uint64_t last_sequence = versions_->LastSequence();
Writer* last_writer = &w;
if (status.ok() && my_batch != nullptr) { // nullptr batch is for compactions