Remove v1 RangeDelAggregator (#4778)
Summary: Now that v2 is fully functional, the v1 aggregator is removed. The v2 aggregator has been renamed. Pull Request resolved: https://github.com/facebook/rocksdb/pull/4778 Differential Revision: D13495930 Pulled By: abhimadan fbshipit-source-id: 9d69500a60a283e79b6c4fa938fc68a8aa4d40d6main
parent
311cd8cf2f
commit
81b6b09f6b
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,492 +0,0 @@ |
|||||||
// Copyright (c) 2018-present, Facebook, Inc. All rights reserved.
|
|
||||||
// This source code is licensed under both the GPLv2 (found in the
|
|
||||||
// COPYING file in the root directory) and Apache 2.0 License
|
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
|
||||||
|
|
||||||
#include "db/range_del_aggregator_v2.h" |
|
||||||
|
|
||||||
#include "db/compaction_iteration_stats.h" |
|
||||||
#include "db/dbformat.h" |
|
||||||
#include "db/pinned_iterators_manager.h" |
|
||||||
#include "db/range_del_aggregator.h" |
|
||||||
#include "db/range_tombstone_fragmenter.h" |
|
||||||
#include "db/version_edit.h" |
|
||||||
#include "include/rocksdb/comparator.h" |
|
||||||
#include "include/rocksdb/types.h" |
|
||||||
#include "table/internal_iterator.h" |
|
||||||
#include "table/scoped_arena_iterator.h" |
|
||||||
#include "table/table_builder.h" |
|
||||||
#include "util/heap.h" |
|
||||||
#include "util/kv_map.h" |
|
||||||
#include "util/vector_iterator.h" |
|
||||||
|
|
||||||
namespace rocksdb { |
|
||||||
|
|
||||||
TruncatedRangeDelIterator::TruncatedRangeDelIterator( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> iter, |
|
||||||
const InternalKeyComparator* icmp, const InternalKey* smallest, |
|
||||||
const InternalKey* largest) |
|
||||||
: iter_(std::move(iter)), |
|
||||||
icmp_(icmp), |
|
||||||
smallest_ikey_(smallest), |
|
||||||
largest_ikey_(largest) { |
|
||||||
if (smallest != nullptr) { |
|
||||||
pinned_bounds_.emplace_back(); |
|
||||||
auto& parsed_smallest = pinned_bounds_.back(); |
|
||||||
if (!ParseInternalKey(smallest->Encode(), &parsed_smallest)) { |
|
||||||
assert(false); |
|
||||||
} |
|
||||||
smallest_ = &parsed_smallest; |
|
||||||
} |
|
||||||
if (largest != nullptr) { |
|
||||||
pinned_bounds_.emplace_back(); |
|
||||||
auto& parsed_largest = pinned_bounds_.back(); |
|
||||||
if (!ParseInternalKey(largest->Encode(), &parsed_largest)) { |
|
||||||
assert(false); |
|
||||||
} |
|
||||||
if (parsed_largest.type == kTypeRangeDeletion && |
|
||||||
parsed_largest.sequence == kMaxSequenceNumber) { |
|
||||||
// The file boundary has been artificially extended by a range tombstone.
|
|
||||||
// We do not need to adjust largest to properly truncate range
|
|
||||||
// tombstones that extend past the boundary.
|
|
||||||
} else if (parsed_largest.sequence == 0) { |
|
||||||
// The largest key in the sstable has a sequence number of 0. Since we
|
|
||||||
// guarantee that no internal keys with the same user key and sequence
|
|
||||||
// number can exist in a DB, we know that the largest key in this sstable
|
|
||||||
// cannot exist as the smallest key in the next sstable. This further
|
|
||||||
// implies that no range tombstone in this sstable covers largest;
|
|
||||||
// otherwise, the file boundary would have been artificially extended.
|
|
||||||
//
|
|
||||||
// Therefore, we will never truncate a range tombstone at largest, so we
|
|
||||||
// can leave it unchanged.
|
|
||||||
} else { |
|
||||||
// The same user key may straddle two sstable boundaries. To ensure that
|
|
||||||
// the truncated end key can cover the largest key in this sstable, reduce
|
|
||||||
// its sequence number by 1.
|
|
||||||
parsed_largest.sequence -= 1; |
|
||||||
} |
|
||||||
largest_ = &parsed_largest; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool TruncatedRangeDelIterator::Valid() const { |
|
||||||
return iter_->Valid() && |
|
||||||
(smallest_ == nullptr || |
|
||||||
icmp_->Compare(*smallest_, iter_->parsed_end_key()) < 0) && |
|
||||||
(largest_ == nullptr || |
|
||||||
icmp_->Compare(iter_->parsed_start_key(), *largest_) < 0); |
|
||||||
} |
|
||||||
|
|
||||||
void TruncatedRangeDelIterator::Next() { iter_->TopNext(); } |
|
||||||
|
|
||||||
void TruncatedRangeDelIterator::Prev() { iter_->TopPrev(); } |
|
||||||
|
|
||||||
void TruncatedRangeDelIterator::InternalNext() { iter_->Next(); } |
|
||||||
|
|
||||||
// NOTE: target is a user key
|
|
||||||
void TruncatedRangeDelIterator::Seek(const Slice& target) { |
|
||||||
if (largest_ != nullptr && |
|
||||||
icmp_->Compare(*largest_, ParsedInternalKey(target, kMaxSequenceNumber, |
|
||||||
kTypeRangeDeletion)) <= 0) { |
|
||||||
iter_->Invalidate(); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (smallest_ != nullptr && |
|
||||||
icmp_->user_comparator()->Compare(target, smallest_->user_key) < 0) { |
|
||||||
iter_->Seek(smallest_->user_key); |
|
||||||
return; |
|
||||||
} |
|
||||||
iter_->Seek(target); |
|
||||||
} |
|
||||||
|
|
||||||
// NOTE: target is a user key
|
|
||||||
void TruncatedRangeDelIterator::SeekForPrev(const Slice& target) { |
|
||||||
if (smallest_ != nullptr && |
|
||||||
icmp_->Compare(ParsedInternalKey(target, 0, kTypeRangeDeletion), |
|
||||||
*smallest_) < 0) { |
|
||||||
iter_->Invalidate(); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (largest_ != nullptr && |
|
||||||
icmp_->user_comparator()->Compare(largest_->user_key, target) < 0) { |
|
||||||
iter_->SeekForPrev(largest_->user_key); |
|
||||||
return; |
|
||||||
} |
|
||||||
iter_->SeekForPrev(target); |
|
||||||
} |
|
||||||
|
|
||||||
void TruncatedRangeDelIterator::SeekToFirst() { |
|
||||||
if (smallest_ != nullptr) { |
|
||||||
iter_->Seek(smallest_->user_key); |
|
||||||
return; |
|
||||||
} |
|
||||||
iter_->SeekToTopFirst(); |
|
||||||
} |
|
||||||
|
|
||||||
void TruncatedRangeDelIterator::SeekToLast() { |
|
||||||
if (largest_ != nullptr) { |
|
||||||
iter_->SeekForPrev(largest_->user_key); |
|
||||||
return; |
|
||||||
} |
|
||||||
iter_->SeekToTopLast(); |
|
||||||
} |
|
||||||
|
|
||||||
std::map<SequenceNumber, std::unique_ptr<TruncatedRangeDelIterator>> |
|
||||||
TruncatedRangeDelIterator::SplitBySnapshot( |
|
||||||
const std::vector<SequenceNumber>& snapshots) { |
|
||||||
using FragmentedIterPair = |
|
||||||
std::pair<const SequenceNumber, |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator>>; |
|
||||||
|
|
||||||
auto split_untruncated_iters = iter_->SplitBySnapshot(snapshots); |
|
||||||
std::map<SequenceNumber, std::unique_ptr<TruncatedRangeDelIterator>> |
|
||||||
split_truncated_iters; |
|
||||||
std::for_each( |
|
||||||
split_untruncated_iters.begin(), split_untruncated_iters.end(), |
|
||||||
[&](FragmentedIterPair& iter_pair) { |
|
||||||
std::unique_ptr<TruncatedRangeDelIterator> truncated_iter( |
|
||||||
new TruncatedRangeDelIterator(std::move(iter_pair.second), icmp_, |
|
||||||
smallest_ikey_, largest_ikey_)); |
|
||||||
split_truncated_iters.emplace(iter_pair.first, |
|
||||||
std::move(truncated_iter)); |
|
||||||
}); |
|
||||||
return split_truncated_iters; |
|
||||||
} |
|
||||||
|
|
||||||
ForwardRangeDelIterator::ForwardRangeDelIterator( |
|
||||||
const InternalKeyComparator* icmp, |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>* iters) |
|
||||||
: icmp_(icmp), |
|
||||||
iters_(iters), |
|
||||||
unused_idx_(0), |
|
||||||
active_seqnums_(SeqMaxComparator()), |
|
||||||
active_iters_(EndKeyMinComparator(icmp)), |
|
||||||
inactive_iters_(StartKeyMinComparator(icmp)) {} |
|
||||||
|
|
||||||
bool ForwardRangeDelIterator::ShouldDelete(const ParsedInternalKey& parsed) { |
|
||||||
assert(iters_ != nullptr); |
|
||||||
// Move active iterators that end before parsed.
|
|
||||||
while (!active_iters_.empty() && |
|
||||||
icmp_->Compare((*active_iters_.top())->end_key(), parsed) <= 0) { |
|
||||||
TruncatedRangeDelIterator* iter = PopActiveIter(); |
|
||||||
do { |
|
||||||
iter->Next(); |
|
||||||
} while (iter->Valid() && icmp_->Compare(iter->end_key(), parsed) <= 0); |
|
||||||
PushIter(iter, parsed); |
|
||||||
assert(active_iters_.size() == active_seqnums_.size()); |
|
||||||
} |
|
||||||
|
|
||||||
// Move inactive iterators that start before parsed.
|
|
||||||
while (!inactive_iters_.empty() && |
|
||||||
icmp_->Compare(inactive_iters_.top()->start_key(), parsed) <= 0) { |
|
||||||
TruncatedRangeDelIterator* iter = PopInactiveIter(); |
|
||||||
while (iter->Valid() && icmp_->Compare(iter->end_key(), parsed) <= 0) { |
|
||||||
iter->Next(); |
|
||||||
} |
|
||||||
PushIter(iter, parsed); |
|
||||||
assert(active_iters_.size() == active_seqnums_.size()); |
|
||||||
} |
|
||||||
|
|
||||||
return active_seqnums_.empty() |
|
||||||
? false |
|
||||||
: (*active_seqnums_.begin())->seq() > parsed.sequence; |
|
||||||
} |
|
||||||
|
|
||||||
void ForwardRangeDelIterator::Invalidate() { |
|
||||||
unused_idx_ = 0; |
|
||||||
active_iters_.clear(); |
|
||||||
active_seqnums_.clear(); |
|
||||||
inactive_iters_.clear(); |
|
||||||
} |
|
||||||
|
|
||||||
ReverseRangeDelIterator::ReverseRangeDelIterator( |
|
||||||
const InternalKeyComparator* icmp, |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>* iters) |
|
||||||
: icmp_(icmp), |
|
||||||
iters_(iters), |
|
||||||
unused_idx_(0), |
|
||||||
active_seqnums_(SeqMaxComparator()), |
|
||||||
active_iters_(StartKeyMaxComparator(icmp)), |
|
||||||
inactive_iters_(EndKeyMaxComparator(icmp)) {} |
|
||||||
|
|
||||||
bool ReverseRangeDelIterator::ShouldDelete(const ParsedInternalKey& parsed) { |
|
||||||
assert(iters_ != nullptr); |
|
||||||
// Move active iterators that start after parsed.
|
|
||||||
while (!active_iters_.empty() && |
|
||||||
icmp_->Compare(parsed, (*active_iters_.top())->start_key()) < 0) { |
|
||||||
TruncatedRangeDelIterator* iter = PopActiveIter(); |
|
||||||
do { |
|
||||||
iter->Prev(); |
|
||||||
} while (iter->Valid() && icmp_->Compare(parsed, iter->start_key()) < 0); |
|
||||||
PushIter(iter, parsed); |
|
||||||
assert(active_iters_.size() == active_seqnums_.size()); |
|
||||||
} |
|
||||||
|
|
||||||
// Move inactive iterators that end after parsed.
|
|
||||||
while (!inactive_iters_.empty() && |
|
||||||
icmp_->Compare(parsed, inactive_iters_.top()->end_key()) < 0) { |
|
||||||
TruncatedRangeDelIterator* iter = PopInactiveIter(); |
|
||||||
while (iter->Valid() && icmp_->Compare(parsed, iter->start_key()) < 0) { |
|
||||||
iter->Prev(); |
|
||||||
} |
|
||||||
PushIter(iter, parsed); |
|
||||||
assert(active_iters_.size() == active_seqnums_.size()); |
|
||||||
} |
|
||||||
|
|
||||||
return active_seqnums_.empty() |
|
||||||
? false |
|
||||||
: (*active_seqnums_.begin())->seq() > parsed.sequence; |
|
||||||
} |
|
||||||
|
|
||||||
void ReverseRangeDelIterator::Invalidate() { |
|
||||||
unused_idx_ = 0; |
|
||||||
active_iters_.clear(); |
|
||||||
active_seqnums_.clear(); |
|
||||||
inactive_iters_.clear(); |
|
||||||
} |
|
||||||
|
|
||||||
bool RangeDelAggregatorV2::StripeRep::ShouldDelete( |
|
||||||
const ParsedInternalKey& parsed, RangeDelPositioningMode mode) { |
|
||||||
if (!InStripe(parsed.sequence) || IsEmpty()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
switch (mode) { |
|
||||||
case RangeDelPositioningMode::kForwardTraversal: |
|
||||||
InvalidateReverseIter(); |
|
||||||
|
|
||||||
// Pick up previously unseen iterators.
|
|
||||||
for (auto it = std::next(iters_.begin(), forward_iter_.UnusedIdx()); |
|
||||||
it != iters_.end(); ++it, forward_iter_.IncUnusedIdx()) { |
|
||||||
auto& iter = *it; |
|
||||||
forward_iter_.AddNewIter(iter.get(), parsed); |
|
||||||
} |
|
||||||
|
|
||||||
return forward_iter_.ShouldDelete(parsed); |
|
||||||
case RangeDelPositioningMode::kBackwardTraversal: |
|
||||||
InvalidateForwardIter(); |
|
||||||
|
|
||||||
// Pick up previously unseen iterators.
|
|
||||||
for (auto it = std::next(iters_.begin(), reverse_iter_.UnusedIdx()); |
|
||||||
it != iters_.end(); ++it, reverse_iter_.IncUnusedIdx()) { |
|
||||||
auto& iter = *it; |
|
||||||
reverse_iter_.AddNewIter(iter.get(), parsed); |
|
||||||
} |
|
||||||
|
|
||||||
return reverse_iter_.ShouldDelete(parsed); |
|
||||||
default: |
|
||||||
assert(false); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool RangeDelAggregatorV2::StripeRep::IsRangeOverlapped(const Slice& start, |
|
||||||
const Slice& end) { |
|
||||||
Invalidate(); |
|
||||||
|
|
||||||
// Set the internal start/end keys so that:
|
|
||||||
// - if start_ikey has the same user key and sequence number as the
|
|
||||||
// current end key, start_ikey will be considered greater; and
|
|
||||||
// - if end_ikey has the same user key and sequence number as the current
|
|
||||||
// start key, end_ikey will be considered greater.
|
|
||||||
ParsedInternalKey start_ikey(start, kMaxSequenceNumber, |
|
||||||
static_cast<ValueType>(0)); |
|
||||||
ParsedInternalKey end_ikey(end, 0, static_cast<ValueType>(0)); |
|
||||||
for (auto& iter : iters_) { |
|
||||||
bool checked_candidate_tombstones = false; |
|
||||||
for (iter->SeekForPrev(start); |
|
||||||
iter->Valid() && icmp_->Compare(iter->start_key(), end_ikey) <= 0; |
|
||||||
iter->Next()) { |
|
||||||
checked_candidate_tombstones = true; |
|
||||||
if (icmp_->Compare(start_ikey, iter->end_key()) < 0 && |
|
||||||
icmp_->Compare(iter->start_key(), end_ikey) <= 0) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!checked_candidate_tombstones) { |
|
||||||
// Do an additional check for when the end of the range is the begin
|
|
||||||
// key of a tombstone, which we missed earlier since SeekForPrev'ing
|
|
||||||
// to the start was invalid.
|
|
||||||
iter->SeekForPrev(end); |
|
||||||
if (iter->Valid() && icmp_->Compare(start_ikey, iter->end_key()) < 0 && |
|
||||||
icmp_->Compare(iter->start_key(), end_ikey) <= 0) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
void ReadRangeDelAggregatorV2::AddTombstones( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
|
||||||
const InternalKey* smallest, const InternalKey* largest) { |
|
||||||
if (input_iter == nullptr || input_iter->empty()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
rep_.AddTombstones( |
|
||||||
std::unique_ptr<TruncatedRangeDelIterator>(new TruncatedRangeDelIterator( |
|
||||||
std::move(input_iter), icmp_, smallest, largest))); |
|
||||||
} |
|
||||||
|
|
||||||
bool ReadRangeDelAggregatorV2::ShouldDelete(const ParsedInternalKey& parsed, |
|
||||||
RangeDelPositioningMode mode) { |
|
||||||
return rep_.ShouldDelete(parsed, mode); |
|
||||||
} |
|
||||||
|
|
||||||
bool ReadRangeDelAggregatorV2::IsRangeOverlapped(const Slice& start, |
|
||||||
const Slice& end) { |
|
||||||
InvalidateRangeDelMapPositions(); |
|
||||||
return rep_.IsRangeOverlapped(start, end); |
|
||||||
} |
|
||||||
|
|
||||||
void CompactionRangeDelAggregatorV2::AddTombstones( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
|
||||||
const InternalKey* smallest, const InternalKey* largest) { |
|
||||||
if (input_iter == nullptr || input_iter->empty()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
assert(input_iter->lower_bound() == 0); |
|
||||||
assert(input_iter->upper_bound() == kMaxSequenceNumber); |
|
||||||
parent_iters_.emplace_back(new TruncatedRangeDelIterator( |
|
||||||
std::move(input_iter), icmp_, smallest, largest)); |
|
||||||
|
|
||||||
auto split_iters = parent_iters_.back()->SplitBySnapshot(*snapshots_); |
|
||||||
for (auto& split_iter : split_iters) { |
|
||||||
auto it = reps_.find(split_iter.first); |
|
||||||
if (it == reps_.end()) { |
|
||||||
bool inserted; |
|
||||||
SequenceNumber upper_bound = split_iter.second->upper_bound(); |
|
||||||
SequenceNumber lower_bound = split_iter.second->lower_bound(); |
|
||||||
std::tie(it, inserted) = reps_.emplace( |
|
||||||
split_iter.first, StripeRep(icmp_, upper_bound, lower_bound)); |
|
||||||
assert(inserted); |
|
||||||
} |
|
||||||
assert(it != reps_.end()); |
|
||||||
it->second.AddTombstones(std::move(split_iter.second)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool CompactionRangeDelAggregatorV2::ShouldDelete( |
|
||||||
const ParsedInternalKey& parsed, RangeDelPositioningMode mode) { |
|
||||||
auto it = reps_.lower_bound(parsed.sequence); |
|
||||||
if (it == reps_.end()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
return it->second.ShouldDelete(parsed, mode); |
|
||||||
} |
|
||||||
|
|
||||||
namespace { |
|
||||||
|
|
||||||
class TruncatedRangeDelMergingIter : public InternalIterator { |
|
||||||
public: |
|
||||||
TruncatedRangeDelMergingIter( |
|
||||||
const InternalKeyComparator* icmp, const Slice* lower_bound, |
|
||||||
const Slice* upper_bound, bool upper_bound_inclusive, |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>& children) |
|
||||||
: icmp_(icmp), |
|
||||||
lower_bound_(lower_bound), |
|
||||||
upper_bound_(upper_bound), |
|
||||||
upper_bound_inclusive_(upper_bound_inclusive), |
|
||||||
heap_(StartKeyMinComparator(icmp)) { |
|
||||||
for (auto& child : children) { |
|
||||||
if (child != nullptr) { |
|
||||||
assert(child->lower_bound() == 0); |
|
||||||
assert(child->upper_bound() == kMaxSequenceNumber); |
|
||||||
children_.push_back(child.get()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool Valid() const override { |
|
||||||
return !heap_.empty() && BeforeEndKey(heap_.top()); |
|
||||||
} |
|
||||||
Status status() const override { return Status::OK(); } |
|
||||||
|
|
||||||
void SeekToFirst() override { |
|
||||||
heap_.clear(); |
|
||||||
for (auto& child : children_) { |
|
||||||
if (lower_bound_ != nullptr) { |
|
||||||
child->Seek(*lower_bound_); |
|
||||||
} else { |
|
||||||
child->SeekToFirst(); |
|
||||||
} |
|
||||||
if (child->Valid()) { |
|
||||||
heap_.push(child); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Next() override { |
|
||||||
auto* top = heap_.top(); |
|
||||||
top->InternalNext(); |
|
||||||
if (top->Valid()) { |
|
||||||
heap_.replace_top(top); |
|
||||||
} else { |
|
||||||
heap_.pop(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Slice key() const override { |
|
||||||
auto* top = heap_.top(); |
|
||||||
cur_start_key_.Set(top->start_key().user_key, top->seq(), |
|
||||||
kTypeRangeDeletion); |
|
||||||
return cur_start_key_.Encode(); |
|
||||||
} |
|
||||||
|
|
||||||
Slice value() const override { |
|
||||||
auto* top = heap_.top(); |
|
||||||
assert(top->end_key().sequence == kMaxSequenceNumber); |
|
||||||
return top->end_key().user_key; |
|
||||||
} |
|
||||||
|
|
||||||
// Unused InternalIterator methods
|
|
||||||
void Prev() override { assert(false); } |
|
||||||
void Seek(const Slice& /* target */) override { assert(false); } |
|
||||||
void SeekForPrev(const Slice& /* target */) override { assert(false); } |
|
||||||
void SeekToLast() override { assert(false); } |
|
||||||
|
|
||||||
private: |
|
||||||
bool BeforeEndKey(const TruncatedRangeDelIterator* iter) const { |
|
||||||
if (upper_bound_ == nullptr) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
int cmp = icmp_->user_comparator()->Compare(iter->start_key().user_key, |
|
||||||
*upper_bound_); |
|
||||||
return upper_bound_inclusive_ ? cmp <= 0 : cmp < 0; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp_; |
|
||||||
const Slice* lower_bound_; |
|
||||||
const Slice* upper_bound_; |
|
||||||
bool upper_bound_inclusive_; |
|
||||||
BinaryHeap<TruncatedRangeDelIterator*, StartKeyMinComparator> heap_; |
|
||||||
std::vector<TruncatedRangeDelIterator*> children_; |
|
||||||
|
|
||||||
mutable InternalKey cur_start_key_; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> |
|
||||||
CompactionRangeDelAggregatorV2::NewIterator(const Slice* lower_bound, |
|
||||||
const Slice* upper_bound, |
|
||||||
bool upper_bound_inclusive) { |
|
||||||
InvalidateRangeDelMapPositions(); |
|
||||||
std::unique_ptr<TruncatedRangeDelMergingIter> merging_iter( |
|
||||||
new TruncatedRangeDelMergingIter(icmp_, lower_bound, upper_bound, |
|
||||||
upper_bound_inclusive, parent_iters_)); |
|
||||||
|
|
||||||
// TODO: add tests where tombstone fragments can be outside of upper and lower
|
|
||||||
// bound range
|
|
||||||
auto fragmented_tombstone_list = |
|
||||||
std::make_shared<FragmentedRangeTombstoneList>( |
|
||||||
std::move(merging_iter), *icmp_, true /* for_compaction */, |
|
||||||
*snapshots_); |
|
||||||
|
|
||||||
return std::unique_ptr<FragmentedRangeTombstoneIterator>( |
|
||||||
new FragmentedRangeTombstoneIterator( |
|
||||||
fragmented_tombstone_list, *icmp_, |
|
||||||
kMaxSequenceNumber /* upper_bound */)); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace rocksdb
|
|
@ -1,436 +0,0 @@ |
|||||||
// Copyright (c) 2018-present, Facebook, Inc. All rights reserved.
|
|
||||||
// This source code is licensed under both the GPLv2 (found in the
|
|
||||||
// COPYING file in the root directory) and Apache 2.0 License
|
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
|
||||||
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include <algorithm> |
|
||||||
#include <iterator> |
|
||||||
#include <list> |
|
||||||
#include <map> |
|
||||||
#include <set> |
|
||||||
#include <string> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "db/compaction_iteration_stats.h" |
|
||||||
#include "db/dbformat.h" |
|
||||||
#include "db/pinned_iterators_manager.h" |
|
||||||
#include "db/range_del_aggregator.h" |
|
||||||
#include "db/range_tombstone_fragmenter.h" |
|
||||||
#include "db/version_edit.h" |
|
||||||
#include "include/rocksdb/comparator.h" |
|
||||||
#include "include/rocksdb/types.h" |
|
||||||
#include "table/internal_iterator.h" |
|
||||||
#include "table/scoped_arena_iterator.h" |
|
||||||
#include "table/table_builder.h" |
|
||||||
#include "util/heap.h" |
|
||||||
#include "util/kv_map.h" |
|
||||||
|
|
||||||
namespace rocksdb { |
|
||||||
|
|
||||||
class TruncatedRangeDelIterator { |
|
||||||
public: |
|
||||||
TruncatedRangeDelIterator( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> iter, |
|
||||||
const InternalKeyComparator* icmp, const InternalKey* smallest, |
|
||||||
const InternalKey* largest); |
|
||||||
|
|
||||||
bool Valid() const; |
|
||||||
|
|
||||||
void Next(); |
|
||||||
void Prev(); |
|
||||||
|
|
||||||
void InternalNext(); |
|
||||||
|
|
||||||
// Seeks to the tombstone with the highest viisble sequence number that covers
|
|
||||||
// target (a user key). If no such tombstone exists, the position will be at
|
|
||||||
// the earliest tombstone that ends after target.
|
|
||||||
void Seek(const Slice& target); |
|
||||||
|
|
||||||
// Seeks to the tombstone with the highest viisble sequence number that covers
|
|
||||||
// target (a user key). If no such tombstone exists, the position will be at
|
|
||||||
// the latest tombstone that starts before target.
|
|
||||||
void SeekForPrev(const Slice& target); |
|
||||||
|
|
||||||
void SeekToFirst(); |
|
||||||
void SeekToLast(); |
|
||||||
|
|
||||||
ParsedInternalKey start_key() const { |
|
||||||
return (smallest_ == nullptr || |
|
||||||
icmp_->Compare(*smallest_, iter_->parsed_start_key()) <= 0) |
|
||||||
? iter_->parsed_start_key() |
|
||||||
: *smallest_; |
|
||||||
} |
|
||||||
|
|
||||||
ParsedInternalKey end_key() const { |
|
||||||
return (largest_ == nullptr || |
|
||||||
icmp_->Compare(iter_->parsed_end_key(), *largest_) <= 0) |
|
||||||
? iter_->parsed_end_key() |
|
||||||
: *largest_; |
|
||||||
} |
|
||||||
|
|
||||||
SequenceNumber seq() const { return iter_->seq(); } |
|
||||||
|
|
||||||
std::map<SequenceNumber, std::unique_ptr<TruncatedRangeDelIterator>> |
|
||||||
SplitBySnapshot(const std::vector<SequenceNumber>& snapshots); |
|
||||||
|
|
||||||
SequenceNumber upper_bound() const { return iter_->upper_bound(); } |
|
||||||
|
|
||||||
SequenceNumber lower_bound() const { return iter_->lower_bound(); } |
|
||||||
|
|
||||||
private: |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> iter_; |
|
||||||
const InternalKeyComparator* icmp_; |
|
||||||
const ParsedInternalKey* smallest_ = nullptr; |
|
||||||
const ParsedInternalKey* largest_ = nullptr; |
|
||||||
std::list<ParsedInternalKey> pinned_bounds_; |
|
||||||
|
|
||||||
const InternalKey* smallest_ikey_; |
|
||||||
const InternalKey* largest_ikey_; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SeqMaxComparator { |
|
||||||
bool operator()(const TruncatedRangeDelIterator* a, |
|
||||||
const TruncatedRangeDelIterator* b) const { |
|
||||||
return a->seq() > b->seq(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
struct StartKeyMinComparator { |
|
||||||
explicit StartKeyMinComparator(const InternalKeyComparator* c) : icmp(c) {} |
|
||||||
|
|
||||||
bool operator()(const TruncatedRangeDelIterator* a, |
|
||||||
const TruncatedRangeDelIterator* b) const { |
|
||||||
return icmp->Compare(a->start_key(), b->start_key()) > 0; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp; |
|
||||||
}; |
|
||||||
|
|
||||||
class ForwardRangeDelIterator { |
|
||||||
public: |
|
||||||
ForwardRangeDelIterator( |
|
||||||
const InternalKeyComparator* icmp, |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>* iters); |
|
||||||
|
|
||||||
bool ShouldDelete(const ParsedInternalKey& parsed); |
|
||||||
void Invalidate(); |
|
||||||
|
|
||||||
void AddNewIter(TruncatedRangeDelIterator* iter, |
|
||||||
const ParsedInternalKey& parsed) { |
|
||||||
iter->Seek(parsed.user_key); |
|
||||||
PushIter(iter, parsed); |
|
||||||
assert(active_iters_.size() == active_seqnums_.size()); |
|
||||||
} |
|
||||||
|
|
||||||
size_t UnusedIdx() const { return unused_idx_; } |
|
||||||
void IncUnusedIdx() { unused_idx_++; } |
|
||||||
|
|
||||||
private: |
|
||||||
using ActiveSeqSet = |
|
||||||
std::multiset<TruncatedRangeDelIterator*, SeqMaxComparator>; |
|
||||||
|
|
||||||
struct EndKeyMinComparator { |
|
||||||
explicit EndKeyMinComparator(const InternalKeyComparator* c) : icmp(c) {} |
|
||||||
|
|
||||||
bool operator()(const ActiveSeqSet::const_iterator& a, |
|
||||||
const ActiveSeqSet::const_iterator& b) const { |
|
||||||
return icmp->Compare((*a)->end_key(), (*b)->end_key()) > 0; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp; |
|
||||||
}; |
|
||||||
|
|
||||||
void PushIter(TruncatedRangeDelIterator* iter, |
|
||||||
const ParsedInternalKey& parsed) { |
|
||||||
if (!iter->Valid()) { |
|
||||||
// The iterator has been fully consumed, so we don't need to add it to
|
|
||||||
// either of the heaps.
|
|
||||||
return; |
|
||||||
} |
|
||||||
int cmp = icmp_->Compare(parsed, iter->start_key()); |
|
||||||
if (cmp < 0) { |
|
||||||
PushInactiveIter(iter); |
|
||||||
} else { |
|
||||||
PushActiveIter(iter); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void PushActiveIter(TruncatedRangeDelIterator* iter) { |
|
||||||
auto seq_pos = active_seqnums_.insert(iter); |
|
||||||
active_iters_.push(seq_pos); |
|
||||||
} |
|
||||||
|
|
||||||
TruncatedRangeDelIterator* PopActiveIter() { |
|
||||||
auto active_top = active_iters_.top(); |
|
||||||
auto iter = *active_top; |
|
||||||
active_iters_.pop(); |
|
||||||
active_seqnums_.erase(active_top); |
|
||||||
return iter; |
|
||||||
} |
|
||||||
|
|
||||||
void PushInactiveIter(TruncatedRangeDelIterator* iter) { |
|
||||||
inactive_iters_.push(iter); |
|
||||||
} |
|
||||||
|
|
||||||
TruncatedRangeDelIterator* PopInactiveIter() { |
|
||||||
auto* iter = inactive_iters_.top(); |
|
||||||
inactive_iters_.pop(); |
|
||||||
return iter; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp_; |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>* iters_; |
|
||||||
size_t unused_idx_; |
|
||||||
ActiveSeqSet active_seqnums_; |
|
||||||
BinaryHeap<ActiveSeqSet::const_iterator, EndKeyMinComparator> active_iters_; |
|
||||||
BinaryHeap<TruncatedRangeDelIterator*, StartKeyMinComparator> inactive_iters_; |
|
||||||
}; |
|
||||||
|
|
||||||
class ReverseRangeDelIterator { |
|
||||||
public: |
|
||||||
ReverseRangeDelIterator( |
|
||||||
const InternalKeyComparator* icmp, |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>* iters); |
|
||||||
|
|
||||||
bool ShouldDelete(const ParsedInternalKey& parsed); |
|
||||||
void Invalidate(); |
|
||||||
|
|
||||||
void AddNewIter(TruncatedRangeDelIterator* iter, |
|
||||||
const ParsedInternalKey& parsed) { |
|
||||||
iter->SeekForPrev(parsed.user_key); |
|
||||||
PushIter(iter, parsed); |
|
||||||
assert(active_iters_.size() == active_seqnums_.size()); |
|
||||||
} |
|
||||||
|
|
||||||
size_t UnusedIdx() const { return unused_idx_; } |
|
||||||
void IncUnusedIdx() { unused_idx_++; } |
|
||||||
|
|
||||||
private: |
|
||||||
using ActiveSeqSet = |
|
||||||
std::multiset<TruncatedRangeDelIterator*, SeqMaxComparator>; |
|
||||||
|
|
||||||
struct EndKeyMaxComparator { |
|
||||||
explicit EndKeyMaxComparator(const InternalKeyComparator* c) : icmp(c) {} |
|
||||||
|
|
||||||
bool operator()(const TruncatedRangeDelIterator* a, |
|
||||||
const TruncatedRangeDelIterator* b) const { |
|
||||||
return icmp->Compare(a->end_key(), b->end_key()) < 0; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp; |
|
||||||
}; |
|
||||||
struct StartKeyMaxComparator { |
|
||||||
explicit StartKeyMaxComparator(const InternalKeyComparator* c) : icmp(c) {} |
|
||||||
|
|
||||||
bool operator()(const ActiveSeqSet::const_iterator& a, |
|
||||||
const ActiveSeqSet::const_iterator& b) const { |
|
||||||
return icmp->Compare((*a)->start_key(), (*b)->start_key()) < 0; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp; |
|
||||||
}; |
|
||||||
|
|
||||||
void PushIter(TruncatedRangeDelIterator* iter, |
|
||||||
const ParsedInternalKey& parsed) { |
|
||||||
if (!iter->Valid()) { |
|
||||||
// The iterator has been fully consumed, so we don't need to add it to
|
|
||||||
// either of the heaps.
|
|
||||||
} else if (icmp_->Compare(iter->end_key(), parsed) <= 0) { |
|
||||||
PushInactiveIter(iter); |
|
||||||
} else { |
|
||||||
PushActiveIter(iter); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void PushActiveIter(TruncatedRangeDelIterator* iter) { |
|
||||||
auto seq_pos = active_seqnums_.insert(iter); |
|
||||||
active_iters_.push(seq_pos); |
|
||||||
} |
|
||||||
|
|
||||||
TruncatedRangeDelIterator* PopActiveIter() { |
|
||||||
auto active_top = active_iters_.top(); |
|
||||||
auto iter = *active_top; |
|
||||||
active_iters_.pop(); |
|
||||||
active_seqnums_.erase(active_top); |
|
||||||
return iter; |
|
||||||
} |
|
||||||
|
|
||||||
void PushInactiveIter(TruncatedRangeDelIterator* iter) { |
|
||||||
inactive_iters_.push(iter); |
|
||||||
} |
|
||||||
|
|
||||||
TruncatedRangeDelIterator* PopInactiveIter() { |
|
||||||
auto* iter = inactive_iters_.top(); |
|
||||||
inactive_iters_.pop(); |
|
||||||
return iter; |
|
||||||
} |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp_; |
|
||||||
const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>* iters_; |
|
||||||
size_t unused_idx_; |
|
||||||
ActiveSeqSet active_seqnums_; |
|
||||||
BinaryHeap<ActiveSeqSet::const_iterator, StartKeyMaxComparator> active_iters_; |
|
||||||
BinaryHeap<TruncatedRangeDelIterator*, EndKeyMaxComparator> inactive_iters_; |
|
||||||
}; |
|
||||||
|
|
||||||
class RangeDelAggregatorV2 { |
|
||||||
public: |
|
||||||
explicit RangeDelAggregatorV2(const InternalKeyComparator* icmp) |
|
||||||
: icmp_(icmp) {} |
|
||||||
virtual ~RangeDelAggregatorV2() {} |
|
||||||
|
|
||||||
virtual void AddTombstones( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
|
||||||
const InternalKey* smallest = nullptr, |
|
||||||
const InternalKey* largest = nullptr) = 0; |
|
||||||
|
|
||||||
bool ShouldDelete(const Slice& key, RangeDelPositioningMode mode) { |
|
||||||
ParsedInternalKey parsed; |
|
||||||
if (!ParseInternalKey(key, &parsed)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
return ShouldDelete(parsed, mode); |
|
||||||
} |
|
||||||
virtual bool ShouldDelete(const ParsedInternalKey& parsed, |
|
||||||
RangeDelPositioningMode mode) = 0; |
|
||||||
|
|
||||||
virtual void InvalidateRangeDelMapPositions() = 0; |
|
||||||
|
|
||||||
virtual bool IsEmpty() const = 0; |
|
||||||
|
|
||||||
bool AddFile(uint64_t file_number) { |
|
||||||
return files_seen_.insert(file_number).second; |
|
||||||
} |
|
||||||
|
|
||||||
protected: |
|
||||||
class StripeRep { |
|
||||||
public: |
|
||||||
StripeRep(const InternalKeyComparator* icmp, SequenceNumber upper_bound, |
|
||||||
SequenceNumber lower_bound) |
|
||||||
: icmp_(icmp), |
|
||||||
forward_iter_(icmp, &iters_), |
|
||||||
reverse_iter_(icmp, &iters_), |
|
||||||
upper_bound_(upper_bound), |
|
||||||
lower_bound_(lower_bound) {} |
|
||||||
|
|
||||||
void AddTombstones(std::unique_ptr<TruncatedRangeDelIterator> input_iter) { |
|
||||||
iters_.push_back(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
bool IsEmpty() const { return iters_.empty(); } |
|
||||||
|
|
||||||
bool ShouldDelete(const ParsedInternalKey& parsed, |
|
||||||
RangeDelPositioningMode mode); |
|
||||||
|
|
||||||
void Invalidate() { |
|
||||||
InvalidateForwardIter(); |
|
||||||
InvalidateReverseIter(); |
|
||||||
} |
|
||||||
|
|
||||||
bool IsRangeOverlapped(const Slice& start, const Slice& end); |
|
||||||
|
|
||||||
private: |
|
||||||
bool InStripe(SequenceNumber seq) const { |
|
||||||
return lower_bound_ <= seq && seq <= upper_bound_; |
|
||||||
} |
|
||||||
|
|
||||||
void InvalidateForwardIter() { forward_iter_.Invalidate(); } |
|
||||||
|
|
||||||
void InvalidateReverseIter() { reverse_iter_.Invalidate(); } |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp_; |
|
||||||
std::vector<std::unique_ptr<TruncatedRangeDelIterator>> iters_; |
|
||||||
ForwardRangeDelIterator forward_iter_; |
|
||||||
ReverseRangeDelIterator reverse_iter_; |
|
||||||
SequenceNumber upper_bound_; |
|
||||||
SequenceNumber lower_bound_; |
|
||||||
}; |
|
||||||
|
|
||||||
const InternalKeyComparator* icmp_; |
|
||||||
|
|
||||||
private: |
|
||||||
std::set<uint64_t> files_seen_; |
|
||||||
}; |
|
||||||
|
|
||||||
class ReadRangeDelAggregatorV2 : public RangeDelAggregatorV2 { |
|
||||||
public: |
|
||||||
ReadRangeDelAggregatorV2(const InternalKeyComparator* icmp, |
|
||||||
SequenceNumber upper_bound) |
|
||||||
: RangeDelAggregatorV2(icmp), |
|
||||||
rep_(icmp, upper_bound, 0 /* lower_bound */) {} |
|
||||||
~ReadRangeDelAggregatorV2() override {} |
|
||||||
|
|
||||||
using RangeDelAggregatorV2::ShouldDelete; |
|
||||||
void AddTombstones( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
|
||||||
const InternalKey* smallest = nullptr, |
|
||||||
const InternalKey* largest = nullptr) override; |
|
||||||
|
|
||||||
bool ShouldDelete(const ParsedInternalKey& parsed, |
|
||||||
RangeDelPositioningMode mode) override; |
|
||||||
|
|
||||||
bool IsRangeOverlapped(const Slice& start, const Slice& end); |
|
||||||
|
|
||||||
void InvalidateRangeDelMapPositions() override { rep_.Invalidate(); } |
|
||||||
|
|
||||||
bool IsEmpty() const override { return rep_.IsEmpty(); } |
|
||||||
|
|
||||||
private: |
|
||||||
StripeRep rep_; |
|
||||||
}; |
|
||||||
|
|
||||||
class CompactionRangeDelAggregatorV2 : public RangeDelAggregatorV2 { |
|
||||||
public: |
|
||||||
CompactionRangeDelAggregatorV2(const InternalKeyComparator* icmp, |
|
||||||
const std::vector<SequenceNumber>& snapshots) |
|
||||||
: RangeDelAggregatorV2(icmp), snapshots_(&snapshots) {} |
|
||||||
~CompactionRangeDelAggregatorV2() override {} |
|
||||||
|
|
||||||
void AddTombstones( |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
|
||||||
const InternalKey* smallest = nullptr, |
|
||||||
const InternalKey* largest = nullptr) override; |
|
||||||
|
|
||||||
using RangeDelAggregatorV2::ShouldDelete; |
|
||||||
bool ShouldDelete(const ParsedInternalKey& parsed, |
|
||||||
RangeDelPositioningMode mode) override; |
|
||||||
|
|
||||||
bool IsRangeOverlapped(const Slice& start, const Slice& end); |
|
||||||
|
|
||||||
void InvalidateRangeDelMapPositions() override { |
|
||||||
for (auto& rep : reps_) { |
|
||||||
rep.second.Invalidate(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool IsEmpty() const override { |
|
||||||
for (const auto& rep : reps_) { |
|
||||||
if (!rep.second.IsEmpty()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Creates an iterator over all the range tombstones in the aggregator, for
|
|
||||||
// use in compaction. Nullptr arguments indicate that the iterator range is
|
|
||||||
// unbounded.
|
|
||||||
// NOTE: the boundaries are used for optimization purposes to reduce the
|
|
||||||
// number of tombstones that are passed to the fragmenter; they do not
|
|
||||||
// guarantee that the resulting iterator only contains range tombstones that
|
|
||||||
// cover keys in the provided range. If required, these bounds must be
|
|
||||||
// enforced during iteration.
|
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> NewIterator( |
|
||||||
const Slice* lower_bound = nullptr, const Slice* upper_bound = nullptr, |
|
||||||
bool upper_bound_inclusive = false); |
|
||||||
|
|
||||||
private: |
|
||||||
std::vector<std::unique_ptr<TruncatedRangeDelIterator>> parent_iters_; |
|
||||||
std::map<SequenceNumber, StripeRep> reps_; |
|
||||||
|
|
||||||
const std::vector<SequenceNumber>* snapshots_; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace rocksdb
|
|
@ -1,709 +0,0 @@ |
|||||||
// Copyright (c) 2018-present, Facebook, Inc. All rights reserved.
|
|
||||||
// This source code is licensed under both the GPLv2 (found in the
|
|
||||||
// COPYING file in the root directory) and Apache 2.0 License
|
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
|
||||||
|
|
||||||
#include "db/range_del_aggregator_v2.h" |
|
||||||
|
|
||||||
#include <memory> |
|
||||||
#include <string> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "db/db_test_util.h" |
|
||||||
#include "db/dbformat.h" |
|
||||||
#include "db/range_tombstone_fragmenter.h" |
|
||||||
#include "util/testutil.h" |
|
||||||
|
|
||||||
namespace rocksdb { |
|
||||||
|
|
||||||
class RangeDelAggregatorV2Test : public testing::Test {}; |
|
||||||
|
|
||||||
namespace { |
|
||||||
|
|
||||||
static auto bytewise_icmp = InternalKeyComparator(BytewiseComparator()); |
|
||||||
|
|
||||||
std::unique_ptr<InternalIterator> MakeRangeDelIter( |
|
||||||
const std::vector<RangeTombstone>& range_dels) { |
|
||||||
std::vector<std::string> keys, values; |
|
||||||
for (const auto& range_del : range_dels) { |
|
||||||
auto key_and_value = range_del.Serialize(); |
|
||||||
keys.push_back(key_and_value.first.Encode().ToString()); |
|
||||||
values.push_back(key_and_value.second.ToString()); |
|
||||||
} |
|
||||||
return std::unique_ptr<test::VectorIterator>( |
|
||||||
new test::VectorIterator(keys, values)); |
|
||||||
} |
|
||||||
|
|
||||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneList>> |
|
||||||
MakeFragmentedTombstoneLists( |
|
||||||
const std::vector<std::vector<RangeTombstone>>& range_dels_list) { |
|
||||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneList>> fragment_lists; |
|
||||||
for (const auto& range_dels : range_dels_list) { |
|
||||||
auto range_del_iter = MakeRangeDelIter(range_dels); |
|
||||||
fragment_lists.emplace_back(new FragmentedRangeTombstoneList( |
|
||||||
std::move(range_del_iter), bytewise_icmp)); |
|
||||||
} |
|
||||||
return fragment_lists; |
|
||||||
} |
|
||||||
|
|
||||||
struct TruncatedIterScanTestCase { |
|
||||||
ParsedInternalKey start; |
|
||||||
ParsedInternalKey end; |
|
||||||
SequenceNumber seq; |
|
||||||
}; |
|
||||||
|
|
||||||
struct TruncatedIterSeekTestCase { |
|
||||||
Slice target; |
|
||||||
ParsedInternalKey start; |
|
||||||
ParsedInternalKey end; |
|
||||||
SequenceNumber seq; |
|
||||||
bool invalid; |
|
||||||
}; |
|
||||||
|
|
||||||
struct ShouldDeleteTestCase { |
|
||||||
ParsedInternalKey lookup_key; |
|
||||||
bool result; |
|
||||||
}; |
|
||||||
|
|
||||||
struct IsRangeOverlappedTestCase { |
|
||||||
Slice start; |
|
||||||
Slice end; |
|
||||||
bool result; |
|
||||||
}; |
|
||||||
|
|
||||||
ParsedInternalKey UncutEndpoint(const Slice& s) { |
|
||||||
return ParsedInternalKey(s, kMaxSequenceNumber, kTypeRangeDeletion); |
|
||||||
} |
|
||||||
|
|
||||||
ParsedInternalKey InternalValue(const Slice& key, SequenceNumber seq) { |
|
||||||
return ParsedInternalKey(key, seq, kTypeValue); |
|
||||||
} |
|
||||||
|
|
||||||
void VerifyIterator( |
|
||||||
TruncatedRangeDelIterator* iter, const InternalKeyComparator& icmp, |
|
||||||
const std::vector<TruncatedIterScanTestCase>& expected_range_dels) { |
|
||||||
// Test forward iteration.
|
|
||||||
iter->SeekToFirst(); |
|
||||||
for (size_t i = 0; i < expected_range_dels.size(); i++, iter->Next()) { |
|
||||||
ASSERT_TRUE(iter->Valid()); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->start_key(), expected_range_dels[i].start)); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->end_key(), expected_range_dels[i].end)); |
|
||||||
EXPECT_EQ(expected_range_dels[i].seq, iter->seq()); |
|
||||||
} |
|
||||||
EXPECT_FALSE(iter->Valid()); |
|
||||||
|
|
||||||
// Test reverse iteration.
|
|
||||||
iter->SeekToLast(); |
|
||||||
std::vector<TruncatedIterScanTestCase> reverse_expected_range_dels( |
|
||||||
expected_range_dels.rbegin(), expected_range_dels.rend()); |
|
||||||
for (size_t i = 0; i < reverse_expected_range_dels.size(); |
|
||||||
i++, iter->Prev()) { |
|
||||||
ASSERT_TRUE(iter->Valid()); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->start_key(), |
|
||||||
reverse_expected_range_dels[i].start)); |
|
||||||
EXPECT_EQ( |
|
||||||
0, icmp.Compare(iter->end_key(), reverse_expected_range_dels[i].end)); |
|
||||||
EXPECT_EQ(reverse_expected_range_dels[i].seq, iter->seq()); |
|
||||||
} |
|
||||||
EXPECT_FALSE(iter->Valid()); |
|
||||||
} |
|
||||||
|
|
||||||
void VerifySeek(TruncatedRangeDelIterator* iter, |
|
||||||
const InternalKeyComparator& icmp, |
|
||||||
const std::vector<TruncatedIterSeekTestCase>& test_cases) { |
|
||||||
for (const auto& test_case : test_cases) { |
|
||||||
iter->Seek(test_case.target); |
|
||||||
if (test_case.invalid) { |
|
||||||
ASSERT_FALSE(iter->Valid()); |
|
||||||
} else { |
|
||||||
ASSERT_TRUE(iter->Valid()); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->start_key(), test_case.start)); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->end_key(), test_case.end)); |
|
||||||
EXPECT_EQ(test_case.seq, iter->seq()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void VerifySeekForPrev( |
|
||||||
TruncatedRangeDelIterator* iter, const InternalKeyComparator& icmp, |
|
||||||
const std::vector<TruncatedIterSeekTestCase>& test_cases) { |
|
||||||
for (const auto& test_case : test_cases) { |
|
||||||
iter->SeekForPrev(test_case.target); |
|
||||||
if (test_case.invalid) { |
|
||||||
ASSERT_FALSE(iter->Valid()); |
|
||||||
} else { |
|
||||||
ASSERT_TRUE(iter->Valid()); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->start_key(), test_case.start)); |
|
||||||
EXPECT_EQ(0, icmp.Compare(iter->end_key(), test_case.end)); |
|
||||||
EXPECT_EQ(test_case.seq, iter->seq()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void VerifyShouldDelete(RangeDelAggregatorV2* range_del_agg, |
|
||||||
const std::vector<ShouldDeleteTestCase>& test_cases) { |
|
||||||
for (const auto& test_case : test_cases) { |
|
||||||
EXPECT_EQ( |
|
||||||
test_case.result, |
|
||||||
range_del_agg->ShouldDelete( |
|
||||||
test_case.lookup_key, RangeDelPositioningMode::kForwardTraversal)); |
|
||||||
} |
|
||||||
for (auto it = test_cases.rbegin(); it != test_cases.rend(); ++it) { |
|
||||||
const auto& test_case = *it; |
|
||||||
EXPECT_EQ( |
|
||||||
test_case.result, |
|
||||||
range_del_agg->ShouldDelete( |
|
||||||
test_case.lookup_key, RangeDelPositioningMode::kBackwardTraversal)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void VerifyIsRangeOverlapped( |
|
||||||
ReadRangeDelAggregatorV2* range_del_agg, |
|
||||||
const std::vector<IsRangeOverlappedTestCase>& test_cases) { |
|
||||||
for (const auto& test_case : test_cases) { |
|
||||||
EXPECT_EQ(test_case.result, |
|
||||||
range_del_agg->IsRangeOverlapped(test_case.start, test_case.end)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void CheckIterPosition(const RangeTombstone& tombstone, |
|
||||||
const FragmentedRangeTombstoneIterator* iter) { |
|
||||||
// Test InternalIterator interface.
|
|
||||||
EXPECT_EQ(tombstone.start_key_, ExtractUserKey(iter->key())); |
|
||||||
EXPECT_EQ(tombstone.end_key_, iter->value()); |
|
||||||
EXPECT_EQ(tombstone.seq_, iter->seq()); |
|
||||||
|
|
||||||
// Test FragmentedRangeTombstoneIterator interface.
|
|
||||||
EXPECT_EQ(tombstone.start_key_, iter->start_key()); |
|
||||||
EXPECT_EQ(tombstone.end_key_, iter->end_key()); |
|
||||||
EXPECT_EQ(tombstone.seq_, GetInternalKeySeqno(iter->key())); |
|
||||||
} |
|
||||||
|
|
||||||
void VerifyFragmentedRangeDels( |
|
||||||
FragmentedRangeTombstoneIterator* iter, |
|
||||||
const std::vector<RangeTombstone>& expected_tombstones) { |
|
||||||
iter->SeekToFirst(); |
|
||||||
for (size_t i = 0; i < expected_tombstones.size(); i++, iter->Next()) { |
|
||||||
ASSERT_TRUE(iter->Valid()); |
|
||||||
CheckIterPosition(expected_tombstones[i], iter); |
|
||||||
} |
|
||||||
EXPECT_FALSE(iter->Valid()); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, EmptyTruncatedIter) { |
|
||||||
auto range_del_iter = MakeRangeDelIter({}); |
|
||||||
FragmentedRangeTombstoneList fragment_list(std::move(range_del_iter), |
|
||||||
bytewise_icmp); |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(&fragment_list, bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
|
|
||||||
TruncatedRangeDelIterator iter(std::move(input_iter), &bytewise_icmp, nullptr, |
|
||||||
nullptr); |
|
||||||
|
|
||||||
iter.SeekToFirst(); |
|
||||||
ASSERT_FALSE(iter.Valid()); |
|
||||||
|
|
||||||
iter.SeekToLast(); |
|
||||||
ASSERT_FALSE(iter.Valid()); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, UntruncatedIter) { |
|
||||||
auto range_del_iter = |
|
||||||
MakeRangeDelIter({{"a", "e", 10}, {"e", "g", 8}, {"j", "n", 4}}); |
|
||||||
FragmentedRangeTombstoneList fragment_list(std::move(range_del_iter), |
|
||||||
bytewise_icmp); |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(&fragment_list, bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
|
|
||||||
TruncatedRangeDelIterator iter(std::move(input_iter), &bytewise_icmp, nullptr, |
|
||||||
nullptr); |
|
||||||
|
|
||||||
VerifyIterator(&iter, bytewise_icmp, |
|
||||||
{{UncutEndpoint("a"), UncutEndpoint("e"), 10}, |
|
||||||
{UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{UncutEndpoint("j"), UncutEndpoint("n"), 4}}); |
|
||||||
|
|
||||||
VerifySeek( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", UncutEndpoint("a"), UncutEndpoint("e"), 10}, |
|
||||||
{"e", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"ia", UncutEndpoint("j"), UncutEndpoint("n"), 4}, |
|
||||||
{"n", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}, |
|
||||||
{"", UncutEndpoint("a"), UncutEndpoint("e"), 10}}); |
|
||||||
|
|
||||||
VerifySeekForPrev( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", UncutEndpoint("a"), UncutEndpoint("e"), 10}, |
|
||||||
{"e", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"ia", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"n", UncutEndpoint("j"), UncutEndpoint("n"), 4}, |
|
||||||
{"", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, UntruncatedIterWithSnapshot) { |
|
||||||
auto range_del_iter = |
|
||||||
MakeRangeDelIter({{"a", "e", 10}, {"e", "g", 8}, {"j", "n", 4}}); |
|
||||||
FragmentedRangeTombstoneList fragment_list(std::move(range_del_iter), |
|
||||||
bytewise_icmp); |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(&fragment_list, bytewise_icmp, |
|
||||||
9 /* snapshot */)); |
|
||||||
|
|
||||||
TruncatedRangeDelIterator iter(std::move(input_iter), &bytewise_icmp, nullptr, |
|
||||||
nullptr); |
|
||||||
|
|
||||||
VerifyIterator(&iter, bytewise_icmp, |
|
||||||
{{UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{UncutEndpoint("j"), UncutEndpoint("n"), 4}}); |
|
||||||
|
|
||||||
VerifySeek( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"e", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"ia", UncutEndpoint("j"), UncutEndpoint("n"), 4}, |
|
||||||
{"n", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}, |
|
||||||
{"", UncutEndpoint("e"), UncutEndpoint("g"), 8}}); |
|
||||||
|
|
||||||
VerifySeekForPrev( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}, |
|
||||||
{"e", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"ia", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"n", UncutEndpoint("j"), UncutEndpoint("n"), 4}, |
|
||||||
{"", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, TruncatedIterPartiallyCutTombstones) { |
|
||||||
auto range_del_iter = |
|
||||||
MakeRangeDelIter({{"a", "e", 10}, {"e", "g", 8}, {"j", "n", 4}}); |
|
||||||
FragmentedRangeTombstoneList fragment_list(std::move(range_del_iter), |
|
||||||
bytewise_icmp); |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(&fragment_list, bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
|
|
||||||
InternalKey smallest("d", 7, kTypeValue); |
|
||||||
InternalKey largest("m", 9, kTypeValue); |
|
||||||
TruncatedRangeDelIterator iter(std::move(input_iter), &bytewise_icmp, |
|
||||||
&smallest, &largest); |
|
||||||
|
|
||||||
VerifyIterator(&iter, bytewise_icmp, |
|
||||||
{{InternalValue("d", 7), UncutEndpoint("e"), 10}, |
|
||||||
{UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{UncutEndpoint("j"), InternalValue("m", 8), 4}}); |
|
||||||
|
|
||||||
VerifySeek( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", InternalValue("d", 7), UncutEndpoint("e"), 10}, |
|
||||||
{"e", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"ia", UncutEndpoint("j"), InternalValue("m", 8), 4}, |
|
||||||
{"n", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}, |
|
||||||
{"", InternalValue("d", 7), UncutEndpoint("e"), 10}}); |
|
||||||
|
|
||||||
VerifySeekForPrev( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", InternalValue("d", 7), UncutEndpoint("e"), 10}, |
|
||||||
{"e", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"ia", UncutEndpoint("e"), UncutEndpoint("g"), 8}, |
|
||||||
{"n", UncutEndpoint("j"), InternalValue("m", 8), 4}, |
|
||||||
{"", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, TruncatedIterFullyCutTombstones) { |
|
||||||
auto range_del_iter = |
|
||||||
MakeRangeDelIter({{"a", "e", 10}, {"e", "g", 8}, {"j", "n", 4}}); |
|
||||||
FragmentedRangeTombstoneList fragment_list(std::move(range_del_iter), |
|
||||||
bytewise_icmp); |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(&fragment_list, bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
|
|
||||||
InternalKey smallest("f", 7, kTypeValue); |
|
||||||
InternalKey largest("i", 9, kTypeValue); |
|
||||||
TruncatedRangeDelIterator iter(std::move(input_iter), &bytewise_icmp, |
|
||||||
&smallest, &largest); |
|
||||||
|
|
||||||
VerifyIterator(&iter, bytewise_icmp, |
|
||||||
{{InternalValue("f", 7), UncutEndpoint("g"), 8}}); |
|
||||||
|
|
||||||
VerifySeek( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", InternalValue("f", 7), UncutEndpoint("g"), 8}, |
|
||||||
{"f", InternalValue("f", 7), UncutEndpoint("g"), 8}, |
|
||||||
{"j", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}}); |
|
||||||
|
|
||||||
VerifySeekForPrev( |
|
||||||
&iter, bytewise_icmp, |
|
||||||
{{"d", UncutEndpoint(""), UncutEndpoint(""), 0, true /* invalid */}, |
|
||||||
{"f", InternalValue("f", 7), UncutEndpoint("g"), 8}, |
|
||||||
{"j", InternalValue("f", 7), UncutEndpoint("g"), 8}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, SingleIterInAggregator) { |
|
||||||
auto range_del_iter = MakeRangeDelIter({{"a", "e", 10}, {"c", "g", 8}}); |
|
||||||
FragmentedRangeTombstoneList fragment_list(std::move(range_del_iter), |
|
||||||
bytewise_icmp); |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(&fragment_list, bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
|
|
||||||
ReadRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, kMaxSequenceNumber); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
|
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("a", 19), false}, |
|
||||||
{InternalValue("b", 9), true}, |
|
||||||
{InternalValue("d", 9), true}, |
|
||||||
{InternalValue("e", 7), true}, |
|
||||||
{InternalValue("g", 7), false}}); |
|
||||||
|
|
||||||
VerifyIsRangeOverlapped(&range_del_agg, {{"", "_", false}, |
|
||||||
{"_", "a", true}, |
|
||||||
{"a", "c", true}, |
|
||||||
{"d", "f", true}, |
|
||||||
{"g", "l", false}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, MultipleItersInAggregator) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
ReadRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, kMaxSequenceNumber); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("a", 19), true}, |
|
||||||
{InternalValue("b", 19), false}, |
|
||||||
{InternalValue("b", 9), true}, |
|
||||||
{InternalValue("d", 9), true}, |
|
||||||
{InternalValue("e", 7), true}, |
|
||||||
{InternalValue("g", 7), false}, |
|
||||||
{InternalValue("h", 24), true}, |
|
||||||
{InternalValue("i", 24), false}, |
|
||||||
{InternalValue("ii", 14), true}, |
|
||||||
{InternalValue("j", 14), false}}); |
|
||||||
|
|
||||||
VerifyIsRangeOverlapped(&range_del_agg, {{"", "_", false}, |
|
||||||
{"_", "a", true}, |
|
||||||
{"a", "c", true}, |
|
||||||
{"d", "f", true}, |
|
||||||
{"g", "l", true}, |
|
||||||
{"x", "y", false}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, MultipleItersInAggregatorWithUpperBound) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
ReadRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, 19); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
19 /* snapshot */)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("a", 19), false}, |
|
||||||
{InternalValue("a", 9), true}, |
|
||||||
{InternalValue("b", 9), true}, |
|
||||||
{InternalValue("d", 9), true}, |
|
||||||
{InternalValue("e", 7), true}, |
|
||||||
{InternalValue("g", 7), false}, |
|
||||||
{InternalValue("h", 24), false}, |
|
||||||
{InternalValue("i", 24), false}, |
|
||||||
{InternalValue("ii", 14), true}, |
|
||||||
{InternalValue("j", 14), false}}); |
|
||||||
|
|
||||||
VerifyIsRangeOverlapped(&range_del_agg, {{"", "_", false}, |
|
||||||
{"_", "a", true}, |
|
||||||
{"a", "c", true}, |
|
||||||
{"d", "f", true}, |
|
||||||
{"g", "l", true}, |
|
||||||
{"x", "y", false}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, MultipleTruncatedItersInAggregator) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "z", 10}}, {{"a", "z", 10}}, {{"a", "z", 10}}}); |
|
||||||
std::vector<std::pair<InternalKey, InternalKey>> iter_bounds = { |
|
||||||
{InternalKey("a", 4, kTypeValue), |
|
||||||
InternalKey("m", kMaxSequenceNumber, kTypeRangeDeletion)}, |
|
||||||
{InternalKey("m", 20, kTypeValue), |
|
||||||
InternalKey("x", kMaxSequenceNumber, kTypeRangeDeletion)}, |
|
||||||
{InternalKey("x", 5, kTypeValue), InternalKey("zz", 30, kTypeValue)}}; |
|
||||||
|
|
||||||
ReadRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, 19); |
|
||||||
for (size_t i = 0; i < fragment_lists.size(); i++) { |
|
||||||
const auto& fragment_list = fragment_lists[i]; |
|
||||||
const auto& bounds = iter_bounds[i]; |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
19 /* snapshot */)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter), &bounds.first, |
|
||||||
&bounds.second); |
|
||||||
} |
|
||||||
|
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("a", 10), false}, |
|
||||||
{InternalValue("a", 9), false}, |
|
||||||
{InternalValue("a", 4), true}, |
|
||||||
{InternalValue("m", 10), false}, |
|
||||||
{InternalValue("m", 9), true}, |
|
||||||
{InternalValue("x", 10), false}, |
|
||||||
{InternalValue("x", 9), false}, |
|
||||||
{InternalValue("x", 5), true}, |
|
||||||
{InternalValue("z", 9), false}}); |
|
||||||
|
|
||||||
VerifyIsRangeOverlapped(&range_del_agg, {{"", "_", false}, |
|
||||||
{"_", "a", true}, |
|
||||||
{"a", "n", true}, |
|
||||||
{"l", "x", true}, |
|
||||||
{"w", "z", true}, |
|
||||||
{"zzz", "zz", false}, |
|
||||||
{"zz", "zzz", false}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, MultipleTruncatedItersInAggregatorSameLevel) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "z", 10}}, {{"a", "z", 10}}, {{"a", "z", 10}}}); |
|
||||||
std::vector<std::pair<InternalKey, InternalKey>> iter_bounds = { |
|
||||||
{InternalKey("a", 4, kTypeValue), |
|
||||||
InternalKey("m", kMaxSequenceNumber, kTypeRangeDeletion)}, |
|
||||||
{InternalKey("m", 20, kTypeValue), |
|
||||||
InternalKey("x", kMaxSequenceNumber, kTypeRangeDeletion)}, |
|
||||||
{InternalKey("x", 5, kTypeValue), InternalKey("zz", 30, kTypeValue)}}; |
|
||||||
|
|
||||||
ReadRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, 19); |
|
||||||
|
|
||||||
auto add_iter_to_agg = [&](size_t i) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_lists[i].get(), |
|
||||||
bytewise_icmp, 19 /* snapshot */)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter), &iter_bounds[i].first, |
|
||||||
&iter_bounds[i].second); |
|
||||||
}; |
|
||||||
|
|
||||||
add_iter_to_agg(0); |
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("a", 10), false}, |
|
||||||
{InternalValue("a", 9), false}, |
|
||||||
{InternalValue("a", 4), true}}); |
|
||||||
|
|
||||||
add_iter_to_agg(1); |
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("m", 10), false}, |
|
||||||
{InternalValue("m", 9), true}}); |
|
||||||
|
|
||||||
add_iter_to_agg(2); |
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("x", 10), false}, |
|
||||||
{InternalValue("x", 9), false}, |
|
||||||
{InternalValue("x", 5), true}, |
|
||||||
{InternalValue("z", 9), false}}); |
|
||||||
|
|
||||||
VerifyIsRangeOverlapped(&range_del_agg, {{"", "_", false}, |
|
||||||
{"_", "a", true}, |
|
||||||
{"a", "n", true}, |
|
||||||
{"l", "x", true}, |
|
||||||
{"w", "z", true}, |
|
||||||
{"zzz", "zz", false}, |
|
||||||
{"zz", "zzz", false}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, CompactionAggregatorNoSnapshots) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
std::vector<SequenceNumber> snapshots; |
|
||||||
CompactionRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, snapshots); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
VerifyShouldDelete(&range_del_agg, {{InternalValue("a", 19), true}, |
|
||||||
{InternalValue("b", 19), false}, |
|
||||||
{InternalValue("b", 9), true}, |
|
||||||
{InternalValue("d", 9), true}, |
|
||||||
{InternalValue("e", 7), true}, |
|
||||||
{InternalValue("g", 7), false}, |
|
||||||
{InternalValue("h", 24), true}, |
|
||||||
{InternalValue("i", 24), false}, |
|
||||||
{InternalValue("ii", 14), true}, |
|
||||||
{InternalValue("j", 14), false}}); |
|
||||||
|
|
||||||
auto range_del_compaction_iter = range_del_agg.NewIterator(); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter.get(), {{"a", "b", 20}, |
|
||||||
{"b", "c", 10}, |
|
||||||
{"c", "e", 10}, |
|
||||||
{"e", "g", 8}, |
|
||||||
{"h", "i", 25}, |
|
||||||
{"ii", "j", 15}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, CompactionAggregatorWithSnapshots) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
std::vector<SequenceNumber> snapshots{9, 19}; |
|
||||||
CompactionRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, snapshots); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
VerifyShouldDelete( |
|
||||||
&range_del_agg, |
|
||||||
{ |
|
||||||
{InternalValue("a", 19), false}, // [10, 19]
|
|
||||||
{InternalValue("a", 9), false}, // [0, 9]
|
|
||||||
{InternalValue("b", 9), false}, // [0, 9]
|
|
||||||
{InternalValue("d", 9), false}, // [0, 9]
|
|
||||||
{InternalValue("d", 7), true}, // [0, 9]
|
|
||||||
{InternalValue("e", 7), true}, // [0, 9]
|
|
||||||
{InternalValue("g", 7), false}, // [0, 9]
|
|
||||||
{InternalValue("h", 24), true}, // [20, kMaxSequenceNumber]
|
|
||||||
{InternalValue("i", 24), false}, // [20, kMaxSequenceNumber]
|
|
||||||
{InternalValue("ii", 14), true}, // [10, 19]
|
|
||||||
{InternalValue("j", 14), false} // [10, 19]
|
|
||||||
}); |
|
||||||
|
|
||||||
auto range_del_compaction_iter = range_del_agg.NewIterator(); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter.get(), {{"a", "b", 20}, |
|
||||||
{"a", "b", 10}, |
|
||||||
{"b", "c", 10}, |
|
||||||
{"c", "e", 10}, |
|
||||||
{"c", "e", 8}, |
|
||||||
{"e", "g", 8}, |
|
||||||
{"h", "i", 25}, |
|
||||||
{"ii", "j", 15}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, CompactionAggregatorEmptyIteratorLeft) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
std::vector<SequenceNumber> snapshots{9, 19}; |
|
||||||
CompactionRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, snapshots); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
Slice start("_"); |
|
||||||
Slice end("__"); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, CompactionAggregatorEmptyIteratorRight) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
std::vector<SequenceNumber> snapshots{9, 19}; |
|
||||||
CompactionRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, snapshots); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
Slice start("p"); |
|
||||||
Slice end("q"); |
|
||||||
auto range_del_compaction_iter1 = |
|
||||||
range_del_agg.NewIterator(&start, &end, false /* end_key_inclusive */); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter1.get(), {}); |
|
||||||
|
|
||||||
auto range_del_compaction_iter2 = |
|
||||||
range_del_agg.NewIterator(&start, &end, true /* end_key_inclusive */); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter2.get(), {}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, CompactionAggregatorBoundedIterator) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "e", 10}, {"c", "g", 8}}, |
|
||||||
{{"a", "b", 20}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
std::vector<SequenceNumber> snapshots{9, 19}; |
|
||||||
CompactionRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, snapshots); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
Slice start("bb"); |
|
||||||
Slice end("e"); |
|
||||||
auto range_del_compaction_iter1 = |
|
||||||
range_del_agg.NewIterator(&start, &end, false /* end_key_inclusive */); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter1.get(), |
|
||||||
{{"a", "c", 10}, {"c", "e", 10}, {"c", "e", 8}}); |
|
||||||
|
|
||||||
auto range_del_compaction_iter2 = |
|
||||||
range_del_agg.NewIterator(&start, &end, true /* end_key_inclusive */); |
|
||||||
VerifyFragmentedRangeDels( |
|
||||||
range_del_compaction_iter2.get(), |
|
||||||
{{"a", "c", 10}, {"c", "e", 10}, {"c", "e", 8}, {"e", "g", 8}}); |
|
||||||
} |
|
||||||
|
|
||||||
TEST_F(RangeDelAggregatorV2Test, |
|
||||||
CompactionAggregatorBoundedIteratorExtraFragments) { |
|
||||||
auto fragment_lists = MakeFragmentedTombstoneLists( |
|
||||||
{{{"a", "d", 10}, {"c", "g", 8}}, |
|
||||||
{{"b", "c", 20}, {"d", "f", 30}, {"h", "i", 25}, {"ii", "j", 15}}}); |
|
||||||
|
|
||||||
std::vector<SequenceNumber> snapshots{9, 19}; |
|
||||||
CompactionRangeDelAggregatorV2 range_del_agg(&bytewise_icmp, snapshots); |
|
||||||
for (const auto& fragment_list : fragment_lists) { |
|
||||||
std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter( |
|
||||||
new FragmentedRangeTombstoneIterator(fragment_list.get(), bytewise_icmp, |
|
||||||
kMaxSequenceNumber)); |
|
||||||
range_del_agg.AddTombstones(std::move(input_iter)); |
|
||||||
} |
|
||||||
|
|
||||||
Slice start("bb"); |
|
||||||
Slice end("e"); |
|
||||||
auto range_del_compaction_iter1 = |
|
||||||
range_del_agg.NewIterator(&start, &end, false /* end_key_inclusive */); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter1.get(), {{"a", "b", 10}, |
|
||||||
{"b", "c", 20}, |
|
||||||
{"b", "c", 10}, |
|
||||||
{"c", "d", 10}, |
|
||||||
{"c", "d", 8}, |
|
||||||
{"d", "f", 30}, |
|
||||||
{"d", "f", 8}, |
|
||||||
{"f", "g", 8}}); |
|
||||||
|
|
||||||
auto range_del_compaction_iter2 = |
|
||||||
range_del_agg.NewIterator(&start, &end, true /* end_key_inclusive */); |
|
||||||
VerifyFragmentedRangeDels(range_del_compaction_iter2.get(), {{"a", "b", 10}, |
|
||||||
{"b", "c", 20}, |
|
||||||
{"b", "c", 10}, |
|
||||||
{"c", "d", 10}, |
|
||||||
{"c", "d", 8}, |
|
||||||
{"d", "f", 30}, |
|
||||||
{"d", "f", 8}, |
|
||||||
{"f", "g", 8}}); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace rocksdb
|
|
||||||
|
|
||||||
int main(int argc, char** argv) { |
|
||||||
::testing::InitGoogleTest(&argc, argv); |
|
||||||
return RUN_ALL_TESTS(); |
|
||||||
} |
|
Loading…
Reference in new issue