/src/rocksdb/table/merging_iterator.cc
Line | Count | Source |
1 | | // Copyright (c) 2011-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 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
7 | | // Use of this source code is governed by a BSD-style license that can be |
8 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
9 | | |
10 | | #include "table/merging_iterator.h" |
11 | | |
12 | | #include "db/arena_wrapped_db_iter.h" |
13 | | #include "monitoring/file_read_sample.h" |
14 | | #include "test_util/sync_point.h" |
15 | | |
16 | | namespace ROCKSDB_NAMESPACE { |
17 | | // MergingIterator uses a min/max heap to combine data from point iterators. |
18 | | // Range tombstones can be added and keys covered by range tombstones will be |
19 | | // skipped. |
20 | | // |
21 | | // The following are implementation details and can be ignored by user. |
22 | | // For merging iterator to process range tombstones, it treats the start and end |
23 | | // keys of a range tombstone as two keys and put them into minHeap_ or maxHeap_ |
24 | | // together with regular point keys. Each range tombstone is active only within |
25 | | // its internal key range [start_key, end_key). An `active_` set is used to |
26 | | // track levels that have an active range tombstone. Take forward scanning |
27 | | // for example. Level j is in active_ if its current range tombstone has its |
28 | | // start_key popped from minHeap_ and its end_key in minHeap_. If the top of |
29 | | // minHeap_ is a point key from level L, we can determine if the point key is |
30 | | // covered by any range tombstone by checking if there is an l <= L in active_. |
31 | | // The case of l == L also involves checking range tombstone's sequence number. |
32 | | // |
33 | | // The following (non-exhaustive) list of invariants are maintained by |
34 | | // MergingIterator during forward scanning. After each InternalIterator API, |
35 | | // i.e., Seek*() and Next(), and FindNextVisibleKey(), if minHeap_ is not empty: |
36 | | // (1) minHeap_.top().type == ITERATOR |
37 | | // (2) minHeap_.top()->key() is not covered by any range tombstone. |
38 | | // |
39 | | // After each call to SeekImpl() in addition to the functions mentioned above: |
40 | | // (3) For all level i and j <= i, range_tombstone_iters_[j].prev.end_key() < |
41 | | // children_[i].iter.key(). That is, range_tombstone_iters_[j] is at or before |
42 | | // the first range tombstone from level j with end_key() > |
43 | | // children_[i].iter.key(). |
44 | | // (4) For all level i and j <= i, if j in active_, then |
45 | | // range_tombstone_iters_[j]->start_key() < children_[i].iter.key(). |
46 | | // - When range_tombstone_iters_[j] is !Valid(), we consider its `prev` to be |
47 | | // the last range tombstone from that range tombstone iterator. |
48 | | // - When referring to range tombstone start/end keys, assume it is the value of |
49 | | // HeapItem::tombstone_pik. This value has op_type = kMaxValid, which makes |
50 | | // range tombstone keys have distinct values from point keys. |
51 | | // |
52 | | // Applicable class variables have their own (forward scanning) invariants |
53 | | // listed in the comments above their definition. |
54 | | class MergingIterator : public InternalIterator { |
55 | | public: |
56 | | MergingIterator(const InternalKeyComparator* comparator, |
57 | | InternalIterator** children, int n, bool is_arena_mode, |
58 | | bool prefix_seek_mode, |
59 | | const Slice* iterate_upper_bound = nullptr) |
60 | 16.1k | : is_arena_mode_(is_arena_mode), |
61 | 16.1k | prefix_seek_mode_(prefix_seek_mode), |
62 | 16.1k | direction_(kForward), |
63 | 16.1k | comparator_(comparator), |
64 | 16.1k | current_(nullptr), |
65 | 16.1k | minHeap_(MinHeapItemComparator(comparator_)), |
66 | 16.1k | pinned_iters_mgr_(nullptr), |
67 | 16.1k | iterate_upper_bound_(iterate_upper_bound) { |
68 | 16.1k | children_.resize(n); |
69 | 16.1k | for (int i = 0; i < n; i++) { |
70 | 0 | children_[i].level = i; |
71 | 0 | children_[i].iter.Set(children[i]); |
72 | 0 | } |
73 | 16.1k | } |
74 | | |
75 | 28.1k | void considerStatus(Status s) { |
76 | 28.1k | if (!s.ok() && status_.ok()) { |
77 | 0 | status_ = s; |
78 | 0 | } |
79 | 28.1k | } |
80 | | |
81 | 26.4k | virtual void AddIterator(InternalIterator* iter) { |
82 | 26.4k | children_.emplace_back(children_.size(), iter); |
83 | 26.4k | if (pinned_iters_mgr_) { |
84 | 0 | iter->SetPinnedItersMgr(pinned_iters_mgr_); |
85 | 0 | } |
86 | | // Invalidate to ensure `Seek*()` is called to construct the heaps before |
87 | | // use. |
88 | 26.4k | current_ = nullptr; |
89 | 26.4k | } |
90 | | |
91 | | // There must be either no range tombstone iterator or the same number of |
92 | | // range tombstone iterators as point iterators after all iters are added. |
93 | | // The i-th added range tombstone iterator and the i-th point iterator |
94 | | // must point to the same LSM level. |
95 | | // Merging iterator takes ownership of `iter` and is responsible for freeing |
96 | | // it. One exception to this is when a LevelIterator moves to a different SST |
97 | | // file or when Iterator::Refresh() is called, the range tombstone iterator |
98 | | // could be updated. In that case, this merging iterator is only responsible |
99 | | // for freeing the new range tombstone iterator that it has pointers to in |
100 | | // range_tombstone_iters_. |
101 | | void AddRangeTombstoneIterator( |
102 | 17.7k | std::unique_ptr<TruncatedRangeDelIterator>&& iter) { |
103 | 17.7k | range_tombstone_iters_.emplace_back(std::move(iter)); |
104 | 17.7k | } |
105 | | |
106 | | // Called by MergingIteratorBuilder when all point iterators and range |
107 | | // tombstone iterators are added. Initializes HeapItems for range tombstone |
108 | | // iterators. |
109 | 9.70k | void Finish() { |
110 | 9.70k | if (!range_tombstone_iters_.empty()) { |
111 | 6.34k | assert(range_tombstone_iters_.size() == children_.size()); |
112 | 6.34k | pinned_heap_item_.resize(range_tombstone_iters_.size()); |
113 | 24.0k | for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) { |
114 | 17.7k | pinned_heap_item_[i].level = i; |
115 | | // Range tombstone end key is exclusive. If a point internal key has the |
116 | | // same user key and sequence number as the start or end key of a range |
117 | | // tombstone, the order will be start < end key < internal key with the |
118 | | // following op_type change. This is helpful to ensure keys popped from |
119 | | // heap are in expected order since range tombstone start/end keys will |
120 | | // be distinct from point internal keys. Strictly speaking, this is only |
121 | | // needed for tombstone end points that are truncated in |
122 | | // TruncatedRangeDelIterator since untruncated tombstone end points |
123 | | // always have kMaxSequenceNumber and kTypeRangeDeletion (see |
124 | | // TruncatedRangeDelIterator::start_key()/end_key()). |
125 | 17.7k | pinned_heap_item_[i].tombstone_pik.type = kTypeMaxValid; |
126 | 17.7k | } |
127 | 6.34k | } |
128 | 9.70k | } |
129 | | |
130 | 16.1k | ~MergingIterator() override { |
131 | 16.1k | range_tombstone_iters_.clear(); |
132 | | |
133 | 26.4k | for (auto& child : children_) { |
134 | 26.4k | child.iter.DeleteIter(is_arena_mode_); |
135 | 26.4k | } |
136 | 16.1k | status_.PermitUncheckedError(); |
137 | 16.1k | } |
138 | | |
139 | 0 | void SetRangeDelReadSeqno(SequenceNumber read_seqno) override { |
140 | 0 | for (auto& child : children_) { |
141 | | // This should only be needed for LevelIterator (iterators from L1+). |
142 | 0 | child.iter.SetRangeDelReadSeqno(read_seqno); |
143 | 0 | } |
144 | 0 | for (auto& child : range_tombstone_iters_) { |
145 | 0 | if (child) { |
146 | 0 | child->SetRangeDelReadSeqno(read_seqno); |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | 81.7k | bool Valid() const override { return current_ != nullptr && status_.ok(); } |
152 | | |
153 | 6.87k | Status status() const override { return status_; } |
154 | | |
155 | | // Add range_tombstone_iters_[level] into min heap. |
156 | | // Updates active_ if the end key of a range tombstone is inserted. |
157 | | // pinned_heap_items_[level].type is updated based on `start_key`. |
158 | | // |
159 | | // If range_tombstone_iters_[level] is after iterate_upper_bound_, |
160 | | // it is removed from the heap. |
161 | | // @param start_key specifies which end point of the range tombstone to add. |
162 | | void InsertRangeTombstoneToMinHeap(size_t level, bool start_key = true, |
163 | 187k | bool replace_top = false) { |
164 | 187k | assert(!range_tombstone_iters_.empty() && |
165 | 187k | range_tombstone_iters_[level]->Valid()); |
166 | | // Maintains Invariant(phi) |
167 | 187k | if (start_key) { |
168 | 93.9k | pinned_heap_item_[level].type = HeapItem::Type::DELETE_RANGE_START; |
169 | 93.9k | ParsedInternalKey pik = range_tombstone_iters_[level]->start_key(); |
170 | | // iterate_upper_bound does not have timestamp |
171 | 93.9k | if (iterate_upper_bound_ && |
172 | 0 | comparator_->user_comparator()->CompareWithoutTimestamp( |
173 | 0 | pik.user_key, true /* a_has_ts */, *iterate_upper_bound_, |
174 | 0 | false /* b_has_ts */) >= 0) { |
175 | 0 | if (replace_top) { |
176 | | // replace_top implies this range tombstone iterator is still in |
177 | | // minHeap_ and at the top. |
178 | 0 | minHeap_.pop(); |
179 | 0 | } |
180 | 0 | return; |
181 | 0 | } |
182 | 93.9k | pinned_heap_item_[level].SetTombstoneKey(std::move(pik)); |
183 | | // Checks Invariant(active_) |
184 | 93.9k | assert(active_.count(level) == 0); |
185 | 93.9k | } else { |
186 | | // allow end key to go over upper bound (if present) since start key is |
187 | | // before upper bound and the range tombstone could still cover a |
188 | | // range before upper bound. |
189 | | // Maintains Invariant(active_) |
190 | 93.9k | pinned_heap_item_[level].SetTombstoneKey( |
191 | 93.9k | range_tombstone_iters_[level]->end_key()); |
192 | 93.9k | pinned_heap_item_[level].type = HeapItem::Type::DELETE_RANGE_END; |
193 | 93.9k | active_.insert(level); |
194 | 93.9k | } |
195 | 187k | if (replace_top) { |
196 | 184k | minHeap_.replace_top(&pinned_heap_item_[level]); |
197 | 184k | } else { |
198 | 2.87k | minHeap_.push(&pinned_heap_item_[level]); |
199 | 2.87k | } |
200 | 187k | } |
201 | | |
202 | | // Add range_tombstone_iters_[level] into max heap. |
203 | | // Updates active_ if the start key of a range tombstone is inserted. |
204 | | // @param end_key specifies which end point of the range tombstone to add. |
205 | | void InsertRangeTombstoneToMaxHeap(size_t level, bool end_key = true, |
206 | 0 | bool replace_top = false) { |
207 | 0 | assert(!range_tombstone_iters_.empty() && |
208 | 0 | range_tombstone_iters_[level]->Valid()); |
209 | 0 | if (end_key) { |
210 | 0 | pinned_heap_item_[level].SetTombstoneKey( |
211 | 0 | range_tombstone_iters_[level]->end_key()); |
212 | 0 | pinned_heap_item_[level].type = HeapItem::Type::DELETE_RANGE_END; |
213 | 0 | assert(active_.count(level) == 0); |
214 | 0 | } else { |
215 | 0 | pinned_heap_item_[level].SetTombstoneKey( |
216 | 0 | range_tombstone_iters_[level]->start_key()); |
217 | 0 | pinned_heap_item_[level].type = HeapItem::Type::DELETE_RANGE_START; |
218 | 0 | active_.insert(level); |
219 | 0 | } |
220 | 0 | if (replace_top) { |
221 | 0 | maxHeap_->replace_top(&pinned_heap_item_[level]); |
222 | 0 | } else { |
223 | 0 | maxHeap_->push(&pinned_heap_item_[level]); |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | | // Remove HeapItems from top of minHeap_ that are of type DELETE_RANGE_START |
228 | | // until minHeap_ is empty or the top of the minHeap_ is not of type |
229 | | // DELETE_RANGE_START. Each such item means a range tombstone becomes active, |
230 | | // so `active_` is updated accordingly. |
231 | 154k | void PopDeleteRangeStart() { |
232 | 248k | while (!minHeap_.empty() && |
233 | 242k | minHeap_.top()->type == HeapItem::Type::DELETE_RANGE_START) { |
234 | 93.9k | TEST_SYNC_POINT_CALLBACK("MergeIterator::PopDeleteRangeStart", nullptr); |
235 | | // Invariant(rti) holds since |
236 | | // range_tombstone_iters_[minHeap_.top()->level] is still valid, and |
237 | | // parameter `replace_top` is set to true here to ensure only one such |
238 | | // HeapItem is in minHeap_. |
239 | 93.9k | InsertRangeTombstoneToMinHeap( |
240 | 93.9k | minHeap_.top()->level, false /* start_key */, true /* replace_top */); |
241 | 93.9k | } |
242 | 154k | } |
243 | | |
244 | | // Remove HeapItems from top of maxHeap_ that are of type DELETE_RANGE_END |
245 | | // until maxHeap_ is empty or the top of the maxHeap_ is not of type |
246 | | // DELETE_RANGE_END. Each such item means a range tombstone becomes active, |
247 | | // so `active_` is updated accordingly. |
248 | 17.5k | void PopDeleteRangeEnd() { |
249 | 17.5k | while (!maxHeap_->empty() && |
250 | 14.5k | maxHeap_->top()->type == HeapItem::Type::DELETE_RANGE_END) { |
251 | | // insert start key of this range tombstone and updates active_ |
252 | 0 | InsertRangeTombstoneToMaxHeap(maxHeap_->top()->level, false /* end_key */, |
253 | 0 | true /* replace_top */); |
254 | 0 | } |
255 | 17.5k | } |
256 | | |
257 | 6.67k | void SeekToFirst() override { |
258 | 6.67k | ClearHeaps(); |
259 | 6.67k | status_ = Status::OK(); |
260 | 16.5k | for (auto& child : children_) { |
261 | 16.5k | child.iter.SeekToFirst(); |
262 | 16.5k | AddToMinHeapOrCheckStatus(&child); |
263 | 16.5k | } |
264 | | |
265 | 17.8k | for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) { |
266 | 11.2k | if (range_tombstone_iters_[i]) { |
267 | 2.87k | range_tombstone_iters_[i]->SeekToFirst(); |
268 | 2.87k | if (range_tombstone_iters_[i]->Valid()) { |
269 | | // It is possible to be invalid due to snapshots. |
270 | 2.87k | InsertRangeTombstoneToMinHeap(i); |
271 | 2.87k | } |
272 | 2.87k | } |
273 | 11.2k | } |
274 | 6.67k | FindNextVisibleKey(); |
275 | 6.67k | direction_ = kForward; |
276 | 6.67k | current_ = CurrentForward(); |
277 | 6.67k | } |
278 | | |
279 | 0 | void SeekToLast() override { |
280 | 0 | ClearHeaps(); |
281 | 0 | InitMaxHeap(); |
282 | 0 | status_ = Status::OK(); |
283 | 0 | for (auto& child : children_) { |
284 | 0 | child.iter.SeekToLast(); |
285 | 0 | AddToMaxHeapOrCheckStatus(&child); |
286 | 0 | } |
287 | |
|
288 | 0 | for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) { |
289 | 0 | if (range_tombstone_iters_[i]) { |
290 | 0 | range_tombstone_iters_[i]->SeekToLast(); |
291 | 0 | if (range_tombstone_iters_[i]->Valid()) { |
292 | | // It is possible to be invalid due to snapshots. |
293 | 0 | InsertRangeTombstoneToMaxHeap(i); |
294 | 0 | } |
295 | 0 | } |
296 | 0 | } |
297 | 0 | FindPrevVisibleKey(); |
298 | 0 | direction_ = kReverse; |
299 | 0 | current_ = CurrentReverse(); |
300 | 0 | } |
301 | | |
302 | | // Position this merging iterator at the first key >= target (internal key). |
303 | | // If range tombstones are present, keys covered by range tombstones are |
304 | | // skipped, and this merging iter points to the first non-range-deleted key >= |
305 | | // target after Seek(). If !Valid() and status().ok() then this iterator |
306 | | // reaches the end. |
307 | | // |
308 | | // If range tombstones are present, cascading seeks may be called (an |
309 | | // optimization adapted from Pebble https://github.com/cockroachdb/pebble). |
310 | | // Roughly, if there is a range tombstone [start, end) that covers the |
311 | | // target user key at level L, then this range tombstone must cover the range |
312 | | // [target key, end) in all levels > L. So for all levels > L, we can pretend |
313 | | // the target key is `end`. This optimization is applied at each level and |
314 | | // hence the name "cascading seek". |
315 | 702 | void Seek(const Slice& target) override { |
316 | | // Define LevelNextVisible(i, k) to be the first key >= k in level i that is |
317 | | // not covered by any range tombstone. |
318 | | // After SeekImpl(target, 0), invariants (3) and (4) hold. |
319 | | // For all level i, target <= children_[i].iter.key() <= LevelNextVisible(i, |
320 | | // target). By the contract of FindNextVisibleKey(), Invariants (1)-(4) |
321 | | // holds after this call, and minHeap_.top().iter points to the |
322 | | // first key >= target among children_ that is not covered by any range |
323 | | // tombstone. |
324 | 702 | status_ = Status::OK(); |
325 | 702 | SeekImpl(target); |
326 | 702 | FindNextVisibleKey(); |
327 | | |
328 | 702 | direction_ = kForward; |
329 | 702 | { |
330 | 702 | PERF_TIMER_GUARD(seek_min_heap_time); |
331 | 702 | current_ = CurrentForward(); |
332 | 702 | } |
333 | 702 | } |
334 | | |
335 | 3.03k | void SeekForPrev(const Slice& target) override { |
336 | 3.03k | assert(range_tombstone_iters_.empty() || |
337 | 3.03k | range_tombstone_iters_.size() == children_.size()); |
338 | 3.03k | status_ = Status::OK(); |
339 | 3.03k | SeekForPrevImpl(target); |
340 | 3.03k | FindPrevVisibleKey(); |
341 | | |
342 | 3.03k | direction_ = kReverse; |
343 | 3.03k | { |
344 | 3.03k | PERF_TIMER_GUARD(seek_max_heap_time); |
345 | 3.03k | current_ = CurrentReverse(); |
346 | 3.03k | } |
347 | 3.03k | } |
348 | | |
349 | 50.6k | void Next() override { |
350 | 50.6k | assert(Valid()); |
351 | | // Ensure that all children are positioned after key(). |
352 | | // If we are moving in the forward direction, it is already |
353 | | // true for all the non-current children since current_ is |
354 | | // the smallest child and key() == current_->key(). |
355 | 50.6k | if (direction_ != kForward) { |
356 | | // The loop advanced all non-current children to be > key() so current_ |
357 | | // should still be strictly the smallest key. |
358 | 0 | SwitchToForward(); |
359 | 0 | } |
360 | | |
361 | | // For the heap modifications below to be correct, current_ must be the |
362 | | // current top of the heap. |
363 | 50.6k | assert(current_ == CurrentForward()); |
364 | | // as the current points to the current record. move the iterator forward. |
365 | 50.6k | current_->Next(); |
366 | 50.6k | if (current_->Valid()) { |
367 | | // current is still valid after the Next() call above. Call |
368 | | // replace_top() to restore the heap property. When the same child |
369 | | // iterator yields a sequence of keys, this is cheap. |
370 | 42.4k | assert(current_->status().ok()); |
371 | 42.4k | minHeap_.replace_top(minHeap_.top()); |
372 | 42.4k | } else { |
373 | | // current stopped being valid, remove it from the heap. |
374 | 8.16k | considerStatus(current_->status()); |
375 | 8.16k | minHeap_.pop(); |
376 | 8.16k | } |
377 | | // Invariants (3) and (4) hold when after advancing current_. |
378 | | // Let k be the smallest key among children_[i].iter.key(). |
379 | | // k <= children_[i].iter.key() <= LevelNextVisible(i, k) holds for all |
380 | | // level i. After FindNextVisible(), Invariants (1)-(4) hold and |
381 | | // minHeap_.top()->key() is the first key >= k from any children_ that is |
382 | | // not covered by any range tombstone. |
383 | 50.6k | FindNextVisibleKey(); |
384 | 50.6k | current_ = CurrentForward(); |
385 | 50.6k | } |
386 | | |
387 | 50.6k | bool NextAndGetResult(IterateResult* result) override { |
388 | 50.6k | Next(); |
389 | 50.6k | bool is_valid = Valid(); |
390 | 50.6k | if (is_valid) { |
391 | 44.9k | result->key = key(); |
392 | 44.9k | result->bound_check_result = UpperBoundCheckResult(); |
393 | 44.9k | result->value_prepared = current_->IsValuePrepared(); |
394 | 44.9k | } |
395 | 50.6k | return is_valid; |
396 | 50.6k | } |
397 | | |
398 | 10.9k | void Prev() override { |
399 | 10.9k | assert(Valid()); |
400 | | // Ensure that all children are positioned before key(). |
401 | | // If we are moving in the reverse direction, it is already |
402 | | // true for all the non-current children since current_ is |
403 | | // the largest child and key() == current_->key(). |
404 | 10.9k | if (direction_ != kReverse) { |
405 | | // Otherwise, retreat the non-current children. We retreat current_ |
406 | | // just after the if-block. |
407 | 463 | SwitchToBackward(); |
408 | 463 | } |
409 | | |
410 | | // For the heap modifications below to be correct, current_ must be the |
411 | | // current top of the heap. |
412 | 10.9k | assert(current_ == CurrentReverse()); |
413 | 10.9k | current_->Prev(); |
414 | 10.9k | if (current_->Valid()) { |
415 | | // current is still valid after the Prev() call above. Call |
416 | | // replace_top() to restore the heap property. When the same child |
417 | | // iterator yields a sequence of keys, this is cheap. |
418 | 6.32k | assert(current_->status().ok()); |
419 | 6.32k | maxHeap_->replace_top(maxHeap_->top()); |
420 | 6.32k | } else { |
421 | | // current stopped being valid, remove it from the heap. |
422 | 4.66k | considerStatus(current_->status()); |
423 | 4.66k | maxHeap_->pop(); |
424 | 4.66k | } |
425 | 10.9k | FindPrevVisibleKey(); |
426 | 10.9k | current_ = CurrentReverse(); |
427 | 10.9k | } |
428 | | |
429 | 78.6k | Slice key() const override { |
430 | 78.6k | assert(Valid()); |
431 | 78.6k | return current_->key(); |
432 | 78.6k | } |
433 | | |
434 | 44.1k | uint64_t write_unix_time() const override { |
435 | 44.1k | assert(Valid()); |
436 | 44.1k | return current_->write_unix_time(); |
437 | 44.1k | } |
438 | | |
439 | 44.1k | Slice value() const override { |
440 | 44.1k | assert(Valid()); |
441 | 44.1k | return current_->value(); |
442 | 44.1k | } |
443 | | |
444 | 15.8k | bool PrepareValue() override { |
445 | 15.8k | assert(Valid()); |
446 | 15.8k | if (current_->PrepareValue()) { |
447 | 15.8k | return true; |
448 | 15.8k | } |
449 | | |
450 | 0 | considerStatus(current_->status()); |
451 | 0 | assert(!status_.ok()); |
452 | 0 | return false; |
453 | 15.8k | } |
454 | | |
455 | | // Here we simply relay MayBeOutOfLowerBound/MayBeOutOfUpperBound result |
456 | | // from current child iterator. Potentially as long as one of child iterator |
457 | | // report out of bound is not possible, we know current key is within bound. |
458 | 0 | bool MayBeOutOfLowerBound() override { |
459 | 0 | assert(Valid()); |
460 | 0 | return current_->MayBeOutOfLowerBound(); |
461 | 0 | } |
462 | | |
463 | 44.9k | IterBoundCheck UpperBoundCheckResult() override { |
464 | 44.9k | assert(Valid()); |
465 | 44.9k | return current_->UpperBoundCheckResult(); |
466 | 44.9k | } |
467 | | |
468 | 9.70k | void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override { |
469 | 9.70k | pinned_iters_mgr_ = pinned_iters_mgr; |
470 | 26.4k | for (auto& child : children_) { |
471 | 26.4k | child.iter.SetPinnedItersMgr(pinned_iters_mgr); |
472 | 26.4k | } |
473 | 9.70k | } |
474 | | |
475 | 8.64k | bool IsKeyPinned() const override { |
476 | 8.64k | assert(Valid()); |
477 | 8.64k | return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() && |
478 | 113 | current_->IsKeyPinned(); |
479 | 8.64k | } |
480 | | |
481 | 6.65k | bool IsValuePinned() const override { |
482 | 6.65k | assert(Valid()); |
483 | 6.65k | return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() && |
484 | 6.65k | current_->IsValuePinned(); |
485 | 6.65k | } |
486 | | |
487 | 0 | void Prepare(const MultiScanArgs* scan_opts) override { |
488 | 0 | for (auto& child : children_) { |
489 | 0 | child.iter.Prepare(scan_opts); |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | private: |
494 | | // Represents an element in the min/max heap. Each HeapItem corresponds to a |
495 | | // point iterator or a range tombstone iterator, differentiated by |
496 | | // HeapItem::type. |
497 | | struct HeapItem { |
498 | 17.7k | HeapItem() = default; |
499 | | |
500 | | // corresponding point iterator |
501 | | IteratorWrapper iter; |
502 | | size_t level = 0; |
503 | | // corresponding range tombstone iterator's start or end key value |
504 | | // depending on value of `type`. |
505 | | ParsedInternalKey tombstone_pik; |
506 | | // Will be overwritten before use, initialize here so compiler does not |
507 | | // complain. |
508 | | enum class Type { ITERATOR, DELETE_RANGE_START, DELETE_RANGE_END }; |
509 | | Type type = Type::ITERATOR; |
510 | | |
511 | | explicit HeapItem(size_t _level, InternalIteratorBase<Slice>* _iter) |
512 | 26.4k | : level(_level), type(Type::ITERATOR) { |
513 | 26.4k | iter.Set(_iter); |
514 | 26.4k | } |
515 | | |
516 | 187k | void SetTombstoneKey(ParsedInternalKey&& pik) { |
517 | | // op_type is already initialized in MergingIterator::Finish(). |
518 | 187k | tombstone_pik.user_key = pik.user_key; |
519 | 187k | tombstone_pik.sequence = pik.sequence; |
520 | 187k | } |
521 | | }; |
522 | | |
523 | | class MinHeapItemComparator { |
524 | | public: |
525 | | explicit MinHeapItemComparator(const InternalKeyComparator* comparator) |
526 | 16.1k | : comparator_(comparator) {} |
527 | | |
528 | 150k | bool operator()(HeapItem* a, HeapItem* b) const { |
529 | 150k | if (LIKELY(a->type == HeapItem::Type::ITERATOR)) { |
530 | 37.8k | if (LIKELY(b->type == HeapItem::Type::ITERATOR)) { |
531 | 15.5k | return comparator_->Compare(a->iter.key(), b->iter.key()) > 0; |
532 | 22.3k | } else { |
533 | 22.3k | return comparator_->Compare(a->iter.key(), b->tombstone_pik) > 0; |
534 | 22.3k | } |
535 | 112k | } else { |
536 | 112k | if (LIKELY(b->type == HeapItem::Type::ITERATOR)) { |
537 | 112k | return comparator_->Compare(a->tombstone_pik, b->iter.key()) > 0; |
538 | 112k | } else { |
539 | 0 | return comparator_->Compare(a->tombstone_pik, b->tombstone_pik) > 0; |
540 | 0 | } |
541 | 112k | } |
542 | 150k | } |
543 | | |
544 | | private: |
545 | | const InternalKeyComparator* comparator_; |
546 | | }; |
547 | | |
548 | | class MaxHeapItemComparator { |
549 | | public: |
550 | | explicit MaxHeapItemComparator(const InternalKeyComparator* comparator) |
551 | 3.03k | : comparator_(comparator) {} |
552 | | |
553 | 14.9k | bool operator()(HeapItem* a, HeapItem* b) const { |
554 | 14.9k | if (LIKELY(a->type == HeapItem::Type::ITERATOR)) { |
555 | 14.9k | if (LIKELY(b->type == HeapItem::Type::ITERATOR)) { |
556 | 14.9k | return comparator_->Compare(a->iter.key(), b->iter.key()) < 0; |
557 | 14.9k | } else { |
558 | 0 | return comparator_->Compare(a->iter.key(), b->tombstone_pik) < 0; |
559 | 0 | } |
560 | 14.9k | } else { |
561 | 0 | if (LIKELY(b->type == HeapItem::Type::ITERATOR)) { |
562 | 0 | return comparator_->Compare(a->tombstone_pik, b->iter.key()) < 0; |
563 | 0 | } else { |
564 | 0 | return comparator_->Compare(a->tombstone_pik, b->tombstone_pik) < 0; |
565 | 0 | } |
566 | 0 | } |
567 | 14.9k | } |
568 | | |
569 | | private: |
570 | | const InternalKeyComparator* comparator_; |
571 | | }; |
572 | | |
573 | | using MergerMinIterHeap = BinaryHeap<HeapItem*, MinHeapItemComparator>; |
574 | | using MergerMaxIterHeap = BinaryHeap<HeapItem*, MaxHeapItemComparator>; |
575 | | |
576 | | friend class MergeIteratorBuilder; |
577 | | // Clears heaps for both directions, used when changing direction or seeking |
578 | | void ClearHeaps(bool clear_active = true); |
579 | | // Ensures that maxHeap_ is initialized when starting to go in the reverse |
580 | | // direction |
581 | | void InitMaxHeap(); |
582 | | // Advance this merging iterator until the current key (minHeap_.top()) is |
583 | | // from a point iterator and is not covered by any range tombstone, |
584 | | // or that there is no more keys (heap is empty). SeekImpl() may be called |
585 | | // to seek to the end of a range tombstone as an optimization. |
586 | | void FindNextVisibleKey(); |
587 | | void FindPrevVisibleKey(); |
588 | | |
589 | | // Advance this merging iterators to the first key >= `target` for all |
590 | | // components from levels >= starting_level. All iterators before |
591 | | // starting_level are untouched. |
592 | | // |
593 | | // @param range_tombstone_reseek Whether target is some range tombstone |
594 | | // end, i.e., whether this SeekImpl() call is a part of a "cascading seek". |
595 | | // This is used only for recoding relevant perf_context. |
596 | | void SeekImpl(const Slice& target, size_t starting_level = 0, |
597 | | bool range_tombstone_reseek = false); |
598 | | |
599 | | // Seek to fist key <= target key (internal key) for |
600 | | // children_[starting_level:]. |
601 | | void SeekForPrevImpl(const Slice& target, size_t starting_level = 0, |
602 | | bool range_tombstone_reseek = false); |
603 | | |
604 | | bool is_arena_mode_; |
605 | | bool prefix_seek_mode_; |
606 | | // Which direction is the iterator moving? |
607 | | enum Direction : uint8_t { kForward, kReverse }; |
608 | | Direction direction_; |
609 | | const InternalKeyComparator* comparator_; |
610 | | // HeapItem for all child point iterators. |
611 | | // Invariant(children_): children_[i] is in minHeap_ iff |
612 | | // children_[i].iter.Valid(), and at most one children_[i] is in minHeap_. |
613 | | // TODO: We could use an autovector with a larger reserved size. |
614 | | std::vector<HeapItem> children_; |
615 | | // HeapItem for range tombstone start and end keys. |
616 | | // pinned_heap_item_[i] corresponds to range_tombstone_iters_[i]. |
617 | | // Invariant(phi): If range_tombstone_iters_[i]->Valid(), |
618 | | // pinned_heap_item_[i].tombstone_pik is equal to |
619 | | // range_tombstone_iters_[i]->start_key() when |
620 | | // pinned_heap_item_[i].type is DELETE_RANGE_START and |
621 | | // range_tombstone_iters_[i]->end_key() when |
622 | | // pinned_heap_item_[i].type is DELETE_RANGE_END (ignoring op_type which is |
623 | | // kMaxValid for all pinned_heap_item_.tombstone_pik). |
624 | | // pinned_heap_item_[i].type is either DELETE_RANGE_START or DELETE_RANGE_END. |
625 | | std::vector<HeapItem> pinned_heap_item_; |
626 | | // range_tombstone_iters_[i] contains range tombstones in the sorted run that |
627 | | // corresponds to children_[i]. range_tombstone_iters_.empty() means not |
628 | | // handling range tombstones in merging iterator. range_tombstone_iters_[i] == |
629 | | // nullptr means the sorted run of children_[i] does not have range |
630 | | // tombstones. |
631 | | // Invariant(rti): pinned_heap_item_[i] is in minHeap_ iff |
632 | | // range_tombstone_iters_[i]->Valid() and at most one pinned_heap_item_[i] is |
633 | | // in minHeap_. |
634 | | std::vector<std::unique_ptr<TruncatedRangeDelIterator>> |
635 | | range_tombstone_iters_; |
636 | | |
637 | | // Levels (indices into range_tombstone_iters_/children_ ) that currently have |
638 | | // "active" range tombstones. See comments above MergingIterator for meaning |
639 | | // of "active". |
640 | | // Invariant(active_): i is in active_ iff range_tombstone_iters_[i]->Valid() |
641 | | // and pinned_heap_item_[i].type == DELETE_RANGE_END. |
642 | | std::set<size_t> active_; |
643 | | |
644 | | bool SkipNextDeleted(); |
645 | | |
646 | | bool SkipPrevDeleted(); |
647 | | |
648 | | // Invariant: at the end of each InternalIterator API, |
649 | | // current_ points to minHeap_.top().iter (maxHeap_ if backward scanning) |
650 | | // or nullptr if no child iterator is valid. |
651 | | // This follows from that current_ = CurrentForward()/CurrentReverse() is |
652 | | // called at the end of each InternalIterator API. |
653 | | IteratorWrapper* current_; |
654 | | // If any of the children have non-ok status, this is one of them. |
655 | | Status status_; |
656 | | // Invariant: min heap property is maintained (parent is always <= child). |
657 | | // This holds by using only BinaryHeap APIs to modify heap. One |
658 | | // exception is to modify heap top item directly (by caller iter->Next()), and |
659 | | // it should be followed by a call to replace_top() or pop(). |
660 | | MergerMinIterHeap minHeap_; |
661 | | |
662 | | // Max heap is used for reverse iteration, which is way less common than |
663 | | // forward. Lazily initialize it to save memory. |
664 | | std::unique_ptr<MergerMaxIterHeap> maxHeap_; |
665 | | PinnedIteratorsManager* pinned_iters_mgr_; |
666 | | |
667 | | // Used to bound range tombstones. For point keys, DBIter and SSTable iterator |
668 | | // take care of boundary checking. |
669 | | const Slice* iterate_upper_bound_; |
670 | | |
671 | | // In forward direction, process a child that is not in the min heap. |
672 | | // If valid, add to the min heap. Otherwise, check status. |
673 | | void AddToMinHeapOrCheckStatus(HeapItem*); |
674 | | |
675 | | // In backward direction, process a child that is not in the max heap. |
676 | | // If valid, add to the min heap. Otherwise, check status. |
677 | | void AddToMaxHeapOrCheckStatus(HeapItem*); |
678 | | |
679 | | void SwitchToForward(); |
680 | | |
681 | | // Switch the direction from forward to backward without changing the |
682 | | // position. Iterator should still be valid. |
683 | | void SwitchToBackward(); |
684 | | |
685 | 58.0k | IteratorWrapper* CurrentForward() const { |
686 | 58.0k | assert(direction_ == kForward); |
687 | 58.0k | assert(minHeap_.empty() || |
688 | 58.0k | minHeap_.top()->type == HeapItem::Type::ITERATOR); |
689 | 58.0k | return !minHeap_.empty() ? &minHeap_.top()->iter : nullptr; |
690 | 58.0k | } |
691 | | |
692 | 14.4k | IteratorWrapper* CurrentReverse() const { |
693 | 14.4k | assert(direction_ == kReverse); |
694 | 14.4k | assert(maxHeap_); |
695 | 14.4k | assert(maxHeap_->empty() || |
696 | 14.4k | maxHeap_->top()->type == HeapItem::Type::ITERATOR); |
697 | 14.4k | return !maxHeap_->empty() ? &maxHeap_->top()->iter : nullptr; |
698 | 14.4k | } |
699 | | }; |
700 | | |
701 | | // Pre-condition: |
702 | | // - Invariants (3) and (4) hold for i < starting_level |
703 | | // - For i < starting_level, range_tombstone_iters_[i].prev.end_key() < |
704 | | // `target`. |
705 | | // - For i < starting_level, if i in active_, then |
706 | | // range_tombstone_iters_[i]->start_key() < `target`. |
707 | | // |
708 | | // Post-condition: |
709 | | // - Invariants (3) and (4) hold for all level i. |
710 | | // - (*) target <= children_[i].iter.key() <= LevelNextVisible(i, target) |
711 | | // for i >= starting_level |
712 | | // - (**) target < pinned_heap_item_[i].tombstone_pik if |
713 | | // range_tombstone_iters_[i].Valid() for i >= starting_level |
714 | | // |
715 | | // Proof sketch: |
716 | | // Invariant (3) holds for all level i. |
717 | | // For j <= i < starting_level, it follows from Pre-condition that (3) holds |
718 | | // and that SeekImpl(-, starting_level) does not update children_[i] or |
719 | | // range_tombstone_iters_[j]. |
720 | | // For j < starting_level and i >= starting_level, it follows from |
721 | | // - Pre-condition that range_tombstone_iters_[j].prev.end_key() < `target` |
722 | | // - range_tombstone_iters_[j] is not updated in SeekImpl(), and |
723 | | // - children_[i].iter.Seek(current_search_key) is called with |
724 | | // current_search_key >= target (shown below). |
725 | | // When current_search_key is updated, it is updated to some |
726 | | // range_tombstone_iter->end_key() after |
727 | | // range_tombstone_iter->SeekInternalKey(current_search_key) was called. So |
728 | | // current_search_key increases if updated and >= target. |
729 | | // For starting_level <= j <= i: |
730 | | // children_[i].iter.Seek(k1) and range_tombstone_iters_[j]->SeekInternalKey(k2) |
731 | | // are called in SeekImpl(). Seek(k1) positions children_[i] at the first key >= |
732 | | // k1 from level i. SeekInternalKey(k2) positions range_tombstone_iters_[j] at |
733 | | // the first range tombstone from level j with end_key() > k2. It suffices to |
734 | | // show that k1 >= k2. Since k1 and k2 are values of current_search_key where |
735 | | // k1 = k2 or k1 is value of a later current_search_key than k2, so k1 >= k2. |
736 | | // |
737 | | // Invariant (4) holds for all level >= 0. |
738 | | // By Pre-condition Invariant (4) holds for i < starting_level. |
739 | | // Since children_[i], range_tombstone_iters_[i] and contents of active_ for |
740 | | // i < starting_level do not change (4) holds for j <= i < starting_level. |
741 | | // By Pre-condition: for all j < starting_level, if j in active_, then |
742 | | // range_tombstone_iters_[j]->start_key() < target. For i >= starting_level, |
743 | | // children_[i].iter.Seek(k) is called for k >= target. So |
744 | | // children_[i].iter.key() >= target > range_tombstone_iters_[j]->start_key() |
745 | | // for j < starting_level and i >= starting_level. So invariant (4) holds for |
746 | | // j < starting_level and i >= starting_level. |
747 | | // For starting_level <= j <= i, j is added to active_ only if |
748 | | // - range_tombstone_iters_[j]->SeekInternalKey(k1) was called |
749 | | // - range_tombstone_iters_[j]->start_key() <= k1 |
750 | | // Since children_[i].iter.Seek(k2) is called for some k2 >= k1 and for all |
751 | | // starting_level <= j <= i, (4) also holds for all starting_level <= j <= i. |
752 | | // |
753 | | // Post-condition (*): target <= children_[i].iter.key() <= LevelNextVisible(i, |
754 | | // target) for i >= starting_level. |
755 | | // target <= children_[i].iter.key() follows from that Seek() is called on some |
756 | | // current_search_key >= target for children_[i].iter. If current_search_key |
757 | | // is updated from k1 to k2 when level = i, we show that the range [k1, k2) is |
758 | | // not visible for children_[j] for any j > i. When current_search_key is |
759 | | // updated from k1 to k2, |
760 | | // - range_tombstone_iters_[i]->SeekInternalKey(k1) was called |
761 | | // - range_tombstone_iters_[i]->Valid() |
762 | | // - range_tombstone_iters_[i]->start_key().user_key <= k1.user_key |
763 | | // - k2 = range_tombstone_iters_[i]->end_key() |
764 | | // We assume that range_tombstone_iters_[i]->start_key() has a higher sequence |
765 | | // number compared to any key from levels > i that has the same user key. So no |
766 | | // point key from levels > i in range [k1, k2) is visible. So |
767 | | // children_[i].iter.key() <= LevelNextVisible(i, target). |
768 | | // |
769 | | // Post-condition (**) target < pinned_heap_item_[i].tombstone_pik for i >= |
770 | | // starting_level if range_tombstone_iters_[i].Valid(). This follows from that |
771 | | // SeekInternalKey() being called for each range_tombstone_iters_ with some key |
772 | | // >= `target` and that we pick start/end key that is > `target` to insert to |
773 | | // minHeap_. |
774 | | void MergingIterator::SeekImpl(const Slice& target, size_t starting_level, |
775 | 702 | bool range_tombstone_reseek) { |
776 | | // active range tombstones before `starting_level` remain active |
777 | 702 | ClearHeaps(false /* clear_active */); |
778 | 702 | ParsedInternalKey pik; |
779 | 702 | if (!range_tombstone_iters_.empty()) { |
780 | | // pik is only used in InsertRangeTombstoneToMinHeap(). |
781 | 519 | ParseInternalKey(target, &pik, false).PermitUncheckedError(); |
782 | 519 | } |
783 | | |
784 | | // TODO: perhaps we could save some upheap cost by add all child iters first |
785 | | // and then do a single heapify. |
786 | | // Invariant(children_) for level < starting_level |
787 | 702 | for (size_t level = 0; level < starting_level; ++level) { |
788 | 0 | PERF_TIMER_GUARD(seek_min_heap_time); |
789 | 0 | AddToMinHeapOrCheckStatus(&children_[level]); |
790 | 0 | } |
791 | 702 | if (!range_tombstone_iters_.empty()) { |
792 | | // Add range tombstones from levels < starting_level. We can insert from |
793 | | // pinned_heap_item_ for the following reasons: |
794 | | // - pinned_heap_item_[level] is in minHeap_ iff |
795 | | // range_tombstone_iters[level]->Valid(). |
796 | | // - If `level` is in active_, then range_tombstone_iters_[level]->Valid() |
797 | | // and pinned_heap_item_[level] is of type RANGE_DELETION_END. |
798 | 519 | for (size_t level = 0; level < starting_level; ++level) { |
799 | | // Restores Invariants(rti), (phi) and (active_) for level < |
800 | | // starting_level |
801 | 0 | if (range_tombstone_iters_[level] && |
802 | 0 | range_tombstone_iters_[level]->Valid()) { |
803 | | // use an iterator on active_ if performance becomes an issue here |
804 | 0 | if (active_.count(level) > 0) { |
805 | 0 | assert(pinned_heap_item_[level].type == |
806 | 0 | HeapItem::Type::DELETE_RANGE_END); |
807 | | // if it was active, then start key must be within upper_bound, |
808 | | // so we can add to minHeap_ directly. |
809 | 0 | minHeap_.push(&pinned_heap_item_[level]); |
810 | 0 | } else { |
811 | 0 | assert(pinned_heap_item_[level].type == |
812 | 0 | HeapItem::Type::DELETE_RANGE_START); |
813 | | // this takes care of checking iterate_upper_bound, but with an extra |
814 | | // key comparison if range_tombstone_iters_[level] was already out of |
815 | | // bound. Consider using a new HeapItem type or some flag to remember |
816 | | // boundary checking result. |
817 | 0 | InsertRangeTombstoneToMinHeap(level); |
818 | 0 | } |
819 | 0 | } else { |
820 | 0 | assert(!active_.count(level)); |
821 | 0 | } |
822 | 0 | } |
823 | | // levels >= starting_level will be reseeked below, so clearing their active |
824 | | // state here. |
825 | 519 | active_.erase(active_.lower_bound(starting_level), active_.end()); |
826 | 519 | } |
827 | | |
828 | 702 | IterKey current_search_key; |
829 | 702 | current_search_key.SetInternalKey(target, false /* copy */); |
830 | | // Seek target might change to some range tombstone end key, so |
831 | | // we need to remember them for async requests. |
832 | | // (level, target) pairs |
833 | 702 | autovector<std::pair<size_t, std::string>> prefetched_target; |
834 | 3.48k | for (auto level = starting_level; level < children_.size(); ++level) { |
835 | 2.78k | { |
836 | 2.78k | PERF_TIMER_GUARD(seek_child_seek_time); |
837 | 2.78k | children_[level].iter.Seek(current_search_key.GetInternalKey()); |
838 | 2.78k | } |
839 | | |
840 | 2.78k | PERF_COUNTER_ADD(seek_child_seek_count, 1); |
841 | | |
842 | 2.78k | if (!range_tombstone_iters_.empty()) { |
843 | 2.23k | if (range_tombstone_reseek) { |
844 | | // This seek is to some range tombstone end key. |
845 | | // Should only happen when there are range tombstones. |
846 | 0 | PERF_COUNTER_ADD(internal_range_del_reseek_count, 1); |
847 | 0 | } |
848 | 2.23k | if (children_[level].iter.status().IsTryAgain()) { |
849 | 0 | prefetched_target.emplace_back( |
850 | 0 | level, current_search_key.GetInternalKey().ToString()); |
851 | 0 | } |
852 | 2.23k | UnownedPtr<TruncatedRangeDelIterator> range_tombstone_iter = |
853 | 2.23k | range_tombstone_iters_[level].get(); |
854 | 2.23k | if (range_tombstone_iter) { |
855 | 0 | range_tombstone_iter->SeekInternalKey( |
856 | 0 | current_search_key.GetInternalKey()); |
857 | | // Invariants (rti) and (phi) |
858 | 0 | if (range_tombstone_iter->Valid()) { |
859 | | // If range tombstone starts after `current_search_key`, |
860 | | // we should insert start key to heap as the range tombstone is not |
861 | | // active yet. |
862 | 0 | InsertRangeTombstoneToMinHeap( |
863 | 0 | level, comparator_->Compare(range_tombstone_iter->start_key(), |
864 | 0 | pik) > 0 /* start_key */); |
865 | | // current_search_key < end_key guaranteed by the SeekInternalKey() |
866 | | // and Valid() calls above. Here we only need to compare user_key |
867 | | // since if target.user_key == |
868 | | // range_tombstone_iter->start_key().user_key and target < |
869 | | // range_tombstone_iter->start_key(), no older level would have any |
870 | | // key in range [target, range_tombstone_iter->start_key()], so no |
871 | | // keys in range [target, range_tombstone_iter->end_key()) from older |
872 | | // level would be visible. So it is safe to seek to |
873 | | // range_tombstone_iter->end_key(). |
874 | | // |
875 | | // TODO: range_tombstone_iter->Seek() finds the max covering |
876 | | // sequence number, can make it cheaper by not looking for max. |
877 | 0 | if (comparator_->user_comparator()->Compare( |
878 | 0 | range_tombstone_iter->start_key().user_key, |
879 | 0 | current_search_key.GetUserKey()) <= 0) { |
880 | 0 | range_tombstone_reseek = true; |
881 | | // Note that for prefix seek case, it is possible that the prefix |
882 | | // is not the same as the original target, it should not affect |
883 | | // correctness. Besides, in most cases, range tombstone start and |
884 | | // end key should have the same prefix? |
885 | 0 | current_search_key.SetInternalKey(range_tombstone_iter->end_key()); |
886 | 0 | } |
887 | 0 | } |
888 | 0 | } |
889 | 2.23k | } |
890 | | // child.iter.status() is set to Status::TryAgain indicating asynchronous |
891 | | // request for retrieval of data blocks has been submitted. So it should |
892 | | // return at this point and Seek should be called again to retrieve the |
893 | | // requested block and add the child to min heap. |
894 | 2.78k | if (children_[level].iter.status().IsTryAgain()) { |
895 | 0 | continue; |
896 | 0 | } |
897 | 2.78k | { |
898 | | // Strictly, we timed slightly more than min heap operation, |
899 | | // but these operations are very cheap. |
900 | 2.78k | PERF_TIMER_GUARD(seek_min_heap_time); |
901 | 2.78k | AddToMinHeapOrCheckStatus(&children_[level]); |
902 | 2.78k | } |
903 | 2.78k | } |
904 | | |
905 | 702 | if (range_tombstone_iters_.empty()) { |
906 | 549 | for (auto& child : children_) { |
907 | 549 | if (child.iter.status().IsTryAgain()) { |
908 | 0 | child.iter.Seek(target); |
909 | 0 | { |
910 | 0 | PERF_TIMER_GUARD(seek_min_heap_time); |
911 | 0 | AddToMinHeapOrCheckStatus(&child); |
912 | 0 | } |
913 | 0 | PERF_COUNTER_ADD(number_async_seek, 1); |
914 | 0 | } |
915 | 549 | } |
916 | 519 | } else { |
917 | 519 | for (auto& prefetch : prefetched_target) { |
918 | | // (level, target) pairs |
919 | 0 | children_[prefetch.first].iter.Seek(prefetch.second); |
920 | 0 | { |
921 | 0 | PERF_TIMER_GUARD(seek_min_heap_time); |
922 | 0 | AddToMinHeapOrCheckStatus(&children_[prefetch.first]); |
923 | 0 | } |
924 | 0 | PERF_COUNTER_ADD(number_async_seek, 1); |
925 | 0 | } |
926 | 519 | } |
927 | 702 | } |
928 | | |
929 | | // Returns true iff the current key (min heap top) should not be returned |
930 | | // to user (of the merging iterator). This can be because the current key |
931 | | // is deleted by some range tombstone, the current key is some fake file |
932 | | // boundary sentinel key, or the current key is an end point of a range |
933 | | // tombstone. Advance the iterator at heap top if needed. Heap order is restored |
934 | | // and `active_` is updated accordingly. |
935 | | // See FindNextVisibleKey() for more detail on internal implementation |
936 | | // of advancing child iters. |
937 | | // When false is returned, if minHeap is not empty, then minHeap_.top().type |
938 | | // == ITERATOR |
939 | | // |
940 | | // REQUIRES: |
941 | | // - min heap is currently not empty, and iter is in kForward direction. |
942 | | // - minHeap_ top is not DELETE_RANGE_START (so that `active_` is current). |
943 | 110k | bool MergingIterator::SkipNextDeleted() { |
944 | | // 3 types of keys: |
945 | | // - point key |
946 | | // - file boundary sentinel keys |
947 | | // - range deletion end key |
948 | 110k | auto current = minHeap_.top(); |
949 | 110k | if (current->type == HeapItem::Type::DELETE_RANGE_END) { |
950 | | // Invariant(active_): range_tombstone_iters_[current->level] is about to |
951 | | // become !Valid() or that its start key is going to be added to minHeap_. |
952 | 93.9k | active_.erase(current->level); |
953 | 93.9k | assert(range_tombstone_iters_[current->level] && |
954 | 93.9k | range_tombstone_iters_[current->level]->Valid()); |
955 | 93.9k | range_tombstone_iters_[current->level]->Next(); |
956 | | // Maintain Invariants (rti) and (phi) |
957 | 93.9k | if (range_tombstone_iters_[current->level]->Valid()) { |
958 | 91.0k | InsertRangeTombstoneToMinHeap(current->level, true /* start_key */, |
959 | 91.0k | true /* replace_top */); |
960 | 91.0k | } else { |
961 | | // TruncatedRangeDelIterator does not have status |
962 | 2.87k | minHeap_.pop(); |
963 | 2.87k | } |
964 | 93.9k | return true /* current key deleted */; |
965 | 93.9k | } |
966 | 16.8k | if (current->iter.IsDeleteRangeSentinelKey()) { |
967 | | // If the file boundary is defined by a range deletion, the range |
968 | | // tombstone's end key must come before this sentinel key (see op_type in |
969 | | // SetTombstoneKey()). |
970 | 3.00k | assert(ExtractValueType(current->iter.key()) != kTypeRangeDeletion || |
971 | 3.00k | active_.count(current->level) == 0); |
972 | | // When entering a new file, range tombstone iter from the old file is |
973 | | // freed, but the last key from that range tombstone iter may still be in |
974 | | // the heap. We need to ensure the data underlying its corresponding key |
975 | | // Slice is still alive. We do so by popping the range tombstone key from |
976 | | // heap before calling iter->Next(). Technically, this change is not needed: |
977 | | // if there is a range tombstone end key that is after file boundary |
978 | | // sentinel key in minHeap_, the range tombstone end key must have been |
979 | | // truncated at file boundary. The underlying data of the range tombstone |
980 | | // end key Slice is the SST file's largest internal key stored as file |
981 | | // metadata in Version. However, since there are too many implicit |
982 | | // assumptions made, it is safer to just ensure range tombstone iter is |
983 | | // still alive. |
984 | 3.00k | minHeap_.pop(); |
985 | | // Remove last SST file's range tombstone end key if there is one. |
986 | | // This means file boundary is before range tombstone end key, |
987 | | // which could happen when a range tombstone and a user key |
988 | | // straddle two SST files. Note that in TruncatedRangeDelIterator |
989 | | // constructor, parsed_largest.sequence is decremented 1 in this case. |
990 | | // Maintains Invariant(rti) that at most one |
991 | | // pinned_heap_item_[current->level] is in minHeap_. |
992 | 3.00k | if (range_tombstone_iters_[current->level] && |
993 | 0 | range_tombstone_iters_[current->level]->Valid()) { |
994 | 0 | if (!minHeap_.empty() && minHeap_.top()->level == current->level) { |
995 | 0 | assert(minHeap_.top()->type == HeapItem::Type::DELETE_RANGE_END); |
996 | 0 | minHeap_.pop(); |
997 | | // Invariant(active_): we are about to enter a new SST file with new |
998 | | // range_tombstone_iters[current->level]. Either it is !Valid() or its |
999 | | // start key is going to be added to minHeap_. |
1000 | 0 | active_.erase(current->level); |
1001 | 0 | } else { |
1002 | | // range tombstone is still valid, but it is not on heap. |
1003 | | // This should only happen if the range tombstone is over iterator |
1004 | | // upper bound. |
1005 | 0 | assert(iterate_upper_bound_ && |
1006 | 0 | comparator_->user_comparator()->CompareWithoutTimestamp( |
1007 | 0 | range_tombstone_iters_[current->level]->start_key().user_key, |
1008 | 0 | true /* a_has_ts */, *iterate_upper_bound_, |
1009 | 0 | false /* b_has_ts */) >= 0); |
1010 | 0 | } |
1011 | 0 | } |
1012 | | // LevelIterator enters a new SST file |
1013 | 3.00k | current->iter.Next(); |
1014 | | // Invariant(children_): current is popped from heap and added back only if |
1015 | | // it is valid |
1016 | 3.00k | if (current->iter.Valid()) { |
1017 | 1.56k | assert(current->iter.status().ok()); |
1018 | 1.56k | minHeap_.push(current); |
1019 | 1.56k | } else { |
1020 | | // TODO(cbi): check status and early return if non-ok. |
1021 | 1.44k | considerStatus(current->iter.status()); |
1022 | 1.44k | } |
1023 | | // Invariants (rti) and (phi) |
1024 | 3.00k | if (range_tombstone_iters_[current->level] && |
1025 | 0 | range_tombstone_iters_[current->level]->Valid()) { |
1026 | 0 | InsertRangeTombstoneToMinHeap(current->level); |
1027 | 0 | } |
1028 | 3.00k | return true /* current key deleted */; |
1029 | 3.00k | } |
1030 | 16.8k | assert(current->type == HeapItem::Type::ITERATOR); |
1031 | | // Point key case: check active_ for range tombstone coverage. |
1032 | 13.8k | ParsedInternalKey pik; |
1033 | 13.8k | ParseInternalKey(current->iter.key(), &pik, false).PermitUncheckedError(); |
1034 | 13.8k | if (!active_.empty()) { |
1035 | 13.8k | auto i = *active_.begin(); |
1036 | 13.8k | if (i < current->level) { |
1037 | | // range tombstone is from a newer level, definitely covers |
1038 | 0 | assert(comparator_->Compare(range_tombstone_iters_[i]->start_key(), |
1039 | 0 | pik) <= 0); |
1040 | 0 | assert(comparator_->Compare(pik, range_tombstone_iters_[i]->end_key()) < |
1041 | 0 | 0); |
1042 | 0 | std::string target; |
1043 | 0 | AppendInternalKey(&target, range_tombstone_iters_[i]->end_key()); |
1044 | 0 | SeekImpl(target, current->level, true); |
1045 | 0 | return true /* current key deleted */; |
1046 | 13.8k | } else if (i == current->level) { |
1047 | | // range tombstone is from the same level as current, check sequence |
1048 | | // number. By `active_` we know current key is between start key and end |
1049 | | // key. |
1050 | 13.8k | assert(comparator_->Compare(range_tombstone_iters_[i]->start_key(), |
1051 | 13.8k | pik) <= 0); |
1052 | 13.8k | assert(comparator_->Compare(pik, range_tombstone_iters_[i]->end_key()) < |
1053 | 13.8k | 0); |
1054 | 13.8k | if (pik.sequence < range_tombstone_iters_[current->level]->seq()) { |
1055 | | // covered by range tombstone |
1056 | 0 | current->iter.Next(); |
1057 | | // Invariant (children_) |
1058 | 0 | if (current->iter.Valid()) { |
1059 | 0 | minHeap_.replace_top(current); |
1060 | 0 | } else { |
1061 | 0 | considerStatus(current->iter.status()); |
1062 | 0 | minHeap_.pop(); |
1063 | 0 | } |
1064 | 0 | return true /* current key deleted */; |
1065 | 13.8k | } else { |
1066 | 13.8k | return false /* current key not deleted */; |
1067 | 13.8k | } |
1068 | 13.8k | } else { |
1069 | 0 | return false /* current key not deleted */; |
1070 | | // range tombstone from an older sorted run with current key < end key. |
1071 | | // current key is not deleted and the older sorted run will have its range |
1072 | | // tombstone updated when the range tombstone's end key are popped from |
1073 | | // minHeap_. |
1074 | 0 | } |
1075 | 13.8k | } |
1076 | | // we can reach here only if active_ is empty |
1077 | 13.8k | assert(active_.empty()); |
1078 | 0 | assert(minHeap_.top()->type == HeapItem::Type::ITERATOR); |
1079 | 0 | return false /* current key not deleted */; |
1080 | 13.8k | } |
1081 | | |
1082 | | void MergingIterator::SeekForPrevImpl(const Slice& target, |
1083 | | size_t starting_level, |
1084 | 3.03k | bool range_tombstone_reseek) { |
1085 | | // active range tombstones before `starting_level` remain active |
1086 | 3.03k | ClearHeaps(false /* clear_active */); |
1087 | 3.03k | InitMaxHeap(); |
1088 | 3.03k | ParsedInternalKey pik; |
1089 | 3.03k | if (!range_tombstone_iters_.empty()) { |
1090 | 1.94k | ParseInternalKey(target, &pik, false).PermitUncheckedError(); |
1091 | 1.94k | } |
1092 | 3.03k | for (size_t level = 0; level < starting_level; ++level) { |
1093 | 0 | PERF_TIMER_GUARD(seek_max_heap_time); |
1094 | 0 | AddToMaxHeapOrCheckStatus(&children_[level]); |
1095 | 0 | } |
1096 | 3.03k | if (!range_tombstone_iters_.empty()) { |
1097 | | // Add range tombstones before starting_level. |
1098 | 1.94k | for (size_t level = 0; level < starting_level; ++level) { |
1099 | 0 | if (range_tombstone_iters_[level] && |
1100 | 0 | range_tombstone_iters_[level]->Valid()) { |
1101 | 0 | assert(static_cast<bool>(active_.count(level)) == |
1102 | 0 | (pinned_heap_item_[level].type == |
1103 | 0 | HeapItem::Type::DELETE_RANGE_START)); |
1104 | 0 | maxHeap_->push(&pinned_heap_item_[level]); |
1105 | 0 | } else { |
1106 | 0 | assert(!active_.count(level)); |
1107 | 0 | } |
1108 | 0 | } |
1109 | | // levels >= starting_level will be reseeked below, |
1110 | 1.94k | active_.erase(active_.lower_bound(starting_level), active_.end()); |
1111 | 1.94k | } |
1112 | | |
1113 | 3.03k | IterKey current_search_key; |
1114 | 3.03k | current_search_key.SetInternalKey(target, false /* copy */); |
1115 | | // Seek target might change to some range tombstone end key, so |
1116 | | // we need to remember them for async requests. |
1117 | | // (level, target) pairs |
1118 | 3.03k | autovector<std::pair<size_t, std::string>> prefetched_target; |
1119 | 12.9k | for (auto level = starting_level; level < children_.size(); ++level) { |
1120 | 9.95k | { |
1121 | 9.95k | PERF_TIMER_GUARD(seek_child_seek_time); |
1122 | 9.95k | children_[level].iter.SeekForPrev(current_search_key.GetInternalKey()); |
1123 | 9.95k | } |
1124 | | |
1125 | 9.95k | PERF_COUNTER_ADD(seek_child_seek_count, 1); |
1126 | | |
1127 | 9.95k | if (!range_tombstone_iters_.empty()) { |
1128 | 6.55k | if (range_tombstone_reseek) { |
1129 | | // This seek is to some range tombstone end key. |
1130 | | // Should only happen when there are range tombstones. |
1131 | 0 | PERF_COUNTER_ADD(internal_range_del_reseek_count, 1); |
1132 | 0 | } |
1133 | 6.55k | if (children_[level].iter.status().IsTryAgain()) { |
1134 | 0 | prefetched_target.emplace_back( |
1135 | 0 | level, current_search_key.GetInternalKey().ToString()); |
1136 | 0 | } |
1137 | 6.55k | UnownedPtr<TruncatedRangeDelIterator> range_tombstone_iter = |
1138 | 6.55k | range_tombstone_iters_[level].get(); |
1139 | 6.55k | if (range_tombstone_iter) { |
1140 | 0 | range_tombstone_iter->SeekForPrev(current_search_key.GetUserKey()); |
1141 | 0 | if (range_tombstone_iter->Valid()) { |
1142 | 0 | InsertRangeTombstoneToMaxHeap( |
1143 | 0 | level, comparator_->Compare(range_tombstone_iter->end_key(), |
1144 | 0 | pik) <= 0 /* end_key */); |
1145 | | // start key <= current_search_key guaranteed by the Seek() call above |
1146 | | // Only interested in user key coverage since older sorted runs must |
1147 | | // have smaller sequence numbers than this tombstone. |
1148 | 0 | if (comparator_->user_comparator()->Compare( |
1149 | 0 | current_search_key.GetUserKey(), |
1150 | 0 | range_tombstone_iter->end_key().user_key) < 0) { |
1151 | 0 | range_tombstone_reseek = true; |
1152 | 0 | current_search_key.SetInternalKey( |
1153 | 0 | range_tombstone_iter->start_key().user_key, kMaxSequenceNumber, |
1154 | 0 | kValueTypeForSeekForPrev); |
1155 | 0 | } |
1156 | 0 | } |
1157 | 0 | } |
1158 | 6.55k | } |
1159 | | // child.iter.status() is set to Status::TryAgain indicating asynchronous |
1160 | | // request for retrieval of data blocks has been submitted. So it should |
1161 | | // return at this point and Seek should be called again to retrieve the |
1162 | | // requested block and add the child to min heap. |
1163 | 9.95k | if (children_[level].iter.status().IsTryAgain()) { |
1164 | 0 | continue; |
1165 | 0 | } |
1166 | 9.95k | { |
1167 | | // Strictly, we timed slightly more than min heap operation, |
1168 | | // but these operations are very cheap. |
1169 | 9.95k | PERF_TIMER_GUARD(seek_max_heap_time); |
1170 | 9.95k | AddToMaxHeapOrCheckStatus(&children_[level]); |
1171 | 9.95k | } |
1172 | 9.95k | } |
1173 | | |
1174 | 3.03k | if (range_tombstone_iters_.empty()) { |
1175 | 3.40k | for (auto& child : children_) { |
1176 | 3.40k | if (child.iter.status().IsTryAgain()) { |
1177 | 0 | child.iter.SeekForPrev(target); |
1178 | 0 | { |
1179 | 0 | PERF_TIMER_GUARD(seek_min_heap_time); |
1180 | 0 | AddToMaxHeapOrCheckStatus(&child); |
1181 | 0 | } |
1182 | 0 | PERF_COUNTER_ADD(number_async_seek, 1); |
1183 | 0 | } |
1184 | 3.40k | } |
1185 | 1.94k | } else { |
1186 | 1.94k | for (auto& prefetch : prefetched_target) { |
1187 | | // (level, target) pairs |
1188 | 0 | children_[prefetch.first].iter.SeekForPrev(prefetch.second); |
1189 | 0 | { |
1190 | 0 | PERF_TIMER_GUARD(seek_max_heap_time); |
1191 | 0 | AddToMaxHeapOrCheckStatus(&children_[prefetch.first]); |
1192 | 0 | } |
1193 | 0 | PERF_COUNTER_ADD(number_async_seek, 1); |
1194 | 0 | } |
1195 | 1.94k | } |
1196 | 3.03k | } |
1197 | | |
1198 | | // See more in comments above SkipNextDeleted(). |
1199 | | // REQUIRES: |
1200 | | // - max heap is currently not empty, and iter is in kReverse direction. |
1201 | | // - maxHeap_ top is not DELETE_RANGE_END (so that `active_` is current). |
1202 | 3.51k | bool MergingIterator::SkipPrevDeleted() { |
1203 | | // 3 types of keys: |
1204 | | // - point key |
1205 | | // - file boundary sentinel keys |
1206 | | // - range deletion start key |
1207 | 3.51k | auto current = maxHeap_->top(); |
1208 | 3.51k | if (current->type == HeapItem::Type::DELETE_RANGE_START) { |
1209 | 0 | active_.erase(current->level); |
1210 | 0 | assert(range_tombstone_iters_[current->level] && |
1211 | 0 | range_tombstone_iters_[current->level]->Valid()); |
1212 | 0 | range_tombstone_iters_[current->level]->Prev(); |
1213 | 0 | if (range_tombstone_iters_[current->level]->Valid()) { |
1214 | 0 | InsertRangeTombstoneToMaxHeap(current->level, true /* end_key */, |
1215 | 0 | true /* replace_top */); |
1216 | 0 | } else { |
1217 | 0 | maxHeap_->pop(); |
1218 | 0 | } |
1219 | 0 | return true /* current key deleted */; |
1220 | 0 | } |
1221 | 3.51k | if (current->iter.IsDeleteRangeSentinelKey()) { |
1222 | | // LevelIterator enters a new SST file |
1223 | 3.51k | maxHeap_->pop(); |
1224 | | // Remove last SST file's range tombstone key if there is one. |
1225 | 3.51k | if (!maxHeap_->empty() && maxHeap_->top()->level == current->level && |
1226 | 0 | maxHeap_->top()->type == HeapItem::Type::DELETE_RANGE_START) { |
1227 | 0 | maxHeap_->pop(); |
1228 | 0 | active_.erase(current->level); |
1229 | 0 | } |
1230 | 3.51k | current->iter.Prev(); |
1231 | 3.51k | if (current->iter.Valid()) { |
1232 | 1.83k | assert(current->iter.status().ok()); |
1233 | 1.83k | maxHeap_->push(current); |
1234 | 1.83k | } else { |
1235 | 1.68k | considerStatus(current->iter.status()); |
1236 | 1.68k | } |
1237 | | |
1238 | 3.51k | if (range_tombstone_iters_[current->level] && |
1239 | 0 | range_tombstone_iters_[current->level]->Valid()) { |
1240 | 0 | InsertRangeTombstoneToMaxHeap(current->level); |
1241 | 0 | } |
1242 | 3.51k | return true /* current key deleted */; |
1243 | 3.51k | } |
1244 | 3.51k | assert(current->type == HeapItem::Type::ITERATOR); |
1245 | | // Point key case: check active_ for range tombstone coverage. |
1246 | 0 | ParsedInternalKey pik; |
1247 | 0 | ParseInternalKey(current->iter.key(), &pik, false).PermitUncheckedError(); |
1248 | 0 | if (!active_.empty()) { |
1249 | 0 | auto i = *active_.begin(); |
1250 | 0 | if (i < current->level) { |
1251 | | // range tombstone is from a newer level, definitely covers |
1252 | 0 | assert(comparator_->Compare(range_tombstone_iters_[i]->start_key(), |
1253 | 0 | pik) <= 0); |
1254 | 0 | assert(comparator_->Compare(pik, range_tombstone_iters_[i]->end_key()) < |
1255 | 0 | 0); |
1256 | 0 | std::string target; |
1257 | 0 | AppendInternalKey(&target, range_tombstone_iters_[i]->start_key()); |
1258 | | // This is different from SkipNextDeleted() which does reseek at sorted |
1259 | | // runs >= level (instead of i+1 here). With min heap, if level L is at |
1260 | | // top of the heap, then levels <L all have internal keys > level L's |
1261 | | // current internal key, which means levels <L are already at a different |
1262 | | // user key. With max heap, if level L is at top of the heap, then levels |
1263 | | // <L all have internal keys smaller than level L's current internal key, |
1264 | | // which might still be the same user key. |
1265 | 0 | SeekForPrevImpl(target, i + 1, true); |
1266 | 0 | return true /* current key deleted */; |
1267 | 0 | } else if (i == current->level) { |
1268 | | // By `active_` we know current key is between start key and end key. |
1269 | 0 | assert(comparator_->Compare(range_tombstone_iters_[i]->start_key(), |
1270 | 0 | pik) <= 0); |
1271 | 0 | assert(comparator_->Compare(pik, range_tombstone_iters_[i]->end_key()) < |
1272 | 0 | 0); |
1273 | 0 | if (pik.sequence < range_tombstone_iters_[current->level]->seq()) { |
1274 | 0 | current->iter.Prev(); |
1275 | 0 | if (current->iter.Valid()) { |
1276 | 0 | maxHeap_->replace_top(current); |
1277 | 0 | } else { |
1278 | 0 | considerStatus(current->iter.status()); |
1279 | 0 | maxHeap_->pop(); |
1280 | 0 | } |
1281 | 0 | return true /* current key deleted */; |
1282 | 0 | } else { |
1283 | 0 | return false /* current key not deleted */; |
1284 | 0 | } |
1285 | 0 | } else { |
1286 | 0 | return false /* current key not deleted */; |
1287 | 0 | } |
1288 | 0 | } |
1289 | | |
1290 | 0 | assert(active_.empty()); |
1291 | 0 | assert(maxHeap_->top()->type == HeapItem::Type::ITERATOR); |
1292 | 0 | return false /* current key not deleted */; |
1293 | 0 | } |
1294 | | |
1295 | 19.3k | void MergingIterator::AddToMinHeapOrCheckStatus(HeapItem* child) { |
1296 | | // Invariant(children_) |
1297 | 19.3k | if (child->iter.Valid()) { |
1298 | 12.0k | assert(child->iter.status().ok()); |
1299 | 12.0k | minHeap_.push(child); |
1300 | 12.0k | } else { |
1301 | 7.22k | considerStatus(child->iter.status()); |
1302 | 7.22k | } |
1303 | 19.3k | } |
1304 | | |
1305 | 11.8k | void MergingIterator::AddToMaxHeapOrCheckStatus(HeapItem* child) { |
1306 | 11.8k | if (child->iter.Valid()) { |
1307 | 6.94k | assert(child->iter.status().ok()); |
1308 | 6.94k | maxHeap_->push(child); |
1309 | 6.94k | } else { |
1310 | 4.91k | considerStatus(child->iter.status()); |
1311 | 4.91k | } |
1312 | 11.8k | } |
1313 | | |
1314 | | // Advance all non current_ child to > current_.key(). |
1315 | | // We advance current_ after the this function call as it does not require |
1316 | | // Seek(). |
1317 | | // Advance all range tombstones iters, including the one corresponding to |
1318 | | // current_, to the first tombstone with end_key > current_.key(). |
1319 | | // TODO: potentially do cascading seek here too |
1320 | | // TODO: show that invariants hold |
1321 | 0 | void MergingIterator::SwitchToForward() { |
1322 | 0 | ClearHeaps(); |
1323 | 0 | Slice target = key(); |
1324 | 0 | for (auto& child : children_) { |
1325 | 0 | if (&child.iter != current_) { |
1326 | 0 | child.iter.Seek(target); |
1327 | | // child.iter.status() is set to Status::TryAgain indicating asynchronous |
1328 | | // request for retrieval of data blocks has been submitted. So it should |
1329 | | // return at this point and Seek should be called again to retrieve the |
1330 | | // requested block and add the child to min heap. |
1331 | 0 | if (child.iter.status() == Status::TryAgain()) { |
1332 | 0 | continue; |
1333 | 0 | } |
1334 | 0 | if (child.iter.Valid() && comparator_->Equal(target, child.iter.key())) { |
1335 | 0 | assert(child.iter.status().ok()); |
1336 | 0 | child.iter.Next(); |
1337 | 0 | } |
1338 | 0 | } |
1339 | 0 | AddToMinHeapOrCheckStatus(&child); |
1340 | 0 | } |
1341 | |
|
1342 | 0 | for (auto& child : children_) { |
1343 | 0 | if (child.iter.status() == Status::TryAgain()) { |
1344 | 0 | child.iter.Seek(target); |
1345 | 0 | if (child.iter.Valid() && comparator_->Equal(target, child.iter.key())) { |
1346 | 0 | assert(child.iter.status().ok()); |
1347 | 0 | child.iter.Next(); |
1348 | 0 | } |
1349 | 0 | AddToMinHeapOrCheckStatus(&child); |
1350 | 0 | } |
1351 | 0 | } |
1352 | | |
1353 | | // Current range tombstone iter also needs to seek for the following case: |
1354 | | // Previous direction is backward, so range tombstone iter may point to a |
1355 | | // tombstone before current_. If there is no such tombstone, then the range |
1356 | | // tombstone iter is !Valid(). Need to reseek here to make it valid again. |
1357 | 0 | if (!range_tombstone_iters_.empty()) { |
1358 | 0 | ParsedInternalKey pik; |
1359 | 0 | ParseInternalKey(target, &pik, false /* log_err_key */) |
1360 | 0 | .PermitUncheckedError(); |
1361 | 0 | for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) { |
1362 | 0 | UnownedPtr<TruncatedRangeDelIterator> iter = |
1363 | 0 | range_tombstone_iters_[i].get(); |
1364 | 0 | if (iter) { |
1365 | 0 | iter->Seek(pik.user_key); |
1366 | | // The while loop is needed as the Seek() call above is only for user |
1367 | | // key. We could have a range tombstone with end_key covering user_key, |
1368 | | // but still is smaller than target. This happens when the range |
1369 | | // tombstone is truncated at iter.largest_. |
1370 | 0 | while (iter->Valid() && |
1371 | 0 | comparator_->Compare(iter->end_key(), pik) <= 0) { |
1372 | 0 | iter->Next(); |
1373 | 0 | } |
1374 | 0 | if (range_tombstone_iters_[i]->Valid()) { |
1375 | 0 | InsertRangeTombstoneToMinHeap( |
1376 | 0 | i, comparator_->Compare(range_tombstone_iters_[i]->start_key(), |
1377 | 0 | pik) > 0 /* start_key */); |
1378 | 0 | } |
1379 | 0 | } |
1380 | 0 | } |
1381 | 0 | } |
1382 | |
|
1383 | 0 | direction_ = kForward; |
1384 | 0 | assert(current_ == CurrentForward()); |
1385 | 0 | } |
1386 | | |
1387 | | // Advance all range tombstones iters, including the one corresponding to |
1388 | | // current_, to the first tombstone with start_key <= current_.key(). |
1389 | 463 | void MergingIterator::SwitchToBackward() { |
1390 | 463 | ClearHeaps(); |
1391 | 463 | InitMaxHeap(); |
1392 | 463 | Slice target = key(); |
1393 | 1.90k | for (auto& child : children_) { |
1394 | 1.90k | if (&child.iter != current_) { |
1395 | 1.44k | child.iter.SeekForPrev(target); |
1396 | 1.44k | TEST_SYNC_POINT_CALLBACK("MergeIterator::Prev:BeforePrev", &child); |
1397 | 1.44k | if (child.iter.Valid() && comparator_->Equal(target, child.iter.key())) { |
1398 | 0 | assert(child.iter.status().ok()); |
1399 | 0 | child.iter.Prev(); |
1400 | 0 | } |
1401 | 1.44k | } |
1402 | 1.90k | AddToMaxHeapOrCheckStatus(&child); |
1403 | 1.90k | } |
1404 | | |
1405 | 463 | ParsedInternalKey pik; |
1406 | 463 | ParseInternalKey(target, &pik, false /* log_err_key */) |
1407 | 463 | .PermitUncheckedError(); |
1408 | 2.01k | for (size_t i = 0; i < range_tombstone_iters_.size(); ++i) { |
1409 | 1.54k | UnownedPtr<TruncatedRangeDelIterator> iter = |
1410 | 1.54k | range_tombstone_iters_[i].get(); |
1411 | 1.54k | if (iter) { |
1412 | 0 | iter->SeekForPrev(pik.user_key); |
1413 | | // Since the SeekForPrev() call above is only for user key, |
1414 | | // we may end up with some range tombstone with start key having the |
1415 | | // same user key at current_, but with a smaller sequence number. This |
1416 | | // makes current_ not at maxHeap_ top for the CurrentReverse() call |
1417 | | // below. If there is a range tombstone start key with the same user |
1418 | | // key and the same sequence number as current_.key(), it will be fine as |
1419 | | // in InsertRangeTombstoneToMaxHeap() we change op_type to be the smallest |
1420 | | // op_type. |
1421 | 0 | while (iter->Valid() && |
1422 | 0 | comparator_->Compare(iter->start_key(), pik) > 0) { |
1423 | 0 | iter->Prev(); |
1424 | 0 | } |
1425 | 0 | if (iter->Valid()) { |
1426 | 0 | InsertRangeTombstoneToMaxHeap( |
1427 | 0 | i, comparator_->Compare(range_tombstone_iters_[i]->end_key(), |
1428 | 0 | pik) <= 0 /* end_key */); |
1429 | 0 | } |
1430 | 0 | } |
1431 | 1.54k | } |
1432 | | |
1433 | 463 | direction_ = kReverse; |
1434 | 463 | if (!prefix_seek_mode_) { |
1435 | | // Note that we don't do assert(current_ == CurrentReverse()) here |
1436 | | // because it is possible to have some keys larger than the seek-key |
1437 | | // inserted between Seek() and SeekToLast(), which makes current_ not |
1438 | | // equal to CurrentReverse(). |
1439 | 463 | current_ = CurrentReverse(); |
1440 | 463 | } |
1441 | 463 | assert(current_ == CurrentReverse()); |
1442 | 463 | } |
1443 | | |
1444 | 10.8k | void MergingIterator::ClearHeaps(bool clear_active) { |
1445 | 10.8k | minHeap_.clear(); |
1446 | 10.8k | if (maxHeap_) { |
1447 | 926 | maxHeap_->clear(); |
1448 | 926 | } |
1449 | 10.8k | if (clear_active) { |
1450 | 7.13k | active_.clear(); |
1451 | 7.13k | } |
1452 | 10.8k | } |
1453 | | |
1454 | 3.49k | void MergingIterator::InitMaxHeap() { |
1455 | 3.49k | if (!maxHeap_) { |
1456 | 3.03k | maxHeap_ = |
1457 | 3.03k | std::make_unique<MergerMaxIterHeap>(MaxHeapItemComparator(comparator_)); |
1458 | 3.03k | } |
1459 | 3.49k | } |
1460 | | |
1461 | | // Assume there is a next key that is not covered by range tombstone. |
1462 | | // Pre-condition: |
1463 | | // - Invariants (3) and (4) |
1464 | | // - There is some k where k <= children_[i].iter.key() <= LevelNextVisible(i, |
1465 | | // k) for all levels i (LevelNextVisible() defined in Seek()). |
1466 | | // |
1467 | | // Define NextVisible(k) to be the first key >= k from among children_ that |
1468 | | // is not covered by any range tombstone. |
1469 | | // Post-condition: |
1470 | | // - Invariants (1)-(4) hold |
1471 | | // - (*): minHeap_->top()->key() == NextVisible(k) |
1472 | | // |
1473 | | // Loop invariants: |
1474 | | // - Invariants (3) and (4) |
1475 | | // - (*): k <= children_[i].iter.key() <= LevelNextVisible(i, k) |
1476 | | // |
1477 | | // Progress: minHeap_.top()->key() is non-decreasing and strictly increases in |
1478 | | // a finite number of iterations. |
1479 | | // TODO: it is possible to call SeekImpl(k2) after SeekImpl(k1) with |
1480 | | // k2 < k1 in the same FindNextVisibleKey(). For example, l1 has a range |
1481 | | // tombstone [2,3) and l2 has a range tombstone [1, 4). Point key 1 from l5 |
1482 | | // triggers SeekImpl(4 /* target */, 5). Then point key 2 from l3 triggers |
1483 | | // SeekImpl(3 /* target */, 3). |
1484 | | // Ideally we should only move iterators forward in SeekImpl(), and the |
1485 | | // progress condition can be made simpler: iterator only moves forward. |
1486 | | // |
1487 | | // Proof sketch: |
1488 | | // Post-condition: |
1489 | | // Invariant (1) holds when this method returns: |
1490 | | // Ignoring the empty minHeap_ case, there are two cases: |
1491 | | // Case 1: active_ is empty and !minHeap_.top()->iter.IsDeleteRangeSentinelKey() |
1492 | | // By invariants (rti) and (active_), active_ being empty means if a |
1493 | | // pinned_heap_item_[i] is in minHeap_, it has type DELETE_RANGE_START. Note |
1494 | | // that PopDeleteRangeStart() was called right before the while loop condition, |
1495 | | // so minHeap_.top() is not of type DELETE_RANGE_START. So minHeap_.top() must |
1496 | | // be of type ITERATOR. |
1497 | | // Case 2: SkipNextDeleted() returns false. The method returns false only when |
1498 | | // minHeap_.top().type == ITERATOR. |
1499 | | // |
1500 | | // Invariant (2) holds when this method returns: |
1501 | | // From Invariant (1), minHeap_.top().type == ITERATOR. Suppose it is |
1502 | | // children_[i] for some i. Suppose that children_[i].iter.key() is covered by |
1503 | | // some range tombstone. This means there is a j <= i and a range tombstone from |
1504 | | // level j with start_key() < children_[i].iter.key() < end_key(). |
1505 | | // - If range_tombstone_iters_[j]->Valid(), by Invariants (rti) and (phi), |
1506 | | // pinned_heap_item_[j] is in minHeap_, and pinned_heap_item_[j].tombstone_pik |
1507 | | // is either start or end key of this range tombstone. If |
1508 | | // pinned_heap_item_[j].tombstone_pik < children_[i].iter.key(), it would be at |
1509 | | // top of minHeap_ which would contradict Invariant (1). So |
1510 | | // pinned_heap_item_[j].tombstone_pik > children_[i].iter.key(). |
1511 | | // By Invariant (3), range_tombstone_iters_[j].prev.end_key() < |
1512 | | // children_[i].iter.key(). We assume that in each level, range tombstones |
1513 | | // cover non-overlapping ranges. So range_tombstone_iters_[j] is at |
1514 | | // the range tombstone with start_key() < children_[i].iter.key() < end_key() |
1515 | | // and has its end_key() in minHeap_. By Invariants (phi) and (active_), |
1516 | | // j is in active_. From while loop condition, SkipNextDeleted() must have |
1517 | | // returned false for this method to return. |
1518 | | // - If j < i, then SeekImpl(range_tombstone_iters_[j']->end_key(), i) |
1519 | | // was called for some j' < i and j' in active_. Note that since j' is in |
1520 | | // active_, pinned_heap_item_[j'] is in minHeap_ and has tombstone_pik = |
1521 | | // range_tombstone_iters_[j']->end_key(). So |
1522 | | // range_tombstone_iters_[j']->end_key() must be larger than |
1523 | | // children_[i].iter.key() to not be at top of minHeap_. This means after |
1524 | | // SeekImpl(), children_[i] would be at a key > children_[i].iter.key() |
1525 | | // -- contradiction. |
1526 | | // - If j == i, children_[i]->Next() would have been called and children_[i] |
1527 | | // would be at a key > children_[i].iter.key() -- contradiction. |
1528 | | // - If !range_tombstone_iters_[j]->Valid(). Then range_tombstone_iters_[j] |
1529 | | // points to an SST file with all range tombstones from that file exhausted. |
1530 | | // The file must come before the file containing the first |
1531 | | // range tombstone with start_key() < children_[i].iter.key() < end_key(). |
1532 | | // Assume files from same level have non-overlapping ranges, the current file's |
1533 | | // meta.largest is less than children_[i].iter.key(). So the file boundary key, |
1534 | | // which has value meta.largest must have been popped from minHeap_ before |
1535 | | // children_[i].iter.key(). So range_tombstone_iters_[j] would not point to |
1536 | | // this SST file -- contradiction. |
1537 | | // So it is impossible for children_[i].iter.key() to be covered by a range |
1538 | | // tombstone. |
1539 | | // |
1540 | | // Post-condition (*) holds when the function returns: |
1541 | | // From loop invariant (*) that k <= children_[i].iter.key() <= |
1542 | | // LevelNextVisible(i, k) and Invariant (2) above, when the function returns, |
1543 | | // minHeap_.top()->key() is the smallest LevelNextVisible(i, k) among all levels |
1544 | | // i. This is equal to NextVisible(k). |
1545 | | // |
1546 | | // Invariant (3) holds after each iteration: |
1547 | | // PopDeleteRangeStart() does not change range tombstone position. |
1548 | | // In SkipNextDeleted(): |
1549 | | // - If DELETE_RANGE_END is popped from minHeap_, it means the range |
1550 | | // tombstone's end key is < all other point keys, so it is safe to advance to |
1551 | | // next range tombstone. |
1552 | | // - If file boundary is popped (current->iter.IsDeleteRangeSentinelKey()), |
1553 | | // we assume that file's last range tombstone's |
1554 | | // end_key <= file boundary key < all other point keys. So it is safe to |
1555 | | // move to the first range tombstone in the next SST file. |
1556 | | // - If children_[i]->Next() is called, then it is fine as it is advancing a |
1557 | | // point iterator. |
1558 | | // - If SeekImpl(target, l) is called, then (3) follows from SeekImpl()'s |
1559 | | // post-condition if its pre-condition holds. First pre-condition follows |
1560 | | // from loop invariant where Invariant (3) holds for all levels i. |
1561 | | // Now we should second pre-condition holds. Since Invariant (3) holds for |
1562 | | // all i, we have for all j <= l, range_tombstone_iters_[j].prev.end_key() |
1563 | | // < children_[l].iter.key(). `target` is the value of |
1564 | | // range_tombstone_iters_[j'].end_key() for some j' < l and j' in active_. |
1565 | | // By Invariant (active_) and (rti), pinned_heap_item_[j'] is in minHeap_ and |
1566 | | // pinned_heap_item_[j'].tombstone_pik = range_tombstone_iters_[j'].end_key(). |
1567 | | // This end_key must be larger than children_[l].key() since it was not at top |
1568 | | // of minHeap_. So for all levels j <= l, |
1569 | | // range_tombstone_iters_[j].prev.end_key() < children_[l].iter.key() < target |
1570 | | // |
1571 | | // Invariant (4) holds after each iteration: |
1572 | | // A level i is inserted into active_ during calls to PopDeleteRangeStart(). |
1573 | | // In that case, range_tombstone_iters_[i].start_key() < all point keys |
1574 | | // by heap property and the assumption that point keys and range tombstone keys |
1575 | | // are distinct. |
1576 | | // If SeekImpl(target, l) is called, then there is a range_tombstone_iters_[j] |
1577 | | // where target = range_tombstone_iters_[j]->end_key() and children_[l]->key() |
1578 | | // < target. By loop invariants, (3) and (4) holds for levels. |
1579 | | // Since target > children_[l]->key(), it also holds that for j < l, |
1580 | | // range_tombstone_iters_[j].prev.end_key() < target and that if j in active_, |
1581 | | // range_tombstone_iters_[i]->start_key() < target. So all pre-conditions of |
1582 | | // SeekImpl(target, l) holds, and (4) follow from its post-condition. |
1583 | | // All other places either in this function either advance point iterators |
1584 | | // or remove some level from active_, so (4) still holds. |
1585 | | // |
1586 | | // Look Invariant (*): for all level i, k <= children_[i] <= LevelNextVisible(i, |
1587 | | // k). |
1588 | | // k <= children_[i] follows from loop `progress` condition. |
1589 | | // Consider when children_[i] is changed for any i. It is through |
1590 | | // children_[i].iter.Next() or SeekImpl() in SkipNextDeleted(). |
1591 | | // If children_[i].iter.Next() is called, there is a range tombstone from level |
1592 | | // i where tombstone seqno > children_[i].iter.key()'s seqno and i in active_. |
1593 | | // By Invariant (4), tombstone's start_key < children_[i].iter.key(). By |
1594 | | // invariants (active_), (phi), and (rti), tombstone's end_key is in minHeap_ |
1595 | | // and that children_[i].iter.key() < end_key. So children_[i].iter.key() is |
1596 | | // not visible, and it is safe to call Next(). |
1597 | | // If SeekImpl(target, l) is called, by its contract, when SeekImpl() returns, |
1598 | | // target <= children_[i]->key() <= LevelNextVisible(i, target) for i >= l, |
1599 | | // and children_[<l] is not touched. We know `target` is |
1600 | | // range_tombstone_iters_[j]->end_key() for some j < i and j is in active_. |
1601 | | // By Invariant (4), range_tombstone_iters_[j]->start_key() < |
1602 | | // children_[i].iter.key() for all i >= l. So for each level i >= l, the range |
1603 | | // [children_[i].iter.key(), target) is not visible. So after SeekImpl(), |
1604 | | // children_[i].iter.key() <= LevelNextVisible(i, target) <= |
1605 | | // LevelNextVisible(i, k). |
1606 | | // |
1607 | | // `Progress` holds for each iteration: |
1608 | | // Very sloppy intuition: |
1609 | | // - in PopDeleteRangeStart(): the value of a pinned_heap_item_.tombstone_pik_ |
1610 | | // is updated from the start key to the end key of the same range tombstone. |
1611 | | // We assume that start key <= end key for the same range tombstone. |
1612 | | // - in SkipNextDeleted() |
1613 | | // - If the top of heap is DELETE_RANGE_END, the range tombstone is advanced |
1614 | | // and the relevant pinned_heap_item_.tombstone_pik is increased or popped |
1615 | | // from minHeap_. |
1616 | | // - If the top of heap is a file boundary key, then both point iter and |
1617 | | // range tombstone iter are advanced to the next file. |
1618 | | // - If the top of heap is ITERATOR and current->iter.Next() is called, it |
1619 | | // moves to a larger point key. |
1620 | | // - If the top of heap is ITERATOR and SeekImpl(k, l) is called, then all |
1621 | | // iterators from levels >= l are advanced to some key >= k by its contract. |
1622 | | // And top of minHeap_ before SeekImpl(k, l) was less than k. |
1623 | | // There are special cases where different heap items have the same key, |
1624 | | // e.g. when two range tombstone end keys share the same value). In |
1625 | | // these cases, iterators are being advanced, so the minimum key should increase |
1626 | | // in a finite number of steps. |
1627 | 58.0k | inline void MergingIterator::FindNextVisibleKey() { |
1628 | 58.0k | PopDeleteRangeStart(); |
1629 | | // PopDeleteRangeStart() implies heap top is not DELETE_RANGE_START |
1630 | | // active_ being empty implies no DELETE_RANGE_END in heap. |
1631 | | // So minHeap_->top() must be of type ITERATOR. |
1632 | 58.0k | while ( |
1633 | 154k | !minHeap_.empty() && |
1634 | 148k | (!active_.empty() || minHeap_.top()->iter.IsDeleteRangeSentinelKey()) && |
1635 | 110k | SkipNextDeleted()) { |
1636 | 96.9k | PopDeleteRangeStart(); |
1637 | 96.9k | } |
1638 | | // Checks Invariant (1) |
1639 | 58.0k | assert(minHeap_.empty() || minHeap_.top()->type == HeapItem::Type::ITERATOR); |
1640 | 58.0k | } |
1641 | | |
1642 | 14.0k | inline void MergingIterator::FindPrevVisibleKey() { |
1643 | 14.0k | PopDeleteRangeEnd(); |
1644 | | // PopDeleteRangeEnd() implies heap top is not DELETE_RANGE_END |
1645 | | // active_ being empty implies no DELETE_RANGE_START in heap. |
1646 | | // So maxHeap_->top() must be of type ITERATOR. |
1647 | 14.0k | while ( |
1648 | 17.5k | !maxHeap_->empty() && |
1649 | 14.5k | (!active_.empty() || maxHeap_->top()->iter.IsDeleteRangeSentinelKey()) && |
1650 | 3.51k | SkipPrevDeleted()) { |
1651 | 3.51k | PopDeleteRangeEnd(); |
1652 | 3.51k | } |
1653 | 14.0k | } |
1654 | | |
1655 | | InternalIterator* NewMergingIterator(const InternalKeyComparator* cmp, |
1656 | | InternalIterator** list, int n, |
1657 | 2.33k | Arena* arena, bool prefix_seek_mode) { |
1658 | 2.33k | assert(n >= 0); |
1659 | 2.33k | if (n == 0) { |
1660 | 0 | return NewEmptyInternalIterator<Slice>(arena); |
1661 | 2.33k | } else if (n == 1) { |
1662 | 2.33k | return list[0]; |
1663 | 2.33k | } else { |
1664 | 0 | if (arena == nullptr) { |
1665 | 0 | return new MergingIterator(cmp, list, n, false, prefix_seek_mode); |
1666 | 0 | } else { |
1667 | 0 | auto mem = arena->AllocateAligned(sizeof(MergingIterator)); |
1668 | 0 | return new (mem) MergingIterator(cmp, list, n, true, prefix_seek_mode); |
1669 | 0 | } |
1670 | 0 | } |
1671 | 2.33k | } |
1672 | | |
1673 | | MergeIteratorBuilder::MergeIteratorBuilder( |
1674 | | const InternalKeyComparator* comparator, Arena* a, bool prefix_seek_mode, |
1675 | | const Slice* iterate_upper_bound) |
1676 | 16.1k | : first_iter(nullptr), use_merging_iter(false), arena(a) { |
1677 | 16.1k | auto mem = arena->AllocateAligned(sizeof(MergingIterator)); |
1678 | 16.1k | merge_iter = new (mem) MergingIterator(comparator, nullptr, 0, true, |
1679 | 16.1k | prefix_seek_mode, iterate_upper_bound); |
1680 | 16.1k | } |
1681 | | |
1682 | 16.1k | MergeIteratorBuilder::~MergeIteratorBuilder() { |
1683 | 16.1k | if (first_iter != nullptr) { |
1684 | 0 | first_iter->~InternalIterator(); |
1685 | 0 | } |
1686 | 16.1k | if (merge_iter != nullptr) { |
1687 | 6.41k | merge_iter->~MergingIterator(); |
1688 | 6.41k | } |
1689 | 16.1k | } |
1690 | | |
1691 | 4.72k | void MergeIteratorBuilder::AddIterator(InternalIterator* iter) { |
1692 | 4.72k | if (!use_merging_iter && first_iter != nullptr) { |
1693 | 0 | merge_iter->AddIterator(first_iter); |
1694 | 0 | use_merging_iter = true; |
1695 | 0 | first_iter = nullptr; |
1696 | 0 | } |
1697 | 4.72k | if (use_merging_iter) { |
1698 | 0 | merge_iter->AddIterator(iter); |
1699 | 4.72k | } else { |
1700 | 4.72k | first_iter = iter; |
1701 | 4.72k | } |
1702 | 4.72k | } |
1703 | | |
1704 | | void MergeIteratorBuilder::AddPointAndTombstoneIterator( |
1705 | | InternalIterator* point_iter, |
1706 | | std::unique_ptr<TruncatedRangeDelIterator>&& tombstone_iter, |
1707 | 28.1k | std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr) { |
1708 | | // tombstone_iter_ptr != nullptr means point_iter is a LevelIterator. |
1709 | 28.1k | bool add_range_tombstone = tombstone_iter || |
1710 | 25.3k | !merge_iter->range_tombstone_iters_.empty() || |
1711 | 25.3k | tombstone_iter_ptr; |
1712 | 28.1k | if (!use_merging_iter && (add_range_tombstone || first_iter)) { |
1713 | 9.70k | use_merging_iter = true; |
1714 | 9.70k | if (first_iter) { |
1715 | 9.70k | merge_iter->AddIterator(first_iter); |
1716 | 9.70k | first_iter = nullptr; |
1717 | 9.70k | } |
1718 | 9.70k | } |
1719 | 28.1k | if (use_merging_iter) { |
1720 | 16.7k | merge_iter->AddIterator(point_iter); |
1721 | 16.7k | if (add_range_tombstone) { |
1722 | | // If there was a gap, fill in nullptr as empty range tombstone iterators. |
1723 | 17.7k | while (merge_iter->range_tombstone_iters_.size() < |
1724 | 17.7k | merge_iter->children_.size() - 1) { |
1725 | 11.4k | merge_iter->AddRangeTombstoneIterator(nullptr); |
1726 | 11.4k | } |
1727 | 6.34k | merge_iter->AddRangeTombstoneIterator(std::move(tombstone_iter)); |
1728 | 6.34k | } |
1729 | | |
1730 | 16.7k | if (tombstone_iter_ptr) { |
1731 | | // This is needed instead of setting to &range_tombstone_iters_[i] |
1732 | | // directly here since the memory address of range_tombstone_iters_[i] |
1733 | | // might change during vector resizing. |
1734 | 3.47k | range_del_iter_ptrs_.emplace_back( |
1735 | 3.47k | merge_iter->range_tombstone_iters_.size() - 1, tombstone_iter_ptr); |
1736 | 3.47k | } |
1737 | 16.7k | } else { |
1738 | 11.3k | first_iter = point_iter; |
1739 | 11.3k | } |
1740 | 28.1k | } |
1741 | | |
1742 | 16.1k | InternalIterator* MergeIteratorBuilder::Finish(ArenaWrappedDBIter* db_iter) { |
1743 | 16.1k | InternalIterator* ret = nullptr; |
1744 | 16.1k | TEST_SYNC_POINT_CALLBACK("MergeIteratorBuilder::Finish:UseMergingIterator", |
1745 | 16.1k | &use_merging_iter); |
1746 | 16.1k | if (!use_merging_iter) { |
1747 | 6.41k | ret = first_iter; |
1748 | 6.41k | first_iter = nullptr; |
1749 | 9.70k | } else { |
1750 | 9.70k | for (auto& p : range_del_iter_ptrs_) { |
1751 | 3.47k | *(p.second) = &(merge_iter->range_tombstone_iters_[p.first]); |
1752 | 3.47k | } |
1753 | 9.70k | if (db_iter && !merge_iter->range_tombstone_iters_.empty()) { |
1754 | | // memtable is always the first level, unless it has been pruned |
1755 | 6.34k | if (memtable_pruned_) { |
1756 | 0 | db_iter->SetMemtableRangetombstoneIter(nullptr); |
1757 | 6.34k | } else { |
1758 | 6.34k | db_iter->SetMemtableRangetombstoneIter( |
1759 | 6.34k | &merge_iter->range_tombstone_iters_.front()); |
1760 | 6.34k | } |
1761 | 6.34k | } |
1762 | 9.70k | merge_iter->Finish(); |
1763 | 9.70k | ret = merge_iter; |
1764 | 9.70k | merge_iter = nullptr; |
1765 | 9.70k | } |
1766 | 16.1k | return ret; |
1767 | 16.1k | } |
1768 | | |
1769 | | } // namespace ROCKSDB_NAMESPACE |