Prevent segfault because SizeUnderCompaction was called without any locks.

Summary:
SizeBeingCompacted was called without any lock protection. This causes
crashes, especially when running db_bench with value_size=128K.
The fix is to compute SizeUnderCompaction while holding the mutex and
passing in these values into the call to Finalize.

(gdb) where
#4  leveldb::VersionSet::SizeBeingCompacted (this=this@entry=0x7f0b490931c0, level=level@entry=4) at db/version_set.cc:1827
#5  0x000000000043a3c8 in leveldb::VersionSet::Finalize (this=this@entry=0x7f0b490931c0, v=v@entry=0x7f0b3b86b480) at db/version_set.cc:1420
#6  0x00000000004418d1 in leveldb::VersionSet::LogAndApply (this=0x7f0b490931c0, edit=0x7f0b3dc8c200, mu=0x7f0b490835b0, new_descriptor_log=<optimized out>) at db/version_set.cc:1016
#7  0x00000000004222b2 in leveldb::DBImpl::InstallCompactionResults (this=this@entry=0x7f0b49083400, compact=compact@entry=0x7f0b2b8330f0) at db/db_impl.cc:1473
#8  0x0000000000426027 in leveldb::DBImpl::DoCompactionWork (this=this@entry=0x7f0b49083400, compact=compact@entry=0x7f0b2b8330f0) at db/db_impl.cc:1757
#9  0x0000000000426690 in leveldb::DBImpl::BackgroundCompaction (this=this@entry=0x7f0b49083400, madeProgress=madeProgress@entry=0x7f0b41bf2d1e, deletion_state=...) at db/db_impl.cc:1268
#10 0x0000000000428f42 in leveldb::DBImpl::BackgroundCall (this=0x7f0b49083400) at db/db_impl.cc:1170
#11 0x000000000045348e in BGThread (this=0x7f0b49023100) at util/env_posix.cc:941
#12 leveldb::(anonymous namespace)::PosixEnv::BGThreadWrapper (arg=0x7f0b49023100) at util/env_posix.cc:874
#13 0x00007f0b4a7cf10d in start_thread (arg=0x7f0b41bf3700) at pthread_create.c:301
#14 0x00007f0b49b4b11d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

Test Plan:
make check

I am running db_bench with a value size of 128K to see if the segfault is fixed.

Reviewers: MarkCallaghan, sheki, emayanke

Reviewed By: sheki

CC: leveldb

Differential Revision: https://reviews.facebook.net/D9279
main
Dhruba Borthakur 12 years ago
parent c04c956b66
commit ebf16f57c9
  1. 36
      db/version_set.cc
  2. 6
      db/version_set.h

@ -1009,11 +1009,15 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu,
// Unlock during expensive MANIFEST log write. New writes cannot get here // Unlock during expensive MANIFEST log write. New writes cannot get here
// because &w is ensuring that all new writes get queued. // because &w is ensuring that all new writes get queued.
{ {
// calculate the amount of data being compacted at every level
std::vector<uint64_t> size_being_compacted(NumberLevels()-1);
SizeBeingCompacted(size_being_compacted);
mu->Unlock(); mu->Unlock();
// The calles to Finalize and UpdateFilesBySize are cpu-heavy // The calls to Finalize and UpdateFilesBySize are cpu-heavy
// and is best called outside the mutex. // and is best called outside the mutex.
Finalize(v); Finalize(v, size_being_compacted);
UpdateFilesBySize(v); UpdateFilesBySize(v);
// Write new record to MANIFEST log // Write new record to MANIFEST log
@ -1226,8 +1230,12 @@ Status VersionSet::Recover() {
if (s.ok()) { if (s.ok()) {
Version* v = new Version(this, current_version_number_++); Version* v = new Version(this, current_version_number_++);
builder.SaveTo(v); builder.SaveTo(v);
// Install recovered version // Install recovered version
Finalize(v); std::vector<uint64_t> size_being_compacted(NumberLevels()-1);
SizeBeingCompacted(size_being_compacted);
Finalize(v, size_being_compacted);
v->offset_manifest_file_ = manifest_file_size; v->offset_manifest_file_ = manifest_file_size;
AppendVersion(v); AppendVersion(v);
manifest_file_number_ = next_file; manifest_file_number_ = next_file;
@ -1350,8 +1358,12 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
if (s.ok()) { if (s.ok()) {
Version* v = new Version(this, 0); Version* v = new Version(this, 0);
builder.SaveTo(v); builder.SaveTo(v);
// Install recovered version // Install recovered version
Finalize(v); std::vector<uint64_t> size_being_compacted(NumberLevels()-1);
SizeBeingCompacted(size_being_compacted);
Finalize(v, size_being_compacted);
AppendVersion(v); AppendVersion(v);
manifest_file_number_ = next_file; manifest_file_number_ = next_file;
next_file_number_ = next_file + 1; next_file_number_ = next_file + 1;
@ -1374,7 +1386,8 @@ void VersionSet::MarkFileNumberUsed(uint64_t number) {
} }
} }
void VersionSet::Finalize(Version* v) { void VersionSet::Finalize(Version* v,
std::vector<uint64_t>& size_being_compacted) {
double max_score = 0; double max_score = 0;
int max_score_level = 0; int max_score_level = 0;
@ -1417,7 +1430,7 @@ void VersionSet::Finalize(Version* v) {
} else { } else {
// Compute the ratio of current size to size limit. // Compute the ratio of current size to size limit.
const uint64_t level_bytes = TotalFileSize(v->files_[level]) - const uint64_t level_bytes = TotalFileSize(v->files_[level]) -
SizeBeingCompacted(level); size_being_compacted[level];
score = static_cast<double>(level_bytes) / MaxBytesForLevel(level); score = static_cast<double>(level_bytes) / MaxBytesForLevel(level);
if (score > 1) { if (score > 1) {
// Log(options_->info_log, "XXX score l%d = %d ", level, (int)score); // Log(options_->info_log, "XXX score l%d = %d ", level, (int)score);
@ -1822,7 +1835,9 @@ void VersionSet::ReleaseCompactionFiles(Compaction* c, Status status) {
} }
// The total size of files that are currently being compacted // The total size of files that are currently being compacted
uint64_t VersionSet::SizeBeingCompacted(int level) { // at at every level upto the penultimate level.
void VersionSet::SizeBeingCompacted(std::vector<uint64_t>& sizes) {
for (int level = 0; level < NumberLevels()-1; level++) {
uint64_t total = 0; uint64_t total = 0;
for (std::set<Compaction*>::iterator it = for (std::set<Compaction*>::iterator it =
compactions_in_progress_[level].begin(); compactions_in_progress_[level].begin();
@ -1834,7 +1849,8 @@ uint64_t VersionSet::SizeBeingCompacted(int level) {
total += c->input(0,i)->file_size; total += c->input(0,i)->file_size;
} }
} }
return total; sizes[level] = total;
}
} }
Compaction* VersionSet::PickCompactionBySize(int level, double score) { Compaction* VersionSet::PickCompactionBySize(int level, double score) {
@ -1916,7 +1932,9 @@ Compaction* VersionSet::PickCompaction() {
// compute the compactions needed. It is better to do it here // compute the compactions needed. It is better to do it here
// and also in LogAndApply(), otherwise the values could be stale. // and also in LogAndApply(), otherwise the values could be stale.
Finalize(current_); std::vector<uint64_t> size_being_compacted(NumberLevels()-1);
current_->vset_->SizeBeingCompacted(size_being_compacted);
Finalize(current_, size_being_compacted);
// We prefer compactions triggered by too much data in a level over // We prefer compactions triggered by too much data in a level over
// the compactions triggered by seeks. // the compactions triggered by seeks.

@ -387,7 +387,7 @@ class VersionSet {
void Init(int num_levels); void Init(int num_levels);
void Finalize(Version* v); void Finalize(Version* v, std::vector<uint64_t>&);
void GetRange(const std::vector<FileMetaData*>& inputs, void GetRange(const std::vector<FileMetaData*>& inputs,
InternalKey* smallest, InternalKey* smallest,
@ -459,8 +459,8 @@ class VersionSet {
void operator=(const VersionSet&); void operator=(const VersionSet&);
// Return the total amount of data that is undergoing // Return the total amount of data that is undergoing
// compactions at this level // compactions per level
uint64_t SizeBeingCompacted(int level); void SizeBeingCompacted(std::vector<uint64_t>&);
// Returns true if any one of the parent files are being compacted // Returns true if any one of the parent files are being compacted
bool ParentRangeInCompaction(const InternalKey* smallest, bool ParentRangeInCompaction(const InternalKey* smallest,

Loading…
Cancel
Save