/src/rocksdb/db/range_del_aggregator.cc
Line | Count | Source |
1 | | // Copyright (c) 2018-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | |
6 | | #include "db/range_del_aggregator.h" |
7 | | |
8 | | #include "db/compaction/compaction_iteration_stats.h" |
9 | | #include "db/dbformat.h" |
10 | | #include "db/pinned_iterators_manager.h" |
11 | | #include "db/range_tombstone_fragmenter.h" |
12 | | #include "db/version_edit.h" |
13 | | #include "rocksdb/comparator.h" |
14 | | #include "rocksdb/types.h" |
15 | | #include "table/internal_iterator.h" |
16 | | #include "table/table_builder.h" |
17 | | #include "util/heap.h" |
18 | | #include "util/kv_map.h" |
19 | | #include "util/vector_iterator.h" |
20 | | |
21 | | namespace ROCKSDB_NAMESPACE { |
22 | | |
23 | | TruncatedRangeDelIterator::TruncatedRangeDelIterator( |
24 | | std::unique_ptr<FragmentedRangeTombstoneIterator> iter, |
25 | | const InternalKeyComparator* icmp, const InternalKey* smallest, |
26 | | const InternalKey* largest) |
27 | 18 | : iter_(std::move(iter)), |
28 | 18 | icmp_(icmp), |
29 | 18 | smallest_ikey_(smallest), |
30 | 18 | largest_ikey_(largest) { |
31 | | // Set up bounds such that range tombstones from this iterator are |
32 | | // truncated to range [smallest_, largest_). |
33 | 18 | if (smallest != nullptr) { |
34 | 6 | pinned_bounds_.emplace_back(); |
35 | 6 | auto& parsed_smallest = pinned_bounds_.back(); |
36 | 6 | Status pik_status = ParseInternalKey(smallest->Encode(), &parsed_smallest, |
37 | 6 | false /* log_err_key */); // TODO |
38 | 6 | pik_status.PermitUncheckedError(); |
39 | 6 | parsed_smallest.type = kTypeMaxValid; |
40 | 6 | assert(pik_status.ok()); |
41 | 6 | smallest_ = &parsed_smallest; |
42 | 6 | } |
43 | 18 | if (largest != nullptr) { |
44 | 6 | pinned_bounds_.emplace_back(); |
45 | 6 | auto& parsed_largest = pinned_bounds_.back(); |
46 | | |
47 | 6 | Status pik_status = ParseInternalKey(largest->Encode(), &parsed_largest, |
48 | 6 | false /* log_err_key */); // TODO |
49 | 6 | pik_status.PermitUncheckedError(); |
50 | 6 | assert(pik_status.ok()); |
51 | | |
52 | 6 | if (parsed_largest.type == kTypeRangeDeletion && |
53 | 5 | parsed_largest.sequence == kMaxSequenceNumber) { |
54 | | // The file boundary has been artificially extended by a range tombstone. |
55 | | // We do not need to adjust largest to properly truncate range |
56 | | // tombstones that extend past the boundary. |
57 | 5 | } else if (parsed_largest.sequence == 0) { |
58 | | // The largest key in the sstable has a sequence number of 0. Since we |
59 | | // guarantee that no internal keys with the same user key and sequence |
60 | | // number can exist in a DB, we know that the largest key in this sstable |
61 | | // cannot exist as the smallest key in the next sstable. This further |
62 | | // implies that no range tombstone in this sstable covers largest; |
63 | | // otherwise, the file boundary would have been artificially extended. |
64 | | // |
65 | | // Therefore, we will never truncate a range tombstone at largest, so we |
66 | | // can leave it unchanged. |
67 | | // TODO: maybe use kMaxValid here to ensure range tombstone having |
68 | | // distinct key from point keys. |
69 | 1 | } else { |
70 | | // The same user key may straddle two sstable boundaries. To ensure that |
71 | | // the truncated end key can cover the largest key in this sstable, reduce |
72 | | // its sequence number by 1. |
73 | 1 | parsed_largest.sequence -= 1; |
74 | | // This line is not needed for correctness, but it ensures that the |
75 | | // truncated end key is not covering keys from the next SST file. |
76 | 1 | parsed_largest.type = kTypeMaxValid; |
77 | 1 | } |
78 | 6 | largest_ = &parsed_largest; |
79 | 6 | } |
80 | 18 | } |
81 | | |
82 | 697 | bool TruncatedRangeDelIterator::Valid() const { |
83 | 697 | assert(iter_ != nullptr); |
84 | 697 | return iter_->Valid() && |
85 | 677 | (smallest_ == nullptr || |
86 | 47 | icmp_->Compare(*smallest_, iter_->parsed_end_key()) < 0) && |
87 | 677 | (largest_ == nullptr || |
88 | 47 | icmp_->Compare(iter_->parsed_start_key(), *largest_) < 0); |
89 | 697 | } |
90 | | |
91 | | // NOTE: target is a user key, with timestamp if enabled. |
92 | 5 | void TruncatedRangeDelIterator::Seek(const Slice& target) { |
93 | 5 | if (largest_ != nullptr && |
94 | 0 | icmp_->Compare(*largest_, ParsedInternalKey(target, kMaxSequenceNumber, |
95 | 0 | kTypeRangeDeletion)) <= 0) { |
96 | 0 | iter_->Invalidate(); |
97 | 0 | return; |
98 | 0 | } |
99 | 5 | if (smallest_ != nullptr && |
100 | 0 | icmp_->user_comparator()->Compare(target, smallest_->user_key) < 0) { |
101 | 0 | iter_->Seek(smallest_->user_key); |
102 | 0 | return; |
103 | 0 | } |
104 | 5 | iter_->Seek(target); |
105 | 5 | } |
106 | | |
107 | 0 | void TruncatedRangeDelIterator::SeekInternalKey(const Slice& target) { |
108 | 0 | if (largest_ && icmp_->Compare(*largest_, target) <= 0) { |
109 | 0 | iter_->Invalidate(); |
110 | 0 | return; |
111 | 0 | } |
112 | 0 | if (smallest_ && icmp_->Compare(target, *smallest_) < 0) { |
113 | | // Since target < smallest, target < largest_. |
114 | | // This seek must land on a range tombstone where end_key() > target, |
115 | | // so there is no need to check again. |
116 | 0 | iter_->Seek(smallest_->user_key); |
117 | 0 | } else { |
118 | 0 | iter_->Seek(ExtractUserKey(target)); |
119 | 0 | while (Valid() && icmp_->Compare(end_key(), target) <= 0) { |
120 | 0 | Next(); |
121 | 0 | } |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | // NOTE: target is a user key, with timestamp if enabled. |
126 | 0 | void TruncatedRangeDelIterator::SeekForPrev(const Slice& target) { |
127 | 0 | if (smallest_ != nullptr && |
128 | 0 | icmp_->Compare(ParsedInternalKey(target, 0, kTypeRangeDeletion), |
129 | 0 | *smallest_) < 0) { |
130 | 0 | iter_->Invalidate(); |
131 | 0 | return; |
132 | 0 | } |
133 | 0 | if (largest_ != nullptr && |
134 | 0 | icmp_->user_comparator()->Compare(largest_->user_key, target) < 0) { |
135 | 0 | iter_->SeekForPrev(largest_->user_key); |
136 | 0 | return; |
137 | 0 | } |
138 | 0 | iter_->SeekForPrev(target); |
139 | 0 | } |
140 | | |
141 | 18 | void TruncatedRangeDelIterator::SeekToFirst() { |
142 | 18 | if (smallest_ != nullptr) { |
143 | 6 | iter_->Seek(smallest_->user_key); |
144 | 6 | return; |
145 | 6 | } |
146 | 12 | iter_->SeekToTopFirst(); |
147 | 12 | } |
148 | | |
149 | 0 | void TruncatedRangeDelIterator::SeekToLast() { |
150 | 0 | if (largest_ != nullptr) { |
151 | 0 | iter_->SeekForPrev(largest_->user_key); |
152 | 0 | return; |
153 | 0 | } |
154 | 0 | iter_->SeekToTopLast(); |
155 | 0 | } |
156 | | |
157 | | std::map<SequenceNumber, std::unique_ptr<TruncatedRangeDelIterator>> |
158 | | TruncatedRangeDelIterator::SplitBySnapshot( |
159 | 6 | const std::vector<SequenceNumber>& snapshots) { |
160 | 6 | using FragmentedIterPair = |
161 | 6 | std::pair<const SequenceNumber, |
162 | 6 | std::unique_ptr<FragmentedRangeTombstoneIterator>>; |
163 | | |
164 | 6 | auto split_untruncated_iters = iter_->SplitBySnapshot(snapshots); |
165 | 6 | std::map<SequenceNumber, std::unique_ptr<TruncatedRangeDelIterator>> |
166 | 6 | split_truncated_iters; |
167 | 6 | std::for_each( |
168 | 6 | split_untruncated_iters.begin(), split_untruncated_iters.end(), |
169 | 6 | [&](FragmentedIterPair& iter_pair) { |
170 | 6 | auto truncated_iter = std::make_unique<TruncatedRangeDelIterator>( |
171 | 6 | std::move(iter_pair.second), icmp_, smallest_ikey_, largest_ikey_); |
172 | 6 | split_truncated_iters.emplace(iter_pair.first, |
173 | 6 | std::move(truncated_iter)); |
174 | 6 | }); |
175 | 6 | return split_truncated_iters; |
176 | 6 | } |
177 | | |
178 | | ForwardRangeDelIterator::ForwardRangeDelIterator( |
179 | | const InternalKeyComparator* icmp) |
180 | 5.92k | : icmp_(icmp), |
181 | 5.92k | unused_idx_(0), |
182 | 5.92k | active_seqnums_(SeqMaxComparator()), |
183 | 5.92k | active_iters_(EndKeyMinComparator(icmp)), |
184 | 5.92k | inactive_iters_(StartKeyMinComparator(icmp)) {} |
185 | | |
186 | 150 | bool ForwardRangeDelIterator::ShouldDelete(const ParsedInternalKey& parsed) { |
187 | | // Move active iterators that end before parsed. |
188 | 167 | while (!active_iters_.empty() && |
189 | 146 | icmp_->Compare((*active_iters_.top())->end_key(), parsed) <= 0) { |
190 | 17 | TruncatedRangeDelIterator* iter = PopActiveIter(); |
191 | 26 | do { |
192 | 26 | iter->Next(); |
193 | 26 | } while (iter->Valid() && icmp_->Compare(iter->end_key(), parsed) <= 0); |
194 | 17 | PushIter(iter, parsed); |
195 | 17 | assert(active_iters_.size() == active_seqnums_.size()); |
196 | 17 | } |
197 | | |
198 | | // Move inactive iterators that start before parsed. |
199 | 155 | while (!inactive_iters_.empty() && |
200 | 11 | icmp_->Compare(inactive_iters_.top()->start_key(), parsed) <= 0) { |
201 | 5 | TruncatedRangeDelIterator* iter = PopInactiveIter(); |
202 | 11 | while (iter->Valid() && icmp_->Compare(iter->end_key(), parsed) <= 0) { |
203 | 6 | iter->Next(); |
204 | 6 | } |
205 | 5 | PushIter(iter, parsed); |
206 | 5 | assert(active_iters_.size() == active_seqnums_.size()); |
207 | 5 | } |
208 | | |
209 | 150 | return active_seqnums_.empty() |
210 | 150 | ? false |
211 | 150 | : (*active_seqnums_.begin())->seq() > parsed.sequence; |
212 | 150 | } |
213 | | |
214 | 6 | void ForwardRangeDelIterator::Invalidate() { |
215 | 6 | unused_idx_ = 0; |
216 | 6 | active_iters_.clear(); |
217 | 6 | active_seqnums_.clear(); |
218 | 6 | inactive_iters_.clear(); |
219 | 6 | } |
220 | | |
221 | | ReverseRangeDelIterator::ReverseRangeDelIterator( |
222 | | const InternalKeyComparator* icmp) |
223 | 5.92k | : icmp_(icmp), |
224 | 5.92k | unused_idx_(0), |
225 | 5.92k | active_seqnums_(SeqMaxComparator()), |
226 | 5.92k | active_iters_(StartKeyMaxComparator(icmp)), |
227 | 5.92k | inactive_iters_(EndKeyMaxComparator(icmp)) {} |
228 | | |
229 | 0 | bool ReverseRangeDelIterator::ShouldDelete(const ParsedInternalKey& parsed) { |
230 | | // Move active iterators that start after parsed. |
231 | 0 | while (!active_iters_.empty() && |
232 | 0 | icmp_->Compare(parsed, (*active_iters_.top())->start_key()) < 0) { |
233 | 0 | TruncatedRangeDelIterator* iter = PopActiveIter(); |
234 | 0 | do { |
235 | 0 | iter->Prev(); |
236 | 0 | } while (iter->Valid() && icmp_->Compare(parsed, iter->start_key()) < 0); |
237 | 0 | PushIter(iter, parsed); |
238 | 0 | assert(active_iters_.size() == active_seqnums_.size()); |
239 | 0 | } |
240 | | |
241 | | // Move inactive iterators that end after parsed. |
242 | 0 | while (!inactive_iters_.empty() && |
243 | 0 | icmp_->Compare(parsed, inactive_iters_.top()->end_key()) < 0) { |
244 | 0 | TruncatedRangeDelIterator* iter = PopInactiveIter(); |
245 | 0 | while (iter->Valid() && icmp_->Compare(parsed, iter->start_key()) < 0) { |
246 | 0 | iter->Prev(); |
247 | 0 | } |
248 | 0 | PushIter(iter, parsed); |
249 | 0 | assert(active_iters_.size() == active_seqnums_.size()); |
250 | 0 | } |
251 | |
|
252 | 0 | return active_seqnums_.empty() |
253 | 0 | ? false |
254 | 0 | : (*active_seqnums_.begin())->seq() > parsed.sequence; |
255 | 0 | } |
256 | | |
257 | 156 | void ReverseRangeDelIterator::Invalidate() { |
258 | 156 | unused_idx_ = 0; |
259 | 156 | active_iters_.clear(); |
260 | 156 | active_seqnums_.clear(); |
261 | 156 | inactive_iters_.clear(); |
262 | 156 | } |
263 | | |
264 | | bool RangeDelAggregator::StripeRep::ShouldDelete( |
265 | 150 | const ParsedInternalKey& parsed, RangeDelPositioningMode mode) { |
266 | 150 | if (!InStripe(parsed.sequence) || IsEmpty()) { |
267 | 0 | return false; |
268 | 0 | } |
269 | 150 | switch (mode) { |
270 | 150 | case RangeDelPositioningMode::kForwardTraversal: |
271 | 150 | InvalidateReverseIter(); |
272 | | |
273 | | // Pick up previously unseen iterators. |
274 | 150 | for (auto it = std::next(iters_.begin(), forward_iter_.UnusedIdx()); |
275 | 155 | it != iters_.end(); ++it, forward_iter_.IncUnusedIdx()) { |
276 | 5 | auto& iter = *it; |
277 | 5 | forward_iter_.AddNewIter(iter.get(), parsed); |
278 | 5 | } |
279 | | |
280 | 150 | return forward_iter_.ShouldDelete(parsed); |
281 | 0 | case RangeDelPositioningMode::kBackwardTraversal: |
282 | 0 | InvalidateForwardIter(); |
283 | | |
284 | | // Pick up previously unseen iterators. |
285 | 0 | for (auto it = std::next(iters_.begin(), reverse_iter_.UnusedIdx()); |
286 | 0 | it != iters_.end(); ++it, reverse_iter_.IncUnusedIdx()) { |
287 | 0 | auto& iter = *it; |
288 | 0 | reverse_iter_.AddNewIter(iter.get(), parsed); |
289 | 0 | } |
290 | |
|
291 | 0 | return reverse_iter_.ShouldDelete(parsed); |
292 | 0 | default: |
293 | 0 | assert(false); |
294 | 0 | return false; |
295 | 150 | } |
296 | 150 | } |
297 | | |
298 | | bool RangeDelAggregator::StripeRep::IsRangeOverlapped(const Slice& start, |
299 | 3.07k | const Slice& end) { |
300 | 3.07k | Invalidate(); |
301 | | |
302 | | // Set the internal start/end keys so that: |
303 | | // - if start_ikey has the same user key and sequence number as the |
304 | | // current end key, start_ikey will be considered greater; and |
305 | | // - if end_ikey has the same user key and sequence number as the current |
306 | | // start key, end_ikey will be considered greater. |
307 | 3.07k | ParsedInternalKey start_ikey(start, kMaxSequenceNumber, |
308 | 3.07k | static_cast<ValueType>(0)); |
309 | 3.07k | ParsedInternalKey end_ikey(end, 0, static_cast<ValueType>(0)); |
310 | 3.07k | for (auto& iter : iters_) { |
311 | 0 | bool checked_candidate_tombstones = false; |
312 | 0 | for (iter->SeekForPrev(start); |
313 | 0 | iter->Valid() && icmp_->Compare(iter->start_key(), end_ikey) <= 0; |
314 | 0 | iter->Next()) { |
315 | 0 | checked_candidate_tombstones = true; |
316 | 0 | if (icmp_->Compare(start_ikey, iter->end_key()) < 0 && |
317 | 0 | icmp_->Compare(iter->start_key(), end_ikey) <= 0) { |
318 | 0 | return true; |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | 0 | if (!checked_candidate_tombstones) { |
323 | | // Do an additional check for when the end of the range is the begin |
324 | | // key of a tombstone, which we missed earlier since SeekForPrev'ing |
325 | | // to the start was invalid. |
326 | 0 | iter->SeekForPrev(end); |
327 | 0 | if (iter->Valid() && icmp_->Compare(start_ikey, iter->end_key()) < 0 && |
328 | 0 | icmp_->Compare(iter->start_key(), end_ikey) <= 0) { |
329 | 0 | return true; |
330 | 0 | } |
331 | 0 | } |
332 | 0 | } |
333 | 3.07k | return false; |
334 | 3.07k | } |
335 | | |
336 | | void ReadRangeDelAggregator::AddTombstones( |
337 | | std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
338 | 3.86k | const InternalKey* smallest, const InternalKey* largest) { |
339 | 3.86k | if (input_iter == nullptr || input_iter->empty()) { |
340 | 3.86k | return; |
341 | 3.86k | } |
342 | 0 | rep_.AddTombstones(std::make_unique<TruncatedRangeDelIterator>( |
343 | 0 | std::move(input_iter), icmp_, smallest, largest)); |
344 | 0 | } |
345 | | |
346 | | bool ReadRangeDelAggregator::ShouldDeleteImpl(const ParsedInternalKey& parsed, |
347 | 0 | RangeDelPositioningMode mode) { |
348 | 0 | return rep_.ShouldDelete(parsed, mode); |
349 | 0 | } |
350 | | |
351 | | bool ReadRangeDelAggregator::IsRangeOverlapped(const Slice& start, |
352 | 3.07k | const Slice& end) { |
353 | 3.07k | InvalidateRangeDelMapPositions(); |
354 | 3.07k | return rep_.IsRangeOverlapped(start, end); |
355 | 3.07k | } |
356 | | |
357 | | void CompactionRangeDelAggregator::AddTombstones( |
358 | | std::unique_ptr<FragmentedRangeTombstoneIterator> input_iter, |
359 | 3.71k | const InternalKey* smallest, const InternalKey* largest) { |
360 | 3.71k | if (input_iter == nullptr || input_iter->empty()) { |
361 | 3.70k | return; |
362 | 3.70k | } |
363 | | // This bounds output of CompactionRangeDelAggregator::NewIterator. |
364 | 6 | if (!trim_ts_.empty()) { |
365 | 0 | assert(icmp_->user_comparator()->timestamp_size() > 0); |
366 | 0 | input_iter->SetTimestampUpperBound(&trim_ts_); |
367 | 0 | } |
368 | | |
369 | 6 | assert(input_iter->lower_bound() == 0); |
370 | 6 | assert(input_iter->upper_bound() == kMaxSequenceNumber); |
371 | 6 | parent_iters_.emplace_back(new TruncatedRangeDelIterator( |
372 | 6 | std::move(input_iter), icmp_, smallest, largest)); |
373 | | |
374 | 6 | Slice* ts_upper_bound = nullptr; |
375 | 6 | if (!ts_upper_bound_.empty()) { |
376 | 0 | assert(icmp_->user_comparator()->timestamp_size() > 0); |
377 | 0 | ts_upper_bound = &ts_upper_bound_; |
378 | 0 | } |
379 | 6 | auto split_iters = parent_iters_.back()->SplitBySnapshot(*snapshots_); |
380 | 6 | for (auto& split_iter : split_iters) { |
381 | 6 | auto it = reps_.find(split_iter.first); |
382 | 6 | if (it == reps_.end()) { |
383 | 6 | bool inserted; |
384 | 6 | SequenceNumber upper_bound = split_iter.second->upper_bound(); |
385 | 6 | SequenceNumber lower_bound = split_iter.second->lower_bound(); |
386 | 6 | std::tie(it, inserted) = reps_.emplace( |
387 | 6 | split_iter.first, StripeRep(icmp_, upper_bound, lower_bound)); |
388 | 6 | assert(inserted); |
389 | 6 | } |
390 | 6 | assert(it != reps_.end()); |
391 | | // ts_upper_bound is used to bound ShouldDelete() to only consider |
392 | | // range tombstones under full_history_ts_low_ and trim_ts_. Keys covered by |
393 | | // range tombstones that are above full_history_ts_low_ should not be |
394 | | // dropped prematurely: user may read with a timestamp between the range |
395 | | // tombstone and the covered key. Note that we cannot set timestamp |
396 | | // upperbound on the original `input_iter` since `input_iter`s are later |
397 | | // used in CompactionRangeDelAggregator::NewIterator to output range |
398 | | // tombstones for persistence. We do not want to only persist range |
399 | | // tombstones with timestamp lower than ts_upper_bound. |
400 | 6 | split_iter.second->SetTimestampUpperBound(ts_upper_bound); |
401 | 6 | it->second.AddTombstones(std::move(split_iter.second)); |
402 | 6 | } |
403 | 6 | } |
404 | | |
405 | | bool CompactionRangeDelAggregator::ShouldDelete(const ParsedInternalKey& parsed, |
406 | 9.02k | RangeDelPositioningMode mode) { |
407 | 9.02k | auto it = reps_.lower_bound(parsed.sequence); |
408 | 9.02k | if (it == reps_.end()) { |
409 | 8.87k | return false; |
410 | 8.87k | } |
411 | 150 | return it->second.ShouldDelete(parsed, mode); |
412 | 9.02k | } |
413 | | |
414 | | namespace { |
415 | | |
416 | | // Produce a sorted (by start internal key) stream of range tombstones from |
417 | | // `children`. lower_bound and upper_bound on internal key can be |
418 | | // optionally specified. Range tombstones that ends before lower_bound or starts |
419 | | // after upper_bound are excluded. |
420 | | // If user-defined timestamp is enabled, lower_bound and upper_bound should |
421 | | // contain timestamp. |
422 | | class TruncatedRangeDelMergingIter : public InternalIterator { |
423 | | public: |
424 | | TruncatedRangeDelMergingIter( |
425 | | const InternalKeyComparator* icmp, const Slice* lower_bound, |
426 | | const Slice* upper_bound, |
427 | | const std::vector<std::unique_ptr<TruncatedRangeDelIterator>>& children) |
428 | 7.25k | : icmp_(icmp), |
429 | 7.25k | lower_bound_(lower_bound), |
430 | 7.25k | upper_bound_(upper_bound), |
431 | 7.25k | heap_(StartKeyMinComparator(icmp)), |
432 | 7.25k | ts_sz_(icmp_->user_comparator()->timestamp_size()) { |
433 | 7.25k | for (auto& child : children) { |
434 | 6 | if (child != nullptr) { |
435 | 6 | assert(child->lower_bound() == 0); |
436 | 6 | assert(child->upper_bound() == kMaxSequenceNumber); |
437 | 6 | children_.push_back(child.get()); |
438 | 6 | } |
439 | 6 | } |
440 | 7.25k | } |
441 | | |
442 | 15.0k | bool Valid() const override { |
443 | 15.0k | return !heap_.empty() && !AfterEndKey(heap_.top()); |
444 | 15.0k | } |
445 | 0 | Status status() const override { return Status::OK(); } |
446 | | |
447 | 14.5k | void SeekToFirst() override { |
448 | 14.5k | heap_.clear(); |
449 | 14.5k | for (auto& child : children_) { |
450 | 12 | if (lower_bound_ != nullptr) { |
451 | 0 | child->Seek(ExtractUserKey(*lower_bound_)); |
452 | | // Since the above `Seek()` operates on a user key while `lower_bound_` |
453 | | // is an internal key, we may need to advance `child` farther for it to |
454 | | // be in bounds. |
455 | 0 | while (child->Valid() && BeforeStartKey(child)) { |
456 | 0 | child->InternalNext(); |
457 | 0 | } |
458 | 12 | } else { |
459 | 12 | child->SeekToFirst(); |
460 | 12 | } |
461 | 12 | if (child->Valid()) { |
462 | 12 | heap_.push(child); |
463 | 12 | } |
464 | 12 | } |
465 | 14.5k | } |
466 | | |
467 | 568 | void Next() override { |
468 | 568 | auto* top = heap_.top(); |
469 | 568 | top->InternalNext(); |
470 | 568 | if (top->Valid()) { |
471 | 556 | heap_.replace_top(top); |
472 | 556 | } else { |
473 | 12 | heap_.pop(); |
474 | 12 | } |
475 | 568 | } |
476 | | |
477 | 1.13k | Slice key() const override { |
478 | 1.13k | auto* top = heap_.top(); |
479 | 1.13k | if (ts_sz_) { |
480 | 0 | cur_start_key_.Set(top->start_key().user_key, top->seq(), |
481 | 0 | kTypeRangeDeletion, top->timestamp()); |
482 | 1.13k | } else { |
483 | 1.13k | cur_start_key_.Set(top->start_key().user_key, top->seq(), |
484 | 1.13k | kTypeRangeDeletion); |
485 | 1.13k | } |
486 | 1.13k | assert(top->start_key().user_key.size() >= ts_sz_); |
487 | 1.13k | return cur_start_key_.Encode(); |
488 | 1.13k | } |
489 | | |
490 | 568 | Slice value() const override { |
491 | 568 | auto* top = heap_.top(); |
492 | 568 | if (!ts_sz_) { |
493 | 568 | return top->end_key().user_key; |
494 | 568 | } |
495 | 568 | assert(top->timestamp().size() == ts_sz_); |
496 | 0 | cur_end_key_.clear(); |
497 | 0 | cur_end_key_.append(top->end_key().user_key.data(), |
498 | 0 | top->end_key().user_key.size() - ts_sz_); |
499 | 0 | cur_end_key_.append(top->timestamp().data(), ts_sz_); |
500 | 0 | return cur_end_key_; |
501 | 568 | } |
502 | | |
503 | | // Unused InternalIterator methods |
504 | 0 | void Prev() override { assert(false); } |
505 | 0 | void Seek(const Slice& /* target */) override { assert(false); } |
506 | 0 | void SeekForPrev(const Slice& /* target */) override { assert(false); } |
507 | 0 | void SeekToLast() override { assert(false); } |
508 | | |
509 | | private: |
510 | 0 | bool BeforeStartKey(const TruncatedRangeDelIterator* iter) const { |
511 | 0 | if (lower_bound_ == nullptr) { |
512 | 0 | return false; |
513 | 0 | } |
514 | 0 | return icmp_->Compare(iter->end_key(), *lower_bound_) <= 0; |
515 | 0 | } |
516 | | |
517 | 568 | bool AfterEndKey(const TruncatedRangeDelIterator* iter) const { |
518 | 568 | if (upper_bound_ == nullptr) { |
519 | 568 | return false; |
520 | 568 | } |
521 | 0 | return icmp_->Compare(iter->start_key(), *upper_bound_) > 0; |
522 | 568 | } |
523 | | |
524 | | const InternalKeyComparator* icmp_; |
525 | | const Slice* lower_bound_; |
526 | | const Slice* upper_bound_; |
527 | | BinaryHeap<TruncatedRangeDelIterator*, StartKeyMinComparator> heap_; |
528 | | std::vector<TruncatedRangeDelIterator*> children_; |
529 | | |
530 | | mutable InternalKey cur_start_key_; |
531 | | mutable std::string cur_end_key_; |
532 | | size_t ts_sz_; |
533 | | }; |
534 | | |
535 | | } // anonymous namespace |
536 | | |
537 | | std::unique_ptr<FragmentedRangeTombstoneIterator> |
538 | | CompactionRangeDelAggregator::NewIterator(const Slice* lower_bound, |
539 | 7.25k | const Slice* upper_bound) { |
540 | 7.25k | InvalidateRangeDelMapPositions(); |
541 | 7.25k | auto merging_iter = std::make_unique<TruncatedRangeDelMergingIter>( |
542 | 7.25k | icmp_, lower_bound, upper_bound, parent_iters_); |
543 | | |
544 | 7.25k | auto fragmented_tombstone_list = |
545 | 7.25k | std::make_shared<FragmentedRangeTombstoneList>( |
546 | 7.25k | std::move(merging_iter), *icmp_, true /* for_compaction */, |
547 | 7.25k | *snapshots_); |
548 | | |
549 | 7.25k | return std::make_unique<FragmentedRangeTombstoneIterator>( |
550 | 7.25k | fragmented_tombstone_list, *icmp_, kMaxSequenceNumber /* upper_bound */); |
551 | 7.25k | } |
552 | | |
553 | | } // namespace ROCKSDB_NAMESPACE |