Skip to content

Commit

Permalink
Restrict RangeDelAggregator's tombstone end-key truncation (#4356)
Browse files Browse the repository at this point in the history
Summary:
`RangeDelAggregator::AddTombstones` contained an assertion which stated that, if a range tombstone extended past the largest key in the sstable, then `FileMetaData::largest` must have a sentinel sequence number of `kMaxSequenceNumber`, which implies that the tombstone's end key is safe to truncate. However, `largest` will not be a sentinel key when the next sstable in the level's smallest key is equal to the current sstable's largest key, which caused the assertion to fail.

The assertion must hold for the truncation to be safe, so it has been moved to an additional check on end-key truncation.
Pull Request resolved: #4356

Differential Revision: D9760891

Pulled By: abhimadan

fbshipit-source-id: 7c20c3885cd919dcd14f291f88fd27aa33defebc
  • Loading branch information
abhimadan authored and facebook-github-bot committed Sep 11, 2018
1 parent 3f52822 commit c86a22a
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 12 deletions.
23 changes: 11 additions & 12 deletions db/range_del_aggregator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -480,18 +480,17 @@ Status RangeDelAggregator::AddTombstones(
}
}
if (largest != nullptr) {
// This is subtly correct despite the discrepancy between
// FileMetaData::largest being inclusive while RangeTombstone::end_key_
// is exclusive. A tombstone will only extend past the bounds of an
// sstable if its end-key is the largest key in the table. If that
// occurs, the largest key for the table is set based on the smallest
// key in the next table in the level. In that case, largest->user_key()
// is not actually a key in the current table and thus we can use it as
// the exclusive end-key for the tombstone.
if (icmp_.user_comparator()->Compare(
tombstone.end_key_, largest->user_key()) > 0) {
// The largest key should be a tombstone sentinel key.
assert(GetInternalKeySeqno(largest->Encode()) == kMaxSequenceNumber);
// To safely truncate the range tombstone's end key, it must extend past
// the largest key in the sstable (which may have been extended to the
// smallest key in the next sstable), and largest must be a tombstone
// sentinel key. A range tombstone may straddle two sstables and not be
// the tombstone sentinel key in the first sstable if a user-key also
// straddles the sstables (possible if there is a snapshot between the
// two versions of the user-key), in which case we cannot truncate the
// range tombstone.
if (icmp_.user_comparator()->Compare(tombstone.end_key_,
largest->user_key()) > 0 &&
GetInternalKeySeqno(largest->Encode()) == kMaxSequenceNumber) {
tombstone.end_key_ = largest->user_key();
}
}
Expand Down
15 changes: 15 additions & 0 deletions db/range_del_aggregator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,21 @@ TEST_F(RangeDelAggregatorTest, TruncateTombstones) {
&smallest, &largest);
}

TEST_F(RangeDelAggregatorTest, OverlappingLargestKeyTruncateTombstones) {
const InternalKey smallest("b", 1, kTypeRangeDeletion);
const InternalKey largest(
"e", 3, // could happen if "e" is in consecutive sstables
kTypeValue);
VerifyRangeDels(
{{"a", "c", 10}, {"d", "f", 10}},
{{"a", 10, true}, // truncated
{"b", 10, false}, // not truncated
{"d", 10, false}, // not truncated
{"e", 10, false}}, // not truncated
{{"b", "c", 10}, {"d", "f", 10}},
&smallest, &largest);
}

} // namespace rocksdb

int main(int argc, char** argv) {
Expand Down

0 comments on commit c86a22a

Please sign in to comment.