Skip to content

Commit

Permalink
Fix user comparator receiving internal key (facebook#4575)
Browse files Browse the repository at this point in the history
Summary:
There was a bug that the user comparator would receive the internal key instead of the user key. The bug was due to RangeMightExistAfterSortedRun expecting user key but receiving internal key when called in GenerateBottommostFiles. The patch augment an existing unit test to reproduce the bug and fixes it.
Pull Request resolved: facebook#4575

Differential Revision: D10500434

Pulled By: maysamyabandeh

fbshipit-source-id: 858346d2fd102cce9e20516d77338c112bdfe366
  • Loading branch information
Maysam Yabandeh authored and ajkr committed Sep 13, 2019
1 parent d8fc97d commit d2feb8c
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 11 deletions.
3 changes: 3 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
* When user uses options.force_consistency_check in RocksDb, instead of crashing the process, we now pass the error back to the users without killing the process.
* Add a new table property, "rocksdb.num.range-deletions", which counts the number of range deletion tombstones in the table.

### Bug Fixes
* Fix the bug where user comparator was sometimes fed with InternalKey instead of the user key. The bug manifests when during GenerateBottommostFiles.

## 5.13.4 (6/12/2018)
### Bug Fixes
* Fix regression bug of Prev() with ReadOptions.iterate_upper_bound.
Expand Down
32 changes: 28 additions & 4 deletions db/db_compaction_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2738,6 +2738,12 @@ TEST_F(DBCompactionTest, SuggestCompactRangeNoTwoLevel0Compactions) {
dbfull()->TEST_WaitForCompact();
}

static std::string ShortKey(int i) {
assert(i < 10000);
char buf[100];
snprintf(buf, sizeof(buf), "key%04d", i);
return std::string(buf);
}

TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
int32_t trivial_move = 0;
Expand All @@ -2750,10 +2756,28 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
[&](void* arg) { non_trivial_move++; });
rocksdb::SyncPoint::GetInstance()->EnableProcessing();

// The key size is guaranteed to be <= 8
class ShortKeyComparator : public Comparator {
int Compare(const rocksdb::Slice& a,
const rocksdb::Slice& b) const override {
assert(a.size() <= 8);
assert(b.size() <= 8);
return BytewiseComparator()->Compare(a, b);
}
const char* Name() const override { return "ShortKeyComparator"; }
void FindShortestSeparator(std::string* start,
const rocksdb::Slice& limit) const override {
return BytewiseComparator()->FindShortestSeparator(start, limit);
}
void FindShortSuccessor(std::string* key) const override {
return BytewiseComparator()->FindShortSuccessor(key);
}
} short_key_cmp;
Options options = CurrentOptions();
options.target_file_size_base = 100000000;
options.write_buffer_size = 100000000;
options.max_subcompactions = max_subcompactions_;
options.comparator = &short_key_cmp;
DestroyAndReopen(options);

int32_t value_size = 10 * 1024; // 10 KB
Expand All @@ -2763,7 +2787,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
// File with keys [ 0 => 99 ]
for (int i = 0; i < 100; i++) {
values.push_back(RandomString(&rnd, value_size));
ASSERT_OK(Put(Key(i), values[i]));
ASSERT_OK(Put(ShortKey(i), values[i]));
}
ASSERT_OK(Flush());

Expand All @@ -2780,7 +2804,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
// File with keys [ 100 => 199 ]
for (int i = 100; i < 200; i++) {
values.push_back(RandomString(&rnd, value_size));
ASSERT_OK(Put(Key(i), values[i]));
ASSERT_OK(Put(ShortKey(i), values[i]));
}
ASSERT_OK(Flush());

Expand All @@ -2798,7 +2822,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
// File with keys [ 200 => 299 ]
for (int i = 200; i < 300; i++) {
values.push_back(RandomString(&rnd, value_size));
ASSERT_OK(Put(Key(i), values[i]));
ASSERT_OK(Put(ShortKey(i), values[i]));
}
ASSERT_OK(Flush());

Expand All @@ -2816,7 +2840,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
ASSERT_EQ(non_trivial_move, 0);

for (int i = 0; i < 300; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
ASSERT_EQ(Get(ShortKey(i)), values[i]);
}

rocksdb::SyncPoint::GetInstance()->DisableProcessing();
Expand Down
10 changes: 6 additions & 4 deletions db/version_set.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1975,7 +1975,9 @@ void VersionStorageInfo::GenerateBottommostFiles() {
} else {
l0_file_idx = -1;
}
if (!RangeMightExistAfterSortedRun(f.smallest_key, f.largest_key,
Slice smallest_user_key = ExtractUserKey(f.smallest_key);
Slice largest_user_key = ExtractUserKey(f.largest_key);
if (!RangeMightExistAfterSortedRun(smallest_user_key, largest_user_key,
static_cast<int>(level),
l0_file_idx)) {
bottommost_files_.emplace_back(static_cast<int>(level),
Expand Down Expand Up @@ -2572,8 +2574,8 @@ uint64_t VersionStorageInfo::EstimateLiveDataSize() const {
}

bool VersionStorageInfo::RangeMightExistAfterSortedRun(
const Slice& smallest_key, const Slice& largest_key, int last_level,
int last_l0_idx) {
const Slice& smallest_user_key, const Slice& largest_user_key,
int last_level, int last_l0_idx) {
assert((last_l0_idx != -1) == (last_level == 0));
// TODO(ajkr): this preserves earlier behavior where we considered an L0 file
// bottommost only if it's the oldest L0 file and there are no files on older
Expand All @@ -2595,7 +2597,7 @@ bool VersionStorageInfo::RangeMightExistAfterSortedRun(
// which overlap with [`smallest_key`, `largest_key`].
if (files_[level].size() > 0 &&
(last_level == 0 ||
OverlapInLevel(level, &smallest_key, &largest_key))) {
OverlapInLevel(level, &smallest_user_key, &largest_user_key))) {
return true;
}
}
Expand Down
6 changes: 3 additions & 3 deletions db/version_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,9 @@ class VersionStorageInfo {
// @param last_level Level after which we check for overlap
// @param last_l0_idx If `last_level == 0`, index of L0 file after which we
// check for overlap; otherwise, must be -1
bool RangeMightExistAfterSortedRun(const Slice& smallest_key,
const Slice& largest_key, int last_level,
int last_l0_idx);
bool RangeMightExistAfterSortedRun(const Slice& smallest_user_key,
const Slice& largest_user_key,
int last_level, int last_l0_idx);

private:
const InternalKeyComparator* internal_comparator_;
Expand Down

0 comments on commit d2feb8c

Please sign in to comment.