Minor Compaction
flush라 부르며 메모리에 있는 데이터(imm memtable)을 디스크(level 0)로 내려주는 작업을 합니다.
Overall Minor Compaction Code Flow

전체적인 과정
MaybeScheduleCompaction에서는 Minor Compaction 작업이 필요한지 체크하고 필요하면BackgroundCall을 호출합니다.BackgroundCall에서BackgroundCompaction을 호출하며 imm memtable이 있는지 확인합니다.CompactMemTable는WriteLevel0Table함수를 호출하여Minor Comapction작업을 실행합니다.
MaybeScheduleCompaction & BackgroundCompaction
이두 함수 부분은 Major compaction에서 설명하여 여기서는 생략하도록 하겠습니다.
CompactMemTable
본격적인 Minor Compaction 이 진행됩니다.
void DBImpl::CompactMemTable() {
mutex_.AssertHeld();
assert(imm_ != nullptr);
// Save the contents of the memtable as a new Table
VersionEdit edit;
Version* base = versions_->current();
base->Ref();
//(TeamCompaction)1.Main point : call the `WriteLevel0Table` function for mior compaction
Status s = WriteLevel0Table(imm_, &edit, base);
base->Unref();
//(TeamCompaction)2. Check if WriteLevel0Table was executed successfully
if (s.ok() && shutting_down_.load(std::memory_order_acquire)) {
s = Status::IOError("Deleting DB during memtable compaction");
}
//(TeamCompaction)3.If mior compaction is successful
// Replace immutable memtable with the generated Table
if (s.ok()) {
edit.SetPrevLogNumber(0);
edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed
s = versions_->LogAndApply(&edit, &mutex_);
}
if (s.ok()) {
// Commit to the new state
imm_->Unref();
imm_ = nullptr;
has_imm_.store(false, std::memory_order_release);
RemoveObsoleteFiles();
} else {
RecordBackgroundError(s);
}
}
Minor Compaction을 실행하기 위해WriteLevel0Table함수를 호출합니다.인자로 현재Minor Compaction을 실행할 imm memtable 과 현재 version을 줍니다.- 만약
WriteLevel0Table함수 실행 중에 오류가 발생 하였으면 Error 메시지 전송합니다. - 성공적으로
Minor Compaction을 실행하였다면 imm memtbale을 rellease을 해줍니다.
WriteLevel0Table
imm memtable 을 디스크로 내려주는 작업을 해줍니다.
Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit,
Version* base) {
mutex_.AssertHeld();
const uint64_t start_micros = env_->NowMicros();
FileMetaData meta;
//(TeamCompaction)1. Organizes imm memtable's meta information.
meta.number = versions_->NewFileNumber();
pending_outputs_.insert(meta.number);
Iterator* iter = mem->NewIterator();
Log(options_.info_log, "Level-0 table #%llu: started",
(unsigned long long)meta.number);
Status s;
{
mutex_.Unlock();
//(TeamCompaction)2. Change imm memtable to SST.
s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta);
mutex_.Lock();
}
Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s",
(unsigned long long)meta.number, (unsigned long long)meta.file_size,
s.ToString().c_str());
delete iter;
pending_outputs_.erase(meta.number);
// Note that if file_size is zero, the file has been deleted and
// should not be added to the manifest.
//(TeamCompaction)3. update the version
int level = 0;
if (s.ok() && meta.file_size > 0) {
const Slice min_user_key = meta.smallest.user_key();
const Slice max_user_key = meta.largest.user_key();
if (base != nullptr) {
level = base->PickLevelForMemTableOutput(min_user_key, max_user_key);
}
edit->AddFile(level, meta.number, meta.file_size, meta.smallest,
meta.largest);
}
CompactionStats stats;
stats.micros = env_->NowMicros() - start_micros;
stats.bytes_written = meta.file_size;
stats_[level].Add(stats);
return s;
}
- 첫번째로 imm memtable의 정보를 정리합니다.
BuildTable함수로 imm memtbale 정보로 SST를 만듭니다.- 성공적으로 SST를 생성하게 되었다면 Version에 이 정보를 업데이트 해줍니다.
추가 설명
Trivial move
Trivial move이란 Minor Compaction의 일종으로 새로 생성되는 SST가 각 level의 SST의 Key가 겹치지 않는다면 바로 level 2로 내려주는 작업을 합니다.
void DBImpl::BackgroundCompaction() {
// ...생략
Status status;
if (c == nullptr) {
// Nothing to do
} else if (!is_manual && c->IsTrivialMove()) {
// 1. Move file to next level
assert(c->num_input_files(0) == 1);
FileMetaData* f = c->input(0, 0);
c->edit()->RemoveFile(c->level(), f->number);
c->edit()->AddFile(c->level() + 1, f->number, f->file_size, f->smallest,
f->largest);
status = versions_->LogAndApply(c->edit(), &mutex_);
if (!status.ok()) {
RecordBackgroundError(status);
}
VersionSet::LevelSummaryStorage tmp;
Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n",
static_cast<unsigned long long>(f->number), c->level() + 1,
static_cast<unsigned long long>(f->file_size),
status.ToString().c_str(), versions_->LevelSummary(&tmp));
}
// ...생략
}
bool Compaction::IsTrivialMove() const {
const VersionSet* vset = input_version_->vset_;
// Avoid a move if there is lots of overlapping grandparent data.
// Otherwise, the move could create a parent file that will require
// a very expensive merge later on.
return (num_input_files(0) == 1 && num_input_files(1) == 0 &&
TotalFileSize(grandparents_) <=
MaxGrandParentOverlapBytes(vset->options_));
}
is_manual && c->IsTrivialMove()이 true라면 Trivial move 을 실행합니다.