Restrict RangeDelAggregator's tombstone end-key truncation (#4356)

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: https://github.com/facebook/rocksdb/pull/4356

Differential Revision: D9760891

Pulled By: abhimadan

fbshipit-source-id: 7c20c3885cd919dcd14f291f88fd27aa33defebc
main
Abhishek Madan 6 years ago committed by Facebook Github Bot
parent 3f5282268f
commit c86a22ac43
  1. 23
      db/range_del_aggregator.cc
  2. 15
      db/range_del_aggregator_test.cc

@ -480,18 +480,17 @@ Status RangeDelAggregator::AddTombstones(
} }
} }
if (largest != nullptr) { if (largest != nullptr) {
// This is subtly correct despite the discrepancy between // To safely truncate the range tombstone's end key, it must extend past
// FileMetaData::largest being inclusive while RangeTombstone::end_key_ // the largest key in the sstable (which may have been extended to the
// is exclusive. A tombstone will only extend past the bounds of an // smallest key in the next sstable), and largest must be a tombstone
// sstable if its end-key is the largest key in the table. If that // sentinel key. A range tombstone may straddle two sstables and not be
// occurs, the largest key for the table is set based on the smallest // the tombstone sentinel key in the first sstable if a user-key also
// key in the next table in the level. In that case, largest->user_key() // straddles the sstables (possible if there is a snapshot between the
// is not actually a key in the current table and thus we can use it as // two versions of the user-key), in which case we cannot truncate the
// the exclusive end-key for the tombstone. // range tombstone.
if (icmp_.user_comparator()->Compare( if (icmp_.user_comparator()->Compare(tombstone.end_key_,
tombstone.end_key_, largest->user_key()) > 0) { largest->user_key()) > 0 &&
// The largest key should be a tombstone sentinel key. GetInternalKeySeqno(largest->Encode()) == kMaxSequenceNumber) {
assert(GetInternalKeySeqno(largest->Encode()) == kMaxSequenceNumber);
tombstone.end_key_ = largest->user_key(); tombstone.end_key_ = largest->user_key();
} }
} }

@ -300,6 +300,21 @@ TEST_F(RangeDelAggregatorTest, TruncateTombstones) {
&smallest, &largest); &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 } // namespace rocksdb
int main(int argc, char** argv) { int main(int argc, char** argv) {

Loading…
Cancel
Save