Coverage Report

Created: 2026-02-14 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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