Skip to content

Commit e39f4f6

Browse files
committed
Fix data race #3
Summary: Added requirement that ComputeCompactionScore() be executed in mutex, since it's accessing being_compacted bool, which can be mutated by other threads. Also added more comments about thread safety of FileMetaData, since it was a bit confusing. However, it seems that FileMetaData doesn't have data races (except being_compacted) Test Plan: Ran 100 ConvertCompactionStyle tests with thread sanitizer. On master -- some failures. With this patch -- none. Reviewers: yhchiang, rven, sdong Reviewed By: sdong Subscribers: dhruba, leveldb Differential Revision: https://reviews.facebook.net/D32283
1 parent e63140d commit e39f4f6

7 files changed

+30
-66
lines changed

db/compaction_picker.cc

+2-19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
namespace rocksdb {
2525

26+
namespace {
2627
uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
2728
uint64_t sum = 0;
2829
for (size_t i = 0; i < files.size() && files[i]; i++) {
@@ -31,7 +32,6 @@ uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
3132
return sum;
3233
}
3334

34-
namespace {
3535
// Determine compression type, based on user options, level of the output
3636
// file and whether compression is disabled.
3737
// If enable_compression is false, then compression is always disabled no
@@ -71,19 +71,6 @@ CompactionPicker::CompactionPicker(const ImmutableCFOptions& ioptions,
7171

7272
CompactionPicker::~CompactionPicker() {}
7373

74-
void CompactionPicker::SizeBeingCompacted(std::vector<uint64_t>& sizes) {
75-
for (int level = 0; level < NumberLevels() - 1; level++) {
76-
uint64_t total = 0;
77-
for (auto c : compactions_in_progress_[level]) {
78-
assert(c->level() == level);
79-
for (size_t i = 0; i < c->num_input_files(0); i++) {
80-
total += c->input(0, i)->compensated_file_size;
81-
}
82-
}
83-
sizes[level] = total;
84-
}
85-
}
86-
8774
// Clear all files to indicate that they are not being compacted
8875
// Delete this compaction from the list of running compactions.
8976
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
@@ -763,13 +750,9 @@ Compaction* LevelCompactionPicker::PickCompaction(
763750
// being compacted). Since we just changed compaction score, we recalculate it
764751
// here
765752
{ // this piece of code recomputes compaction score
766-
std::vector<uint64_t> size_being_compacted(NumberLevels() - 1);
767-
SizeBeingCompacted(size_being_compacted);
768-
769753
CompactionOptionsFIFO dummy_compaction_options_fifo;
770754
vstorage->ComputeCompactionScore(mutable_cf_options,
771-
dummy_compaction_options_fifo,
772-
size_being_compacted);
755+
dummy_compaction_options_fifo);
773756
}
774757

775758
return c;

db/compaction_picker.h

-7
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ class CompactionPicker {
9191
// Free up the files that participated in a compaction
9292
void ReleaseCompactionFiles(Compaction* c, Status status);
9393

94-
// Return the total amount of data that is undergoing
95-
// compactions per level
96-
void SizeBeingCompacted(std::vector<uint64_t>& sizes);
97-
9894
// Returns true if any one of the specified files are being compacted
9995
bool FilesInCompaction(const std::vector<FileMetaData*>& files);
10096

@@ -314,7 +310,4 @@ class NullCompactionPicker : public CompactionPicker {
314310
};
315311
#endif // !ROCKSDB_LITE
316312

317-
// Utility function
318-
extern uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files);
319-
320313
} // namespace rocksdb

db/compaction_picker_test.cc

-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ class CompactionPickerTest {
8383
}
8484

8585
void UpdateVersionStorageInfo() {
86-
vstorage_->ComputeCompactionScore(mutable_cf_options_, fifo_options_,
87-
size_being_compacted_);
8886
vstorage_->UpdateFilesBySize();
8987
vstorage_->UpdateNumNonEmptyLevels();
9088
vstorage_->GenerateFileIndexer();

db/version_builder_test.cc

-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ class VersionBuilderTest {
7474
}
7575

7676
void UpdateVersionStorageInfo() {
77-
vstorage_.ComputeCompactionScore(mutable_cf_options_, fifo_options_,
78-
size_being_compacted_);
7977
vstorage_.UpdateFilesBySize();
8078
vstorage_.UpdateNumNonEmptyLevels();
8179
vstorage_.GenerateFileIndexer();

db/version_edit.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ struct FileMetaData {
7676

7777
// File size compensated by deletion entry.
7878
// This is updated in Version::UpdateAccumulatedStats() first time when the
79-
// file is created or loaded. After it is updated, it is immutable.
79+
// file is created or loaded. After it is updated (!= 0), it is immutable.
8080
uint64_t compensated_file_size;
81+
// These values can mutate, but they can only be read or written from
82+
// single-threaded LogAndApply thread
8183
uint64_t num_entries; // the number of entries.
8284
uint64_t num_deletions; // the number of deletion entries.
8385
uint64_t raw_key_size; // total uncompressed key size.

db/version_set.cc

+20-27
Original file line numberDiff line numberDiff line change
@@ -850,12 +850,8 @@ void VersionStorageInfo::GenerateLevelFilesBrief() {
850850
}
851851
}
852852

853-
void Version::PrepareApply(const MutableCFOptions& mutable_cf_options,
854-
std::vector<uint64_t>& size_being_compacted) {
853+
void Version::PrepareApply() {
855854
UpdateAccumulatedStats();
856-
storage_info_.ComputeCompactionScore(
857-
mutable_cf_options, cfd_->ioptions()->compaction_options_fifo,
858-
size_being_compacted);
859855
storage_info_.UpdateFilesBySize();
860856
storage_info_.UpdateNumNonEmptyLevels();
861857
storage_info_.GenerateFileIndexer();
@@ -947,7 +943,9 @@ void VersionStorageInfo::ComputeCompensatedSizes() {
947943
for (int level = 0; level < num_levels_; level++) {
948944
for (auto* file_meta : files_[level]) {
949945
// Here we only compute compensated_file_size for those file_meta
950-
// which compensated_file_size is uninitialized (== 0).
946+
// which compensated_file_size is uninitialized (== 0). This is true only
947+
// for files that have been created right now and no other thread has
948+
// access to them. That's why we can safely mutate compensated_file_size.
951949
if (file_meta->compensated_file_size == 0) {
952950
file_meta->compensated_file_size = file_meta->fd.GetFileSize() +
953951
file_meta->num_deletions * average_value_size *
@@ -966,8 +964,7 @@ int VersionStorageInfo::MaxInputLevel() const {
966964

967965
void VersionStorageInfo::ComputeCompactionScore(
968966
const MutableCFOptions& mutable_cf_options,
969-
const CompactionOptionsFIFO& compaction_options_fifo,
970-
std::vector<uint64_t>& size_being_compacted) {
967+
const CompactionOptionsFIFO& compaction_options_fifo) {
971968
double max_score = 0;
972969
int max_score_level = 0;
973970

@@ -1008,9 +1005,13 @@ void VersionStorageInfo::ComputeCompactionScore(
10081005
}
10091006
} else {
10101007
// Compute the ratio of current size to size limit.
1011-
const uint64_t level_bytes =
1012-
TotalCompensatedFileSize(files_[level]) - size_being_compacted[level];
1013-
score = static_cast<double>(level_bytes) /
1008+
uint64_t level_bytes_no_compacting = 0;
1009+
for (auto f : files_[level]) {
1010+
if (f && f->being_compacted == false) {
1011+
level_bytes_no_compacting += f->compensated_file_size;
1012+
}
1013+
}
1014+
score = static_cast<double>(level_bytes_no_compacting) /
10141015
mutable_cf_options.MaxBytesForLevel(level);
10151016
if (max_score < score) {
10161017
max_score = score;
@@ -1527,6 +1528,11 @@ VersionSet::~VersionSet() {
15271528

15281529
void VersionSet::AppendVersion(ColumnFamilyData* column_family_data,
15291530
Version* v) {
1531+
// compute new compaction score
1532+
v->storage_info()->ComputeCompactionScore(
1533+
*column_family_data->GetLatestMutableCFOptions(),
1534+
column_family_data->ioptions()->compaction_options_fifo);
1535+
15301536
// Mark v finalized
15311537
v->storage_info_.SetFinalized();
15321538

@@ -1637,13 +1643,6 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
16371643
// Unlock during expensive operations. New writes cannot get here
16381644
// because &w is ensuring that all new writes get queued.
16391645
{
1640-
std::vector<uint64_t> size_being_compacted;
1641-
if (!edit->IsColumnFamilyManipulation()) {
1642-
size_being_compacted.resize(v->storage_info()->num_levels() - 1);
1643-
// calculate the amount of data being compacted at every level
1644-
column_family_data->compaction_picker()->SizeBeingCompacted(
1645-
size_being_compacted);
1646-
}
16471646

16481647
mu->Unlock();
16491648

@@ -1674,7 +1673,7 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
16741673

16751674
if (!edit->IsColumnFamilyManipulation()) {
16761675
// This is cpu-heavy operations, which should be called outside mutex.
1677-
v->PrepareApply(mutable_cf_options, size_being_compacted);
1676+
v->PrepareApply();
16781677
}
16791678

16801679
// Write new record to MANIFEST log
@@ -2097,10 +2096,7 @@ Status VersionSet::Recover(
20972096
builder->SaveTo(v->storage_info());
20982097

20992098
// Install recovered version
2100-
std::vector<uint64_t> size_being_compacted(
2101-
v->storage_info()->num_levels() - 1);
2102-
cfd->compaction_picker()->SizeBeingCompacted(size_being_compacted);
2103-
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), size_being_compacted);
2099+
v->PrepareApply();
21042100
AppendVersion(cfd, v);
21052101
}
21062102

@@ -2434,10 +2430,7 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
24342430

24352431
Version* v = new Version(cfd, this, current_version_number_++);
24362432
builder->SaveTo(v->storage_info());
2437-
std::vector<uint64_t> size_being_compacted(
2438-
v->storage_info()->num_levels() - 1);
2439-
cfd->compaction_picker()->SizeBeingCompacted(size_being_compacted);
2440-
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), size_being_compacted);
2433+
v->PrepareApply();
24412434

24422435
printf("--------------- Column family \"%s\" (ID %u) --------------\n",
24432436
cfd->GetName().c_str(), (unsigned int)cfd->GetID());

db/version_set.h

+5-8
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,11 @@ class VersionStorageInfo {
113113

114114
// Updates internal structures that keep track of compaction scores
115115
// We use compaction scores to figure out which compaction to do next
116-
// REQUIRES: If Version is not yet saved to current_, it can be called without
117-
// a lock. Once a version is saved to current_, call only with mutex held
116+
// REQUIRES: db_mutex held!!
118117
// TODO find a better way to pass compaction_options_fifo.
119118
void ComputeCompactionScore(
120119
const MutableCFOptions& mutable_cf_options,
121-
const CompactionOptionsFIFO& compaction_options_fifo,
122-
std::vector<uint64_t>& size_being_compacted);
120+
const CompactionOptionsFIFO& compaction_options_fifo);
123121

124122
// Generate level_files_brief_ from files_
125123
void GenerateLevelFilesBrief();
@@ -365,10 +363,9 @@ class Version {
365363
Status* status, MergeContext* merge_context,
366364
bool* value_found = nullptr);
367365

368-
// Update scores, pre-calculated variables. It needs to be called before
369-
// applying the version to the version set.
370-
void PrepareApply(const MutableCFOptions& mutable_cf_options,
371-
std::vector<uint64_t>& size_being_compacted);
366+
// Loads some stats information from files. Call without mutex held. It needs
367+
// to be called before applying the version to the version set.
368+
void PrepareApply();
372369

373370
// Reference count management (so Versions do not disappear out from
374371
// under live iterators)

0 commit comments

Comments
 (0)