@ -27,6 +27,12 @@ enum Direction {
kReverse ,
} ;
struct AddTombstonesArgs {
const std : : vector < RangeTombstone > tombstones ;
const InternalKey * smallest ;
const InternalKey * largest ;
} ;
static auto bytewise_icmp = InternalKeyComparator ( BytewiseComparator ( ) ) ;
void AddTombstones ( RangeDelAggregator * range_del_agg ,
@ -54,8 +60,7 @@ void VerifyRangeDelIter(
RangeDelIterator * range_del_iter ,
const std : : vector < RangeTombstone > & expected_range_dels ) {
size_t i = 0 ;
for ( ; range_del_iter - > Valid ( ) & & i < expected_range_dels . size ( ) ;
range_del_iter - > Next ( ) , i + + ) {
for ( ; range_del_iter - > Valid ( ) ; range_del_iter - > Next ( ) , i + + ) {
VerifyTombstonesEq ( expected_range_dels [ i ] , range_del_iter - > Tombstone ( ) ) ;
}
ASSERT_EQ ( expected_range_dels . size ( ) , i ) ;
@ -63,22 +68,26 @@ void VerifyRangeDelIter(
}
void VerifyRangeDels (
const std : : vector < RangeTombstone > & range_dels_in ,
const std : : vector < AddTombstonesArgs > & all_args ,
const std : : vector < ExpectedPoint > & expected_points ,
const std : : vector < RangeTombstone > & expected_collapsed_range_dels ,
const InternalKey * smallest = nullptr , const InternalKey * largest = nullptr ,
const InternalKeyComparator & icmp = bytewise_icmp ) {
// Test same result regardless of which order the range deletions are added
// and regardless of collapsed mode.
for ( bool collapsed : { false , true } ) {
for ( Direction dir : { kForward , kReverse } ) {
RangeDelAggregator range_del_agg ( icmp , { } /* snapshots */ , collapsed ) ;
std : : vector < RangeTombstone > all_range_dels ;
std : : vector < RangeTombstone > range_dels = range_dels_in ;
if ( dir = = kReverse ) {
std : : reverse ( range_dels . begin ( ) , range_dels . end ( ) ) ;
for ( const auto & args : all_args ) {
std : : vector < RangeTombstone > range_dels = args . tombstones ;
if ( dir = = kReverse ) {
std : : reverse ( range_dels . begin ( ) , range_dels . end ( ) ) ;
}
all_range_dels . insert ( all_range_dels . end ( ) , range_dels . begin ( ) ,
range_dels . end ( ) ) ;
AddTombstones ( & range_del_agg , range_dels , args . smallest , args . largest ) ;
}
AddTombstones ( & range_del_agg , range_dels , smallest , largest ) ;
auto mode = RangeDelPositioningMode : : kFullScan ;
if ( collapsed ) {
@ -90,38 +99,45 @@ void VerifyRangeDels(
parsed_key . user_key = expected_point . begin ;
parsed_key . sequence = expected_point . seq ;
parsed_key . type = kTypeValue ;
ASSERT_FALSE ( range_del_agg . ShouldDelete ( parsed_key , mode ) ) ;
std : : string ikey ;
AppendInternalKey ( & ikey , parsed_key ) ;
ASSERT_FALSE ( range_del_agg . ShouldDelete ( ikey , mode ) ) ;
if ( parsed_key . sequence > 0 ) {
- - parsed_key . sequence ;
ikey . clear ( ) ;
AppendInternalKey ( & ikey , parsed_key ) ;
if ( expected_point . expectAlive ) {
ASSERT_FALSE ( range_del_agg . ShouldDelete ( parsed_key , mode ) ) ;
ASSERT_FALSE ( range_del_agg . ShouldDelete ( i key, mode ) ) ;
} else {
ASSERT_TRUE ( range_del_agg . ShouldDelete ( parsed_ key, mode ) ) ;
ASSERT_TRUE ( range_del_agg . ShouldDelete ( i key, mode ) ) ;
}
}
}
if ( collapsed ) {
range_dels = expected_collapsed_range_dels ;
VerifyRangeDelIter ( range_del_agg . NewIterator ( ) . get ( ) , range_dels ) ;
} else if ( smallest = = nullptr & & largest = = nullptr ) {
all_range_dels = expected_collapsed_range_dels ;
VerifyRangeDelIter ( range_del_agg . NewIterator ( ) . get ( ) , all_range_dels ) ;
} else if ( all_args . size ( ) = = 1 & & all_args [ 0 ] . smallest = = nullptr & &
all_args [ 0 ] . largest = = nullptr ) {
// Tombstones in an uncollapsed map are presented in start key
// order. Tombstones with the same start key are presented in
// insertion order. We don't handle tombstone truncation here, so the
// verification is only performed if no truncation was requested.
std : : stable_sort ( range_dels . begin ( ) , range_dels . end ( ) ,
std : : stable_sort ( all_ range_dels. begin ( ) , all_ range_dels. end ( ) ,
[ & ] ( const RangeTombstone & a , const RangeTombstone & b ) {
return icmp . user_comparator ( ) - > Compare (
a . start_key_ , b . start_key_ ) < 0 ;
} ) ;
VerifyRangeDelIter ( range_del_agg . NewIterator ( ) . get ( ) , range_dels ) ;
VerifyRangeDelIter ( range_del_agg . NewIterator ( ) . get ( ) , all_ range_dels) ;
}
}
}
RangeDelAggregator range_del_agg ( icmp , { } /* snapshots */ ,
false /* collapse_deletions */ ) ;
AddTombstones ( & range_del_agg , range_dels_in ) ;
for ( const auto & args : all_args ) {
AddTombstones ( & range_del_agg , args . tombstones , args . smallest , args . largest ) ;
}
for ( size_t i = 1 ; i < expected_points . size ( ) ; + + i ) {
bool overlapped = range_del_agg . IsRangeOverlapped (
expected_points [ i - 1 ] . begin , expected_points [ i ] . begin ) ;
@ -138,65 +154,64 @@ void VerifyRangeDels(
TEST_F ( RangeDelAggregatorTest , Empty ) { VerifyRangeDels ( { } , { { " a " , 0 } } , { } ) ; }
TEST_F ( RangeDelAggregatorTest , SameStartAndEnd ) {
VerifyRangeDels ( { { " a " , " a " , 5 } } , { { " " , 0 } , { " a " , 0 } , { " b " , 0 } } , { } ) ;
VerifyRangeDels ( { { { { " a " , " a " , 5 } } } } , { { " " , 0 } , { " a " , 0 } , { " b " , 0 } } , { } ) ;
}
TEST_F ( RangeDelAggregatorTest , Single ) {
VerifyRangeDels ( { { " a " , " b " , 10 } } , { { " " , 0 } , { " a " , 10 } , { " b " , 0 } } ,
VerifyRangeDels ( { { { { " a " , " b " , 10 } } } } , { { " " , 0 } , { " a " , 10 } , { " b " , 0 } } ,
{ { " a " , " b " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlapAboveLeft ) {
VerifyRangeDels ( { { " a " , " c " , 10 } , { " b " , " d " , 5 } } ,
VerifyRangeDels ( { { { { " a " , " c " , 10 } , { " b " , " d " , 5 } } } } ,
{ { " " , 0 } , { " a " , 10 } , { " c " , 5 } , { " d " , 0 } } ,
{ { " a " , " c " , 10 } , { " c " , " d " , 5 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlapAboveRight ) {
VerifyRangeDels ( { { " a " , " c " , 5 } , { " b " , " d " , 10 } } ,
VerifyRangeDels ( { { { { " a " , " c " , 5 } , { " b " , " d " , 10 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 10 } , { " d " , 0 } } ,
{ { " a " , " b " , 5 } , { " b " , " d " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlapAboveMiddle ) {
VerifyRangeDels ( { { " a " , " d " , 5 } , { " b " , " c " , 10 } } ,
VerifyRangeDels ( { { { { " a " , " d " , 5 } , { " b " , " c " , 10 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 10 } , { " c " , 5 } , { " d " , 0 } } ,
{ { " a " , " b " , 5 } , { " b " , " c " , 10 } , { " c " , " d " , 5 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlapAboveMiddleReverse ) {
VerifyRangeDels ( { { " d " , " a " , 5 } , { " c " , " b " , 10 } } ,
VerifyRangeDels ( { { { { " d " , " a " , 5 } , { " c " , " b " , 10 } } } } ,
{ { " z " , 0 } , { " d " , 5 } , { " c " , 10 } , { " b " , 5 } , { " a " , 0 } } ,
{ { " d " , " c " , 5 } , { " c " , " b " , 10 } , { " b " , " a " , 5 } } ,
nullptr /* smallest */ , nullptr /* largest */ ,
InternalKeyComparator ( ReverseBytewiseComparator ( ) ) ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlapFully ) {
VerifyRangeDels ( { { " a " , " d " , 10 } , { " b " , " c " , 5 } } ,
VerifyRangeDels ( { { { { " a " , " d " , 10 } , { " b " , " c " , 5 } } } } ,
{ { " " , 0 } , { " a " , 10 } , { " d " , 0 } } , { { " a " , " d " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlapPoint ) {
VerifyRangeDels ( { { " a " , " b " , 5 } , { " b " , " c " , 10 } } ,
VerifyRangeDels ( { { { { " a " , " b " , 5 } , { " b " , " c " , 10 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 10 } , { " c " , 0 } } ,
{ { " a " , " b " , 5 } , { " b " , " c " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , SameStartKey ) {
VerifyRangeDels ( { { " a " , " c " , 5 } , { " a " , " b " , 10 } } ,
VerifyRangeDels ( { { { { " a " , " c " , 5 } , { " a " , " b " , 10 } } } } ,
{ { " " , 0 } , { " a " , 10 } , { " b " , 5 } , { " c " , 0 } } ,
{ { " a " , " b " , 10 } , { " b " , " c " , 5 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , SameEndKey ) {
VerifyRangeDels ( { { " a " , " d " , 5 } , { " b " , " d " , 10 } } ,
VerifyRangeDels ( { { { { " a " , " d " , 5 } , { " b " , " d " , 10 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 10 } , { " d " , 0 } } ,
{ { " a " , " b " , 5 } , { " b " , " d " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , GapsBetweenRanges ) {
VerifyRangeDels ( { { " a " , " b " , 5 } , { " c " , " d " , 10 } , { " e " , " f " , 15 } } ,
VerifyRangeDels ( { { { { " a " , " b " , 5 } , { " c " , " d " , 10 } , { " e " , " f " , 15 } } } } ,
{ { " " , 0 } ,
{ " a " , 5 } ,
{ " b " , 0 } ,
@ -209,25 +224,25 @@ TEST_F(RangeDelAggregatorTest, GapsBetweenRanges) {
}
TEST_F ( RangeDelAggregatorTest , IdenticalSameSeqNo ) {
VerifyRangeDels ( { { " a " , " b " , 5 } , { " a " , " b " , 5 } } ,
VerifyRangeDels ( { { { { " a " , " b " , 5 } , { " a " , " b " , 5 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 0 } } ,
{ { " a " , " b " , 5 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , ContiguousSameSeqNo ) {
VerifyRangeDels ( { { " a " , " b " , 5 } , { " b " , " c " , 5 } } ,
VerifyRangeDels ( { { { { " a " , " b " , 5 } , { " b " , " c " , 5 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 5 } , { " c " , 0 } } ,
{ { " a " , " c " , 5 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlappingSameSeqNo ) {
VerifyRangeDels ( { { " a " , " c " , 5 } , { " b " , " d " , 5 } } ,
VerifyRangeDels ( { { { { " a " , " c " , 5 } , { " b " , " d " , 5 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 5 } , { " c " , 5 } , { " d " , 0 } } ,
{ { " a " , " d " , 5 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , CoverSameSeqNo ) {
VerifyRangeDels ( { { " a " , " d " , 5 } , { " b " , " c " , 5 } } ,
VerifyRangeDels ( { { { { " a " , " d " , 5 } , { " b " , " c " , 5 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 5 } , { " c " , 5 } , { " d " , 0 } } ,
{ { " a " , " d " , 5 } } ) ;
}
@ -236,27 +251,27 @@ TEST_F(RangeDelAggregatorTest, CoverSameSeqNo) {
// larger one when VerifyRangeDels() runs them in reverse
TEST_F ( RangeDelAggregatorTest , CoverMultipleFromLeft ) {
VerifyRangeDels (
{ { " b " , " d " , 5 } , { " c " , " f " , 10 } , { " e " , " g " , 15 } , { " a " , " f " , 20 } } ,
{ { { { " b " , " d " , 5 } , { " c " , " f " , 10 } , { " e " , " g " , 15 } , { " a " , " f " , 20 } } } } ,
{ { " " , 0 } , { " a " , 20 } , { " f " , 15 } , { " g " , 0 } } ,
{ { " a " , " f " , 20 } , { " f " , " g " , 15 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , CoverMultipleFromRight ) {
VerifyRangeDels (
{ { " b " , " d " , 5 } , { " c " , " f " , 10 } , { " e " , " g " , 15 } , { " c " , " h " , 20 } } ,
{ { { { " b " , " d " , 5 } , { " c " , " f " , 10 } , { " e " , " g " , 15 } , { " c " , " h " , 20 } } } } ,
{ { " " , 0 } , { " b " , 5 } , { " c " , 20 } , { " h " , 0 } } ,
{ { " b " , " c " , 5 } , { " c " , " h " , 20 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , CoverMultipleFully ) {
VerifyRangeDels (
{ { " b " , " d " , 5 } , { " c " , " f " , 10 } , { " e " , " g " , 15 } , { " a " , " h " , 20 } } ,
{ { { { " b " , " d " , 5 } , { " c " , " f " , 10 } , { " e " , " g " , 15 } , { " a " , " h " , 20 } } } } ,
{ { " " , 0 } , { " a " , 20 } , { " h " , 0 } } , { { " a " , " h " , 20 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , AlternateMultipleAboveBelow ) {
VerifyRangeDels (
{ { " b " , " d " , 15 } , { " c " , " f " , 10 } , { " e " , " g " , 20 } , { " a " , " h " , 5 } } ,
{ { { { " b " , " d " , 15 } , { " c " , " f " , 10 } , { " e " , " g " , 20 } , { " a " , " h " , 5 } } } } ,
{ { " " , 0 } , { " a " , 5 } , { " b " , 15 } , { " d " , 10 } , { " e " , 20 } , { " g " , 5 } , { " h " , 0 } } ,
{ { " a " , " b " , 5 } ,
{ " b " , " d " , 15 } ,
@ -321,31 +336,132 @@ TEST_F(RangeDelAggregatorTest, MergingIteratorSeek) {
}
TEST_F ( RangeDelAggregatorTest , TruncateTombstones ) {
const InternalKey smallest ( " b " , 1 , kTypeRangeDeletion ) ;
const InternalKey smallest ( " b " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest ( " e " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
VerifyRangeDels (
{ { " a " , " c " , 10 } , { " d " , " f " , 10 } } ,
{ { { { " a " , " c " , 10 } , { " d " , " f " , 10 } } , & smallest , & largest } } ,
{ { " a " , 10 , true } , // truncated
{ " b " , 10 , false } , // not truncated
{ " d " , 10 , false } , // not truncated
{ " e " , 10 , true } } , // truncated
{ { " b " , " c " , 10 } , { " d " , " e " , 10 } } ,
& smallest , & largest ) ;
{ { " b " , " c " , 10 } , { " d " , " e " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlappingLargestKeyTruncateTombstones ) {
const InternalKey smallest ( " b " , 1 , kTypeRangeDeletion ) ;
TEST_F ( RangeDelAggregatorTest , OverlappingLargestKeyTruncateBelow Tombstone ) {
const InternalKey smallest ( " b " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest (
" e " , 3 , // could happen if "e" is in consecutive sstables
kTypeValue ) ;
VerifyRangeDels (
{ { " a " , " c " , 10 } , { " d " , " f " , 10 } } ,
{ { { { " a " , " c " , 10 } , { " d " , " f " , 10 } } , & smallest , & largest } } ,
{ { " a " , 10 , true } , // truncated
{ " b " , 10 , false } , // not truncated
{ " d " , 10 , false } , // not truncated
{ " e " , 10 , false } , // not truncated
{ " e " , 2 , true } } , // truncated here
{ { " b " , " c " , 10 } , { " d " , " e " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlappingLargestKeyTruncateAboveTombstone ) {
const InternalKey smallest ( " b " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest (
" e " , 15 , // could happen if "e" is in consecutive sstables
kTypeValue ) ;
VerifyRangeDels (
{ { { { " a " , " c " , 10 } , { " d " , " f " , 10 } } , & smallest , & largest } } ,
{ { " a " , 10 , true } , // truncated
{ " b " , 10 , false } , // not truncated
{ " d " , 10 , false } , // not truncated
{ " e " , kMaxSequenceNumber , true } } , // truncated
{ { " b " , " c " , 10 } , { " d " , " e " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlappingSmallestKeyTruncateBelowTombstone ) {
const InternalKey smallest ( " b " , 5 , kTypeValue ) ;
const InternalKey largest ( " e " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
VerifyRangeDels (
{ { { { " a " , " c " , 10 } , { " d " , " f " , 10 } } , & smallest , & largest } } ,
{ { " a " , 10 , true } , // truncated
{ " b " , 10 , true } , // truncated
{ " b " , 6 , false } , // not truncated; start boundary moved
{ " d " , 10 , false } , // not truncated
{ " e " , kMaxSequenceNumber , true } } , // truncated
{ { " b " , " c " , 10 } , { " d " , " e " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlappingSmallestKeyTruncateAboveTombstone ) {
const InternalKey smallest ( " b " , 15 , kTypeValue ) ;
const InternalKey largest ( " e " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
VerifyRangeDels (
{ { { { " a " , " c " , 10 } , { " d " , " f " , 10 } } , & smallest , & largest } } ,
{ { " a " , 10 , true } , // truncated
{ " b " , 15 , 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 ) ;
{ " e " , kMaxSequenceNumber , true } } , // truncated
{ { " b " , " c " , 10 } , { " d " , " e " , 10 } } ) ;
}
TEST_F ( RangeDelAggregatorTest , OverlappingBoundaryGapAboveTombstone ) {
const InternalKey smallest1 ( " b " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest1 ( " c " , 20 , kTypeValue ) ;
const InternalKey smallest2 ( " c " , 10 , kTypeValue ) ;
const InternalKey largest2 ( " e " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
VerifyRangeDels (
{ { { { " b " , " d " , 5 } } , & smallest1 , & largest1 } ,
{ { { " b " , " d " , 5 } } , & smallest2 , & largest2 } } ,
{ { " b " , 5 , false } , // not truncated
{ " c " , 5 , false } } , // not truncated
{ { " b " , " c " , 5 } , { " c " , " d " , 5 } } ) ; // not collapsed due to boundaries
}
TEST_F ( RangeDelAggregatorTest , OverlappingBoundaryGapBelowTombstone ) {
const InternalKey smallest1 ( " b " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest1 ( " c " , 20 , kTypeValue ) ;
const InternalKey smallest2 ( " c " , 10 , kTypeValue ) ;
const InternalKey largest2 ( " e " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
VerifyRangeDels (
{ { { { " b " , " d " , 30 } } , & smallest1 , & largest1 } ,
{ { { " b " , " d " , 30 } } , & smallest2 , & largest2 } } ,
{ { " b " , 30 , false } , // not truncated
{ " c " , 30 , false } , // not truncated
{ " c " , 19 , true } , // truncated here (keys in this range should not exist)
{ " c " , 11 , false } } , // not truncated again
{ { " b " , " c " , 30 } , { " c " , " d " , 30 } } ) ; // not collapsed due to boundaries
}
TEST_F ( RangeDelAggregatorTest , OverlappingBoundaryGapContainsTombstone ) {
const InternalKey smallest1 ( " b " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest1 ( " c " , 20 , kTypeValue ) ;
const InternalKey smallest2 ( " c " , 10 , kTypeValue ) ;
const InternalKey largest2 ( " e " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
VerifyRangeDels (
{ { { { " b " , " d " , 15 } } , & smallest1 , & largest1 } ,
{ { { " b " , " d " , 15 } } , & smallest2 , & largest2 } } ,
{ { " b " , 15 , false } , // not truncated
{ " c " , 15 , true } , // truncated (keys in this range should not exist)
{ " c " , 11 , false } } , // not truncated here
{ { " b " , " c " , 15 } , { " c " , " d " , 15 } } ) ; // not collapsed due to boundaries
}
TEST_F ( RangeDelAggregatorTest , FileCoversOneKeyAndTombstoneAbove ) {
const InternalKey smallest ( " a " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest ( " a " , 20 , kTypeValue ) ;
VerifyRangeDels (
{ { { { " a " , " b " , 35 } } , & smallest , & largest } } ,
{ { " a " , 40 , true } , // not truncated
{ " a " , 35 , false } } , // not truncated
{ { " a " , " a " , 35 } } ) ; // empty tombstone but can't occur during a compaction
}
TEST_F ( RangeDelAggregatorTest , FileCoversOneKeyAndTombstoneBelow ) {
const InternalKey smallest ( " a " , kMaxSequenceNumber , kTypeRangeDeletion ) ;
const InternalKey largest ( " a " , 20 , kTypeValue ) ;
VerifyRangeDels (
{ { { { " a " , " b " , 15 } } , & smallest , & largest } } ,
{ { " a " , 20 , true } , // truncated here
{ " a " , 15 , true } } , // truncated
{ { " a " , " a " , 15 } } ) ; // empty tombstone but can't occur during a compaction
}
} // namespace rocksdb