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 #include "db/compaction/compaction_iterator.h"
7
8 #include <string>
9 #include <vector>
10
11 #include "db/dbformat.h"
12 #include "port/port.h"
13 #include "test_util/testharness.h"
14 #include "test_util/testutil.h"
15 #include "util/string_util.h"
16 #include "util/vector_iterator.h"
17 #include "utilities/merge_operators.h"
18
19 namespace ROCKSDB_NAMESPACE {
20
21 // Expects no merging attempts.
22 class NoMergingMergeOp : public MergeOperator {
23 public:
FullMergeV2(const MergeOperationInput &,MergeOperationOutput *) const24 bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
25 MergeOperationOutput* /*merge_out*/) const override {
26 ADD_FAILURE();
27 return false;
28 }
PartialMergeMulti(const Slice &,const std::deque<Slice> &,std::string *,Logger *) const29 bool PartialMergeMulti(const Slice& /*key*/,
30 const std::deque<Slice>& /*operand_list*/,
31 std::string* /*new_value*/,
32 Logger* /*logger*/) const override {
33 ADD_FAILURE();
34 return false;
35 }
Name() const36 const char* Name() const override {
37 return "CompactionIteratorTest NoMergingMergeOp";
38 }
39 };
40
41 // Compaction filter that gets stuck when it sees a particular key,
42 // then gets unstuck when told to.
43 // Always returns Decision::kRemove.
44 class StallingFilter : public CompactionFilter {
45 public:
FilterV2(int,const Slice & key,ValueType,const Slice &,std::string *,std::string *) const46 Decision FilterV2(int /*level*/, const Slice& key, ValueType /*type*/,
47 const Slice& /*existing_value*/, std::string* /*new_value*/,
48 std::string* /*skip_until*/) const override {
49 int k = std::atoi(key.ToString().c_str());
50 last_seen.store(k);
51 while (k >= stall_at.load()) {
52 std::this_thread::yield();
53 }
54 return Decision::kRemove;
55 }
56
Name() const57 const char* Name() const override {
58 return "CompactionIteratorTest StallingFilter";
59 }
60
61 // Wait until the filter sees a key >= k and stalls at that key.
62 // If `exact`, asserts that the seen key is equal to k.
WaitForStall(int k,bool exact=true)63 void WaitForStall(int k, bool exact = true) {
64 stall_at.store(k);
65 while (last_seen.load() < k) {
66 std::this_thread::yield();
67 }
68 if (exact) {
69 EXPECT_EQ(k, last_seen.load());
70 }
71 }
72
73 // Filter will stall on key >= stall_at. Advance stall_at to unstall.
74 mutable std::atomic<int> stall_at{0};
75 // Last key the filter was called with.
76 mutable std::atomic<int> last_seen{0};
77 };
78
79 // Compaction filter that filter out all keys.
80 class FilterAllKeysCompactionFilter : public CompactionFilter {
81 public:
FilterV2(int,const Slice &,ValueType,const Slice &,std::string *,std::string *) const82 Decision FilterV2(int /*level*/, const Slice& /*key*/, ValueType /*type*/,
83 const Slice& /*existing_value*/, std::string* /*new_value*/,
84 std::string* /*skip_until*/) const override {
85 return Decision::kRemove;
86 }
87
Name() const88 const char* Name() const override { return "AllKeysCompactionFilter"; }
89 };
90
91 class LoggingForwardVectorIterator : public VectorIterator {
92 public:
93 struct Action {
94 enum class Type {
95 SEEK_TO_FIRST,
96 SEEK,
97 NEXT,
98 };
99
100 Type type;
101 std::string arg;
102
ActionROCKSDB_NAMESPACE::LoggingForwardVectorIterator::Action103 explicit Action(Type _type, std::string _arg = "")
104 : type(_type), arg(_arg) {}
105
operator ==ROCKSDB_NAMESPACE::LoggingForwardVectorIterator::Action106 bool operator==(const Action& rhs) const {
107 return std::tie(type, arg) == std::tie(rhs.type, rhs.arg);
108 }
109 };
110
LoggingForwardVectorIterator(const std::vector<std::string> & keys,const std::vector<std::string> & values)111 LoggingForwardVectorIterator(const std::vector<std::string>& keys,
112 const std::vector<std::string>& values)
113 : VectorIterator(keys, values) {
114 current_ = keys_.size();
115 }
116
SeekToFirst()117 void SeekToFirst() override {
118 log.emplace_back(Action::Type::SEEK_TO_FIRST);
119 VectorIterator::SeekToFirst();
120 }
SeekToLast()121 void SeekToLast() override { assert(false); }
122
Seek(const Slice & target)123 void Seek(const Slice& target) override {
124 log.emplace_back(Action::Type::SEEK, target.ToString());
125 VectorIterator::Seek(target);
126 }
127
SeekForPrev(const Slice &)128 void SeekForPrev(const Slice& /*target*/) override { assert(false); }
129
Next()130 void Next() override {
131 assert(Valid());
132 log.emplace_back(Action::Type::NEXT);
133 VectorIterator::Next();
134 }
Prev()135 void Prev() override { assert(false); }
136
key() const137 Slice key() const override {
138 assert(Valid());
139 return VectorIterator::key();
140 }
value() const141 Slice value() const override {
142 assert(Valid());
143 return VectorIterator::value();
144 }
145
146 std::vector<Action> log;
147 };
148
149 class FakeCompaction : public CompactionIterator::CompactionProxy {
150 public:
level() const151 int level() const override { return 0; }
152
KeyNotExistsBeyondOutputLevel(const Slice &,std::vector<size_t> *) const153 bool KeyNotExistsBeyondOutputLevel(
154 const Slice& /*user_key*/,
155 std::vector<size_t>* /*level_ptrs*/) const override {
156 return is_bottommost_level || key_not_exists_beyond_output_level;
157 }
158
bottommost_level() const159 bool bottommost_level() const override { return is_bottommost_level; }
160
number_levels() const161 int number_levels() const override { return 1; }
162
GetLargestUserKey() const163 Slice GetLargestUserKey() const override {
164 return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
165 }
166
allow_ingest_behind() const167 bool allow_ingest_behind() const override { return is_allow_ingest_behind; }
168
preserve_deletes() const169 bool preserve_deletes() const override { return false; }
170
enable_blob_garbage_collection() const171 bool enable_blob_garbage_collection() const override { return false; }
172
blob_garbage_collection_age_cutoff() const173 double blob_garbage_collection_age_cutoff() const override { return 0.0; }
174
input_version() const175 Version* input_version() const override { return nullptr; }
176
DoesInputReferenceBlobFiles() const177 bool DoesInputReferenceBlobFiles() const override { return false; }
178
real_compaction() const179 const Compaction* real_compaction() const override { return nullptr; }
180
181 bool key_not_exists_beyond_output_level = false;
182
183 bool is_bottommost_level = false;
184
185 bool is_allow_ingest_behind = false;
186 };
187
188 // A simplified snapshot checker which assumes each snapshot has a global
189 // last visible sequence.
190 class TestSnapshotChecker : public SnapshotChecker {
191 public:
TestSnapshotChecker(SequenceNumber last_committed_sequence,const std::unordered_map<SequenceNumber,SequenceNumber> & snapshots={{}})192 explicit TestSnapshotChecker(
193 SequenceNumber last_committed_sequence,
194 const std::unordered_map<SequenceNumber, SequenceNumber>& snapshots = {{}})
195 : last_committed_sequence_(last_committed_sequence),
196 snapshots_(snapshots) {}
197
CheckInSnapshot(SequenceNumber seq,SequenceNumber snapshot_seq) const198 SnapshotCheckerResult CheckInSnapshot(
199 SequenceNumber seq, SequenceNumber snapshot_seq) const override {
200 if (snapshot_seq == kMaxSequenceNumber) {
201 return seq <= last_committed_sequence_
202 ? SnapshotCheckerResult::kInSnapshot
203 : SnapshotCheckerResult::kNotInSnapshot;
204 }
205 assert(snapshots_.count(snapshot_seq) > 0);
206 return seq <= snapshots_.at(snapshot_seq)
207 ? SnapshotCheckerResult::kInSnapshot
208 : SnapshotCheckerResult::kNotInSnapshot;
209 }
210
211 private:
212 SequenceNumber last_committed_sequence_;
213 // A map of valid snapshot to last visible sequence to the snapshot.
214 std::unordered_map<SequenceNumber, SequenceNumber> snapshots_;
215 };
216
217 // Test param:
218 // bool: whether to pass snapshot_checker to compaction iterator.
219 class CompactionIteratorTest : public testing::TestWithParam<bool> {
220 public:
CompactionIteratorTest()221 CompactionIteratorTest()
222 : cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}
223
CompactionIteratorTest(const Comparator * ucmp)224 explicit CompactionIteratorTest(const Comparator* ucmp)
225 : cmp_(ucmp), icmp_(cmp_), snapshots_({}) {}
226
InitIterators(const std::vector<std::string> & ks,const std::vector<std::string> & vs,const std::vector<std::string> & range_del_ks,const std::vector<std::string> & range_del_vs,SequenceNumber last_sequence,SequenceNumber last_committed_sequence=kMaxSequenceNumber,MergeOperator * merge_op=nullptr,CompactionFilter * filter=nullptr,bool bottommost_level=false,SequenceNumber earliest_write_conflict_snapshot=kMaxSequenceNumber,bool key_not_exists_beyond_output_level=false,const std::string * full_history_ts_low=nullptr)227 void InitIterators(
228 const std::vector<std::string>& ks, const std::vector<std::string>& vs,
229 const std::vector<std::string>& range_del_ks,
230 const std::vector<std::string>& range_del_vs,
231 SequenceNumber last_sequence,
232 SequenceNumber last_committed_sequence = kMaxSequenceNumber,
233 MergeOperator* merge_op = nullptr, CompactionFilter* filter = nullptr,
234 bool bottommost_level = false,
235 SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
236 bool key_not_exists_beyond_output_level = false,
237 const std::string* full_history_ts_low = nullptr) {
238 std::unique_ptr<InternalIterator> unfragmented_range_del_iter(
239 new VectorIterator(range_del_ks, range_del_vs, &icmp_));
240 auto tombstone_list = std::make_shared<FragmentedRangeTombstoneList>(
241 std::move(unfragmented_range_del_iter), icmp_);
242 std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
243 new FragmentedRangeTombstoneIterator(tombstone_list, icmp_,
244 kMaxSequenceNumber));
245 range_del_agg_.reset(new CompactionRangeDelAggregator(&icmp_, snapshots_));
246 range_del_agg_->AddTombstones(std::move(range_del_iter));
247
248 std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
249 if (filter || bottommost_level || key_not_exists_beyond_output_level) {
250 compaction_proxy_ = new FakeCompaction();
251 compaction_proxy_->is_bottommost_level = bottommost_level;
252 compaction_proxy_->is_allow_ingest_behind = AllowIngestBehind();
253 compaction_proxy_->key_not_exists_beyond_output_level =
254 key_not_exists_beyond_output_level;
255 compaction.reset(compaction_proxy_);
256 }
257 bool use_snapshot_checker = UseSnapshotChecker() || GetParam();
258 if (use_snapshot_checker || last_committed_sequence < kMaxSequenceNumber) {
259 snapshot_checker_.reset(
260 new TestSnapshotChecker(last_committed_sequence, snapshot_map_));
261 }
262 merge_helper_.reset(
263 new MergeHelper(Env::Default(), cmp_, merge_op, filter, nullptr, false,
264 0 /*latest_snapshot*/, snapshot_checker_.get(),
265 0 /*level*/, nullptr /*statistics*/, &shutting_down_));
266
267 if (c_iter_) {
268 // Since iter_ is still used in ~CompactionIterator(), we call
269 // ~CompactionIterator() first.
270 c_iter_.reset();
271 }
272 iter_.reset(new LoggingForwardVectorIterator(ks, vs));
273 iter_->SeekToFirst();
274 c_iter_.reset(new CompactionIterator(
275 iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
276 earliest_write_conflict_snapshot, snapshot_checker_.get(),
277 Env::Default(), false /* report_detailed_time */, false,
278 range_del_agg_.get(), nullptr /* blob_file_builder */,
279 true /*allow_data_in_errors*/, std::move(compaction), filter,
280 &shutting_down_, /*preserve_deletes_seqnum=*/0,
281 /*manual_compaction_paused=*/nullptr,
282 /*manual_compaction_canceled=*/nullptr, /*info_log=*/nullptr,
283 full_history_ts_low));
284 }
285
AddSnapshot(SequenceNumber snapshot,SequenceNumber last_visible_seq=kMaxSequenceNumber)286 void AddSnapshot(SequenceNumber snapshot,
287 SequenceNumber last_visible_seq = kMaxSequenceNumber) {
288 snapshots_.push_back(snapshot);
289 snapshot_map_[snapshot] = last_visible_seq;
290 }
291
UseSnapshotChecker() const292 virtual bool UseSnapshotChecker() const { return false; }
293
AllowIngestBehind() const294 virtual bool AllowIngestBehind() const { return false; }
295
RunTest(const std::vector<std::string> & input_keys,const std::vector<std::string> & input_values,const std::vector<std::string> & expected_keys,const std::vector<std::string> & expected_values,SequenceNumber last_committed_seq=kMaxSequenceNumber,MergeOperator * merge_operator=nullptr,CompactionFilter * compaction_filter=nullptr,bool bottommost_level=false,SequenceNumber earliest_write_conflict_snapshot=kMaxSequenceNumber,bool key_not_exists_beyond_output_level=false,const std::string * full_history_ts_low=nullptr)296 void RunTest(
297 const std::vector<std::string>& input_keys,
298 const std::vector<std::string>& input_values,
299 const std::vector<std::string>& expected_keys,
300 const std::vector<std::string>& expected_values,
301 SequenceNumber last_committed_seq = kMaxSequenceNumber,
302 MergeOperator* merge_operator = nullptr,
303 CompactionFilter* compaction_filter = nullptr,
304 bool bottommost_level = false,
305 SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
306 bool key_not_exists_beyond_output_level = false,
307 const std::string* full_history_ts_low = nullptr) {
308 InitIterators(input_keys, input_values, {}, {}, kMaxSequenceNumber,
309 last_committed_seq, merge_operator, compaction_filter,
310 bottommost_level, earliest_write_conflict_snapshot,
311 key_not_exists_beyond_output_level, full_history_ts_low);
312 c_iter_->SeekToFirst();
313 for (size_t i = 0; i < expected_keys.size(); i++) {
314 std::string info = "i = " + ToString(i);
315 ASSERT_TRUE(c_iter_->Valid()) << info;
316 ASSERT_OK(c_iter_->status()) << info;
317 ASSERT_EQ(expected_keys[i], c_iter_->key().ToString()) << info;
318 ASSERT_EQ(expected_values[i], c_iter_->value().ToString()) << info;
319 c_iter_->Next();
320 }
321 ASSERT_OK(c_iter_->status());
322 ASSERT_FALSE(c_iter_->Valid());
323 }
324
ClearSnapshots()325 void ClearSnapshots() {
326 snapshots_.clear();
327 snapshot_map_.clear();
328 }
329
330 const Comparator* cmp_;
331 const InternalKeyComparator icmp_;
332 std::vector<SequenceNumber> snapshots_;
333 // A map of valid snapshot to last visible sequence to the snapshot.
334 std::unordered_map<SequenceNumber, SequenceNumber> snapshot_map_;
335 std::unique_ptr<MergeHelper> merge_helper_;
336 std::unique_ptr<LoggingForwardVectorIterator> iter_;
337 std::unique_ptr<CompactionIterator> c_iter_;
338 std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
339 std::unique_ptr<SnapshotChecker> snapshot_checker_;
340 std::atomic<bool> shutting_down_{false};
341 FakeCompaction* compaction_proxy_;
342 };
343
344 // It is possible that the output of the compaction iterator is empty even if
345 // the input is not.
TEST_P(CompactionIteratorTest,EmptyResult)346 TEST_P(CompactionIteratorTest, EmptyResult) {
347 InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
348 test::KeyStr("a", 3, kTypeValue)},
349 {"", "val"}, {}, {}, 5);
350 c_iter_->SeekToFirst();
351 ASSERT_OK(c_iter_->status());
352 ASSERT_FALSE(c_iter_->Valid());
353 }
354
355 // If there is a corruption after a single deletion, the corrupted key should
356 // be preserved.
TEST_P(CompactionIteratorTest,CorruptionAfterSingleDeletion)357 TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
358 InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
359 test::KeyStr("a", 3, kTypeValue, true),
360 test::KeyStr("b", 10, kTypeValue)},
361 {"", "val", "val2"}, {}, {}, 10);
362 c_iter_->SeekToFirst();
363 ASSERT_TRUE(c_iter_->Valid());
364 ASSERT_EQ(test::KeyStr("a", 5, kTypeSingleDeletion),
365 c_iter_->key().ToString());
366 c_iter_->Next();
367 ASSERT_TRUE(c_iter_->Valid());
368 ASSERT_EQ(test::KeyStr("a", 3, kTypeValue, true), c_iter_->key().ToString());
369 c_iter_->Next();
370 ASSERT_TRUE(c_iter_->Valid());
371 ASSERT_EQ(test::KeyStr("b", 10, kTypeValue), c_iter_->key().ToString());
372 c_iter_->Next();
373 ASSERT_OK(c_iter_->status());
374 ASSERT_FALSE(c_iter_->Valid());
375 }
376
TEST_P(CompactionIteratorTest,SimpleRangeDeletion)377 TEST_P(CompactionIteratorTest, SimpleRangeDeletion) {
378 InitIterators({test::KeyStr("morning", 5, kTypeValue),
379 test::KeyStr("morning", 2, kTypeValue),
380 test::KeyStr("night", 3, kTypeValue)},
381 {"zao", "zao", "wan"},
382 {test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
383 c_iter_->SeekToFirst();
384 ASSERT_TRUE(c_iter_->Valid());
385 ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
386 c_iter_->Next();
387 ASSERT_TRUE(c_iter_->Valid());
388 ASSERT_EQ(test::KeyStr("night", 3, kTypeValue), c_iter_->key().ToString());
389 c_iter_->Next();
390 ASSERT_OK(c_iter_->status());
391 ASSERT_FALSE(c_iter_->Valid());
392 }
393
TEST_P(CompactionIteratorTest,RangeDeletionWithSnapshots)394 TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
395 AddSnapshot(10);
396 std::vector<std::string> ks1;
397 ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
398 std::vector<std::string> vs1{"mz"};
399 std::vector<std::string> ks2{test::KeyStr("morning", 15, kTypeValue),
400 test::KeyStr("morning", 5, kTypeValue),
401 test::KeyStr("night", 40, kTypeValue),
402 test::KeyStr("night", 20, kTypeValue)};
403 std::vector<std::string> vs2{"zao 15", "zao 5", "wan 40", "wan 20"};
404 InitIterators(ks2, vs2, ks1, vs1, 40);
405 c_iter_->SeekToFirst();
406 ASSERT_TRUE(c_iter_->Valid());
407 ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
408 c_iter_->Next();
409 ASSERT_TRUE(c_iter_->Valid());
410 ASSERT_EQ(test::KeyStr("night", 40, kTypeValue), c_iter_->key().ToString());
411 c_iter_->Next();
412 ASSERT_OK(c_iter_->status());
413 ASSERT_FALSE(c_iter_->Valid());
414 }
415
TEST_P(CompactionIteratorTest,CompactionFilterSkipUntil)416 TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
417 class Filter : public CompactionFilter {
418 Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
419 const Slice& existing_value, std::string* /*new_value*/,
420 std::string* skip_until) const override {
421 std::string k = key.ToString();
422 std::string v = existing_value.ToString();
423 // See InitIterators() call below for the sequence of keys and their
424 // filtering decisions. Here we closely assert that compaction filter is
425 // called with the expected keys and only them, and with the right values.
426 if (k == "a") {
427 EXPECT_EQ(ValueType::kValue, t);
428 EXPECT_EQ("av50", v);
429 return Decision::kKeep;
430 }
431 if (k == "b") {
432 EXPECT_EQ(ValueType::kValue, t);
433 EXPECT_EQ("bv60", v);
434 *skip_until = "d+";
435 return Decision::kRemoveAndSkipUntil;
436 }
437 if (k == "e") {
438 EXPECT_EQ(ValueType::kMergeOperand, t);
439 EXPECT_EQ("em71", v);
440 return Decision::kKeep;
441 }
442 if (k == "f") {
443 if (v == "fm65") {
444 EXPECT_EQ(ValueType::kMergeOperand, t);
445 *skip_until = "f";
446 } else {
447 EXPECT_EQ("fm30", v);
448 EXPECT_EQ(ValueType::kMergeOperand, t);
449 *skip_until = "g+";
450 }
451 return Decision::kRemoveAndSkipUntil;
452 }
453 if (k == "h") {
454 EXPECT_EQ(ValueType::kValue, t);
455 EXPECT_EQ("hv91", v);
456 return Decision::kKeep;
457 }
458 if (k == "i") {
459 EXPECT_EQ(ValueType::kMergeOperand, t);
460 EXPECT_EQ("im95", v);
461 *skip_until = "z";
462 return Decision::kRemoveAndSkipUntil;
463 }
464 ADD_FAILURE();
465 return Decision::kKeep;
466 }
467
468 const char* Name() const override {
469 return "CompactionIteratorTest.CompactionFilterSkipUntil::Filter";
470 }
471 };
472
473 NoMergingMergeOp merge_op;
474 Filter filter;
475 InitIterators(
476 {test::KeyStr("a", 50, kTypeValue), // keep
477 test::KeyStr("a", 45, kTypeMerge),
478 test::KeyStr("b", 60, kTypeValue), // skip to "d+"
479 test::KeyStr("b", 40, kTypeValue), test::KeyStr("c", 35, kTypeValue),
480 test::KeyStr("d", 70, kTypeMerge),
481 test::KeyStr("e", 71, kTypeMerge), // keep
482 test::KeyStr("f", 65, kTypeMerge), // skip to "f", aka keep
483 test::KeyStr("f", 30, kTypeMerge), // skip to "g+"
484 test::KeyStr("f", 25, kTypeValue), test::KeyStr("g", 90, kTypeValue),
485 test::KeyStr("h", 91, kTypeValue), // keep
486 test::KeyStr("i", 95, kTypeMerge), // skip to "z"
487 test::KeyStr("j", 99, kTypeValue)},
488 {"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
489 "fv25", "gv90", "hv91", "im95", "jv99"},
490 {}, {}, kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
491
492 // Compaction should output just "a", "e" and "h" keys.
493 c_iter_->SeekToFirst();
494 ASSERT_TRUE(c_iter_->Valid());
495 ASSERT_EQ(test::KeyStr("a", 50, kTypeValue), c_iter_->key().ToString());
496 ASSERT_EQ("av50", c_iter_->value().ToString());
497 c_iter_->Next();
498 ASSERT_TRUE(c_iter_->Valid());
499 ASSERT_EQ(test::KeyStr("e", 71, kTypeMerge), c_iter_->key().ToString());
500 ASSERT_EQ("em71", c_iter_->value().ToString());
501 c_iter_->Next();
502 ASSERT_TRUE(c_iter_->Valid());
503 ASSERT_EQ(test::KeyStr("h", 91, kTypeValue), c_iter_->key().ToString());
504 ASSERT_EQ("hv91", c_iter_->value().ToString());
505 c_iter_->Next();
506 ASSERT_OK(c_iter_->status());
507 ASSERT_FALSE(c_iter_->Valid());
508
509 // Check that the compaction iterator did the correct sequence of calls on
510 // the underlying iterator.
511 using A = LoggingForwardVectorIterator::Action;
512 using T = A::Type;
513 std::vector<A> expected_actions = {
514 A(T::SEEK_TO_FIRST),
515 A(T::NEXT),
516 A(T::NEXT),
517 A(T::SEEK, test::KeyStr("d+", kMaxSequenceNumber, kValueTypeForSeek)),
518 A(T::NEXT),
519 A(T::NEXT),
520 A(T::SEEK, test::KeyStr("g+", kMaxSequenceNumber, kValueTypeForSeek)),
521 A(T::NEXT),
522 A(T::SEEK, test::KeyStr("z", kMaxSequenceNumber, kValueTypeForSeek))};
523 ASSERT_EQ(expected_actions, iter_->log);
524 }
525
TEST_P(CompactionIteratorTest,ShuttingDownInFilter)526 TEST_P(CompactionIteratorTest, ShuttingDownInFilter) {
527 NoMergingMergeOp merge_op;
528 StallingFilter filter;
529 InitIterators(
530 {test::KeyStr("1", 1, kTypeValue), test::KeyStr("2", 2, kTypeValue),
531 test::KeyStr("3", 3, kTypeValue), test::KeyStr("4", 4, kTypeValue)},
532 {"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
533 &merge_op, &filter);
534 // Don't leave tombstones (kTypeDeletion) for filtered keys.
535 compaction_proxy_->key_not_exists_beyond_output_level = true;
536
537 std::atomic<bool> seek_done{false};
538 ROCKSDB_NAMESPACE::port::Thread compaction_thread([&] {
539 c_iter_->SeekToFirst();
540 EXPECT_FALSE(c_iter_->Valid());
541 EXPECT_TRUE(c_iter_->status().IsShutdownInProgress());
542 seek_done.store(true);
543 });
544
545 // Let key 1 through.
546 filter.WaitForStall(1);
547
548 // Shutdown during compaction filter call for key 2.
549 filter.WaitForStall(2);
550 shutting_down_.store(true);
551 EXPECT_FALSE(seek_done.load());
552
553 // Unstall filter and wait for SeekToFirst() to return.
554 filter.stall_at.store(3);
555 compaction_thread.join();
556 assert(seek_done.load());
557
558 // Check that filter was never called again.
559 EXPECT_EQ(2, filter.last_seen.load());
560 }
561
562 // Same as ShuttingDownInFilter, but shutdown happens during filter call for
563 // a merge operand, not for a value.
TEST_P(CompactionIteratorTest,ShuttingDownInMerge)564 TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
565 NoMergingMergeOp merge_op;
566 StallingFilter filter;
567 InitIterators(
568 {test::KeyStr("1", 1, kTypeValue), test::KeyStr("2", 2, kTypeMerge),
569 test::KeyStr("3", 3, kTypeMerge), test::KeyStr("4", 4, kTypeValue)},
570 {"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
571 &merge_op, &filter);
572 compaction_proxy_->key_not_exists_beyond_output_level = true;
573
574 std::atomic<bool> seek_done{false};
575 ROCKSDB_NAMESPACE::port::Thread compaction_thread([&] {
576 c_iter_->SeekToFirst();
577 ASSERT_FALSE(c_iter_->Valid());
578 ASSERT_TRUE(c_iter_->status().IsShutdownInProgress());
579 seek_done.store(true);
580 });
581
582 // Let key 1 through.
583 filter.WaitForStall(1);
584
585 // Shutdown during compaction filter call for key 2.
586 filter.WaitForStall(2);
587 shutting_down_.store(true);
588 EXPECT_FALSE(seek_done.load());
589
590 // Unstall filter and wait for SeekToFirst() to return.
591 filter.stall_at.store(3);
592 compaction_thread.join();
593 assert(seek_done.load());
594
595 // Check that filter was never called again.
596 EXPECT_EQ(2, filter.last_seen.load());
597 }
598
TEST_P(CompactionIteratorTest,SingleMergeOperand)599 TEST_P(CompactionIteratorTest, SingleMergeOperand) {
600 class Filter : public CompactionFilter {
601 Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
602 const Slice& existing_value, std::string* /*new_value*/,
603 std::string* /*skip_until*/) const override {
604 std::string k = key.ToString();
605 std::string v = existing_value.ToString();
606
607 // See InitIterators() call below for the sequence of keys and their
608 // filtering decisions. Here we closely assert that compaction filter is
609 // called with the expected keys and only them, and with the right values.
610 if (k == "a") {
611 EXPECT_EQ(ValueType::kMergeOperand, t);
612 EXPECT_EQ("av1", v);
613 return Decision::kKeep;
614 } else if (k == "b") {
615 EXPECT_EQ(ValueType::kMergeOperand, t);
616 return Decision::kKeep;
617 } else if (k == "c") {
618 return Decision::kKeep;
619 }
620
621 ADD_FAILURE();
622 return Decision::kKeep;
623 }
624
625 const char* Name() const override {
626 return "CompactionIteratorTest.SingleMergeOperand::Filter";
627 }
628 };
629
630 class SingleMergeOp : public MergeOperator {
631 public:
632 bool FullMergeV2(const MergeOperationInput& merge_in,
633 MergeOperationOutput* merge_out) const override {
634 // See InitIterators() call below for why "c" is the only key for which
635 // FullMergeV2 should be called.
636 EXPECT_EQ("c", merge_in.key.ToString());
637
638 std::string temp_value;
639 if (merge_in.existing_value != nullptr) {
640 temp_value = merge_in.existing_value->ToString();
641 }
642
643 for (auto& operand : merge_in.operand_list) {
644 temp_value.append(operand.ToString());
645 }
646 merge_out->new_value = temp_value;
647
648 return true;
649 }
650
651 bool PartialMergeMulti(const Slice& key,
652 const std::deque<Slice>& operand_list,
653 std::string* new_value,
654 Logger* /*logger*/) const override {
655 std::string string_key = key.ToString();
656 EXPECT_TRUE(string_key == "a" || string_key == "b");
657
658 if (string_key == "a") {
659 EXPECT_EQ(1, operand_list.size());
660 } else if (string_key == "b") {
661 EXPECT_EQ(2, operand_list.size());
662 }
663
664 std::string temp_value;
665 for (auto& operand : operand_list) {
666 temp_value.append(operand.ToString());
667 }
668 swap(temp_value, *new_value);
669
670 return true;
671 }
672
673 const char* Name() const override {
674 return "CompactionIteratorTest SingleMergeOp";
675 }
676
677 bool AllowSingleOperand() const override { return true; }
678 };
679
680 SingleMergeOp merge_op;
681 Filter filter;
682 InitIterators(
683 // a should invoke PartialMergeMulti with a single merge operand.
684 {test::KeyStr("a", 50, kTypeMerge),
685 // b should invoke PartialMergeMulti with two operands.
686 test::KeyStr("b", 70, kTypeMerge), test::KeyStr("b", 60, kTypeMerge),
687 // c should invoke FullMerge due to kTypeValue at the beginning.
688 test::KeyStr("c", 90, kTypeMerge), test::KeyStr("c", 80, kTypeValue)},
689 {"av1", "bv2", "bv1", "cv2", "cv1"}, {}, {}, kMaxSequenceNumber,
690 kMaxSequenceNumber, &merge_op, &filter);
691
692 c_iter_->SeekToFirst();
693 ASSERT_TRUE(c_iter_->Valid());
694 ASSERT_EQ(test::KeyStr("a", 50, kTypeMerge), c_iter_->key().ToString());
695 ASSERT_EQ("av1", c_iter_->value().ToString());
696 c_iter_->Next();
697 ASSERT_TRUE(c_iter_->Valid());
698 ASSERT_EQ("bv1bv2", c_iter_->value().ToString());
699 c_iter_->Next();
700 ASSERT_OK(c_iter_->status());
701 ASSERT_EQ("cv1cv2", c_iter_->value().ToString());
702 }
703
704 // In bottommost level, values earlier than earliest snapshot can be output
705 // with sequence = 0.
TEST_P(CompactionIteratorTest,ZeroOutSequenceAtBottomLevel)706 TEST_P(CompactionIteratorTest, ZeroOutSequenceAtBottomLevel) {
707 AddSnapshot(1);
708 RunTest({test::KeyStr("a", 1, kTypeValue), test::KeyStr("b", 2, kTypeValue)},
709 {"v1", "v2"},
710 {test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 2, kTypeValue)},
711 {"v1", "v2"}, kMaxSequenceNumber /*last_committed_seq*/,
712 nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
713 true /*bottommost_level*/);
714 }
715
716 // In bottommost level, deletions earlier than earliest snapshot can be removed
717 // permanently.
TEST_P(CompactionIteratorTest,RemoveDeletionAtBottomLevel)718 TEST_P(CompactionIteratorTest, RemoveDeletionAtBottomLevel) {
719 AddSnapshot(1);
720 RunTest(
721 {test::KeyStr("a", 1, kTypeDeletion), test::KeyStr("b", 3, kTypeDeletion),
722 test::KeyStr("b", 1, kTypeValue)},
723 {"", "", ""},
724 {test::KeyStr("b", 3, kTypeDeletion), test::KeyStr("b", 0, kTypeValue)},
725 {"", ""}, kMaxSequenceNumber /*last_committed_seq*/,
726 nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
727 true /*bottommost_level*/);
728 }
729
730 // In bottommost level, single deletions earlier than earliest snapshot can be
731 // removed permanently.
TEST_P(CompactionIteratorTest,RemoveSingleDeletionAtBottomLevel)732 TEST_P(CompactionIteratorTest, RemoveSingleDeletionAtBottomLevel) {
733 AddSnapshot(1);
734 RunTest({test::KeyStr("a", 1, kTypeSingleDeletion),
735 test::KeyStr("b", 2, kTypeSingleDeletion)},
736 {"", ""}, {test::KeyStr("b", 2, kTypeSingleDeletion)}, {""},
737 kMaxSequenceNumber /*last_committed_seq*/, nullptr /*merge_operator*/,
738 nullptr /*compaction_filter*/, true /*bottommost_level*/);
739 }
740
TEST_P(CompactionIteratorTest,ConvertToPutAtBottom)741 TEST_P(CompactionIteratorTest, ConvertToPutAtBottom) {
742 std::shared_ptr<MergeOperator> merge_op =
743 MergeOperators::CreateStringAppendOperator();
744 RunTest({test::KeyStr("a", 4, kTypeMerge), test::KeyStr("a", 3, kTypeMerge),
745 test::KeyStr("a", 2, kTypeMerge), test::KeyStr("b", 1, kTypeValue)},
746 {"a4", "a3", "a2", "b1"},
747 {test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 0, kTypeValue)},
748 {"a2,a3,a4", "b1"}, kMaxSequenceNumber /*last_committed_seq*/,
749 merge_op.get(), nullptr /*compaction_filter*/,
750 true /*bottomost_level*/);
751 }
752
753 INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
754 testing::Values(true, false));
755
756 // Tests how CompactionIterator work together with SnapshotChecker.
757 class CompactionIteratorWithSnapshotCheckerTest
758 : public CompactionIteratorTest {
759 public:
UseSnapshotChecker() const760 bool UseSnapshotChecker() const override { return true; }
761 };
762
763 // Uncommitted keys (keys with seq > last_committed_seq) should be output as-is
764 // while committed version of these keys should get compacted as usual.
765
TEST_F(CompactionIteratorWithSnapshotCheckerTest,PreserveUncommittedKeys_Value)766 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
767 PreserveUncommittedKeys_Value) {
768 RunTest(
769 {test::KeyStr("foo", 3, kTypeValue), test::KeyStr("foo", 2, kTypeValue),
770 test::KeyStr("foo", 1, kTypeValue)},
771 {"v3", "v2", "v1"},
772 {test::KeyStr("foo", 3, kTypeValue), test::KeyStr("foo", 2, kTypeValue)},
773 {"v3", "v2"}, 2 /*last_committed_seq*/);
774 }
775
TEST_F(CompactionIteratorWithSnapshotCheckerTest,PreserveUncommittedKeys_Deletion)776 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
777 PreserveUncommittedKeys_Deletion) {
778 RunTest({test::KeyStr("foo", 2, kTypeDeletion),
779 test::KeyStr("foo", 1, kTypeValue)},
780 {"", "v1"},
781 {test::KeyStr("foo", 2, kTypeDeletion),
782 test::KeyStr("foo", 1, kTypeValue)},
783 {"", "v1"}, 1 /*last_committed_seq*/);
784 }
785
TEST_F(CompactionIteratorWithSnapshotCheckerTest,PreserveUncommittedKeys_Merge)786 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
787 PreserveUncommittedKeys_Merge) {
788 auto merge_op = MergeOperators::CreateStringAppendOperator();
789 RunTest(
790 {test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 2, kTypeMerge),
791 test::KeyStr("foo", 1, kTypeValue)},
792 {"v3", "v2", "v1"},
793 {test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 2, kTypeValue)},
794 {"v3", "v1,v2"}, 2 /*last_committed_seq*/, merge_op.get());
795 }
796
TEST_F(CompactionIteratorWithSnapshotCheckerTest,PreserveUncommittedKeys_SingleDelete)797 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
798 PreserveUncommittedKeys_SingleDelete) {
799 RunTest({test::KeyStr("foo", 2, kTypeSingleDeletion),
800 test::KeyStr("foo", 1, kTypeValue)},
801 {"", "v1"},
802 {test::KeyStr("foo", 2, kTypeSingleDeletion),
803 test::KeyStr("foo", 1, kTypeValue)},
804 {"", "v1"}, 1 /*last_committed_seq*/);
805 }
806
TEST_F(CompactionIteratorWithSnapshotCheckerTest,PreserveUncommittedKeys_BlobIndex)807 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
808 PreserveUncommittedKeys_BlobIndex) {
809 RunTest({test::KeyStr("foo", 3, kTypeBlobIndex),
810 test::KeyStr("foo", 2, kTypeBlobIndex),
811 test::KeyStr("foo", 1, kTypeBlobIndex)},
812 {"v3", "v2", "v1"},
813 {test::KeyStr("foo", 3, kTypeBlobIndex),
814 test::KeyStr("foo", 2, kTypeBlobIndex)},
815 {"v3", "v2"}, 2 /*last_committed_seq*/);
816 }
817
818 // Test compaction iterator dedup keys visible to the same snapshot.
819
TEST_F(CompactionIteratorWithSnapshotCheckerTest,DedupSameSnapshot_Value)820 TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Value) {
821 AddSnapshot(2, 1);
822 RunTest(
823 {test::KeyStr("foo", 4, kTypeValue), test::KeyStr("foo", 3, kTypeValue),
824 test::KeyStr("foo", 2, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
825 {"v4", "v3", "v2", "v1"},
826 {test::KeyStr("foo", 4, kTypeValue), test::KeyStr("foo", 3, kTypeValue),
827 test::KeyStr("foo", 1, kTypeValue)},
828 {"v4", "v3", "v1"}, 3 /*last_committed_seq*/);
829 }
830
TEST_F(CompactionIteratorWithSnapshotCheckerTest,DedupSameSnapshot_Deletion)831 TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Deletion) {
832 AddSnapshot(2, 1);
833 RunTest(
834 {test::KeyStr("foo", 4, kTypeValue),
835 test::KeyStr("foo", 3, kTypeDeletion),
836 test::KeyStr("foo", 2, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
837 {"v4", "", "v2", "v1"},
838 {test::KeyStr("foo", 4, kTypeValue),
839 test::KeyStr("foo", 3, kTypeDeletion),
840 test::KeyStr("foo", 1, kTypeValue)},
841 {"v4", "", "v1"}, 3 /*last_committed_seq*/);
842 }
843
TEST_F(CompactionIteratorWithSnapshotCheckerTest,DedupSameSnapshot_Merge)844 TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Merge) {
845 AddSnapshot(2, 1);
846 AddSnapshot(4, 3);
847 auto merge_op = MergeOperators::CreateStringAppendOperator();
848 RunTest(
849 {test::KeyStr("foo", 5, kTypeMerge), test::KeyStr("foo", 4, kTypeMerge),
850 test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 2, kTypeMerge),
851 test::KeyStr("foo", 1, kTypeValue)},
852 {"v5", "v4", "v3", "v2", "v1"},
853 {test::KeyStr("foo", 5, kTypeMerge), test::KeyStr("foo", 4, kTypeMerge),
854 test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 1, kTypeValue)},
855 {"v5", "v4", "v2,v3", "v1"}, 4 /*last_committed_seq*/, merge_op.get());
856 }
857
TEST_F(CompactionIteratorWithSnapshotCheckerTest,DedupSameSnapshot_SingleDeletion)858 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
859 DedupSameSnapshot_SingleDeletion) {
860 AddSnapshot(2, 1);
861 RunTest(
862 {test::KeyStr("foo", 4, kTypeValue),
863 test::KeyStr("foo", 3, kTypeSingleDeletion),
864 test::KeyStr("foo", 2, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
865 {"v4", "", "v2", "v1"},
866 {test::KeyStr("foo", 4, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
867 {"v4", "v1"}, 3 /*last_committed_seq*/);
868 }
869
TEST_F(CompactionIteratorWithSnapshotCheckerTest,DedupSameSnapshot_BlobIndex)870 TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_BlobIndex) {
871 AddSnapshot(2, 1);
872 RunTest({test::KeyStr("foo", 4, kTypeBlobIndex),
873 test::KeyStr("foo", 3, kTypeBlobIndex),
874 test::KeyStr("foo", 2, kTypeBlobIndex),
875 test::KeyStr("foo", 1, kTypeBlobIndex)},
876 {"v4", "v3", "v2", "v1"},
877 {test::KeyStr("foo", 4, kTypeBlobIndex),
878 test::KeyStr("foo", 3, kTypeBlobIndex),
879 test::KeyStr("foo", 1, kTypeBlobIndex)},
880 {"v4", "v3", "v1"}, 3 /*last_committed_seq*/);
881 }
882
883 // At bottom level, sequence numbers can be zero out, and deletions can be
884 // removed, but only when they are visible to earliest snapshot.
885
TEST_F(CompactionIteratorWithSnapshotCheckerTest,NotZeroOutSequenceIfNotVisibleToEarliestSnapshot)886 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
887 NotZeroOutSequenceIfNotVisibleToEarliestSnapshot) {
888 AddSnapshot(2, 1);
889 RunTest({test::KeyStr("a", 1, kTypeValue), test::KeyStr("b", 2, kTypeValue),
890 test::KeyStr("c", 3, kTypeValue)},
891 {"v1", "v2", "v3"},
892 {test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 2, kTypeValue),
893 test::KeyStr("c", 3, kTypeValue)},
894 {"v1", "v2", "v3"}, kMaxSequenceNumber /*last_committed_seq*/,
895 nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
896 true /*bottommost_level*/);
897 }
898
TEST_F(CompactionIteratorWithSnapshotCheckerTest,NotRemoveDeletionIfNotVisibleToEarliestSnapshot)899 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
900 NotRemoveDeletionIfNotVisibleToEarliestSnapshot) {
901 AddSnapshot(2, 1);
902 RunTest(
903 {test::KeyStr("a", 1, kTypeDeletion), test::KeyStr("b", 2, kTypeDeletion),
904 test::KeyStr("c", 3, kTypeDeletion)},
905 {"", "", ""}, {}, {"", ""}, kMaxSequenceNumber /*last_committed_seq*/,
906 nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
907 true /*bottommost_level*/);
908 }
909
TEST_F(CompactionIteratorWithSnapshotCheckerTest,NotRemoveDeletionIfValuePresentToEarlierSnapshot)910 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
911 NotRemoveDeletionIfValuePresentToEarlierSnapshot) {
912 AddSnapshot(2,1);
913 RunTest({test::KeyStr("a", 4, kTypeDeletion),
914 test::KeyStr("a", 1, kTypeValue), test::KeyStr("b", 3, kTypeValue)},
915 {"", "", ""},
916 {test::KeyStr("a", 4, kTypeDeletion),
917 test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 3, kTypeValue)},
918 {"", "", ""}, kMaxSequenceNumber /*last_committed_seq*/,
919 nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
920 true /*bottommost_level*/);
921 }
922
TEST_F(CompactionIteratorWithSnapshotCheckerTest,NotRemoveSingleDeletionIfNotVisibleToEarliestSnapshot)923 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
924 NotRemoveSingleDeletionIfNotVisibleToEarliestSnapshot) {
925 AddSnapshot(2, 1);
926 RunTest({test::KeyStr("a", 1, kTypeSingleDeletion),
927 test::KeyStr("b", 2, kTypeSingleDeletion),
928 test::KeyStr("c", 3, kTypeSingleDeletion)},
929 {"", "", ""},
930 {test::KeyStr("b", 2, kTypeSingleDeletion),
931 test::KeyStr("c", 3, kTypeSingleDeletion)},
932 {"", ""}, kMaxSequenceNumber /*last_committed_seq*/,
933 nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
934 true /*bottommost_level*/);
935 }
936
937 // Single delete should not cancel out values that not visible to the
938 // same set of snapshots
TEST_F(CompactionIteratorWithSnapshotCheckerTest,SingleDeleteAcrossSnapshotBoundary)939 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
940 SingleDeleteAcrossSnapshotBoundary) {
941 AddSnapshot(2, 1);
942 RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
943 test::KeyStr("a", 1, kTypeValue)},
944 {"", "v1"},
945 {test::KeyStr("a", 2, kTypeSingleDeletion),
946 test::KeyStr("a", 1, kTypeValue)},
947 {"", "v1"}, 2 /*last_committed_seq*/);
948 }
949
950 // Single delete should be kept in case it is not visible to the
951 // earliest write conflict snapshot. If a single delete is kept for this reason,
952 // corresponding value can be trimmed to save space.
TEST_F(CompactionIteratorWithSnapshotCheckerTest,KeepSingleDeletionForWriteConflictChecking)953 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
954 KeepSingleDeletionForWriteConflictChecking) {
955 AddSnapshot(2, 0);
956 RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
957 test::KeyStr("a", 1, kTypeValue)},
958 {"", "v1"},
959 {test::KeyStr("a", 2, kTypeSingleDeletion),
960 test::KeyStr("a", 1, kTypeValue)},
961 {"", ""}, 2 /*last_committed_seq*/, nullptr /*merge_operator*/,
962 nullptr /*compaction_filter*/, false /*bottommost_level*/,
963 2 /*earliest_write_conflict_snapshot*/);
964 }
965
966 // Same as above but with a blob index. In addition to the value getting
967 // trimmed, the type of the KV is changed to kTypeValue.
TEST_F(CompactionIteratorWithSnapshotCheckerTest,KeepSingleDeletionForWriteConflictChecking_BlobIndex)968 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
969 KeepSingleDeletionForWriteConflictChecking_BlobIndex) {
970 AddSnapshot(2, 0);
971 RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
972 test::KeyStr("a", 1, kTypeBlobIndex)},
973 {"", "fake_blob_index"},
974 {test::KeyStr("a", 2, kTypeSingleDeletion),
975 test::KeyStr("a", 1, kTypeValue)},
976 {"", ""}, 2 /*last_committed_seq*/, nullptr /*merge_operator*/,
977 nullptr /*compaction_filter*/, false /*bottommost_level*/,
978 2 /*earliest_write_conflict_snapshot*/);
979 }
980
981 // Compaction filter should keep uncommitted key as-is, and
982 // * Convert the latest value to deletion, and/or
983 // * if latest value is a merge, apply filter to all subsequent merges.
984
TEST_F(CompactionIteratorWithSnapshotCheckerTest,CompactionFilter_Value)985 TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Value) {
986 std::unique_ptr<CompactionFilter> compaction_filter(
987 new FilterAllKeysCompactionFilter());
988 RunTest(
989 {test::KeyStr("a", 2, kTypeValue), test::KeyStr("a", 1, kTypeValue),
990 test::KeyStr("b", 3, kTypeValue), test::KeyStr("c", 1, kTypeValue)},
991 {"v2", "v1", "v3", "v4"},
992 {test::KeyStr("a", 2, kTypeValue), test::KeyStr("a", 1, kTypeDeletion),
993 test::KeyStr("b", 3, kTypeValue), test::KeyStr("c", 1, kTypeDeletion)},
994 {"v2", "", "v3", ""}, 1 /*last_committed_seq*/,
995 nullptr /*merge_operator*/, compaction_filter.get());
996 }
997
TEST_F(CompactionIteratorWithSnapshotCheckerTest,CompactionFilter_Deletion)998 TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Deletion) {
999 std::unique_ptr<CompactionFilter> compaction_filter(
1000 new FilterAllKeysCompactionFilter());
1001 RunTest(
1002 {test::KeyStr("a", 2, kTypeDeletion), test::KeyStr("a", 1, kTypeValue)},
1003 {"", "v1"},
1004 {test::KeyStr("a", 2, kTypeDeletion),
1005 test::KeyStr("a", 1, kTypeDeletion)},
1006 {"", ""}, 1 /*last_committed_seq*/, nullptr /*merge_operator*/,
1007 compaction_filter.get());
1008 }
1009
TEST_F(CompactionIteratorWithSnapshotCheckerTest,CompactionFilter_PartialMerge)1010 TEST_F(CompactionIteratorWithSnapshotCheckerTest,
1011 CompactionFilter_PartialMerge) {
1012 std::shared_ptr<MergeOperator> merge_op =
1013 MergeOperators::CreateStringAppendOperator();
1014 std::unique_ptr<CompactionFilter> compaction_filter(
1015 new FilterAllKeysCompactionFilter());
1016 RunTest({test::KeyStr("a", 3, kTypeMerge), test::KeyStr("a", 2, kTypeMerge),
1017 test::KeyStr("a", 1, kTypeMerge)},
1018 {"v3", "v2", "v1"}, {test::KeyStr("a", 3, kTypeMerge)}, {"v3"},
1019 2 /*last_committed_seq*/, merge_op.get(), compaction_filter.get());
1020 }
1021
TEST_F(CompactionIteratorWithSnapshotCheckerTest,CompactionFilter_FullMerge)1022 TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_FullMerge) {
1023 std::shared_ptr<MergeOperator> merge_op =
1024 MergeOperators::CreateStringAppendOperator();
1025 std::unique_ptr<CompactionFilter> compaction_filter(
1026 new FilterAllKeysCompactionFilter());
1027 RunTest(
1028 {test::KeyStr("a", 3, kTypeMerge), test::KeyStr("a", 2, kTypeMerge),
1029 test::KeyStr("a", 1, kTypeValue)},
1030 {"v3", "v2", "v1"},
1031 {test::KeyStr("a", 3, kTypeMerge), test::KeyStr("a", 1, kTypeDeletion)},
1032 {"v3", ""}, 2 /*last_committed_seq*/, merge_op.get(),
1033 compaction_filter.get());
1034 }
1035
1036 // Tests how CompactionIterator work together with AllowIngestBehind.
1037 class CompactionIteratorWithAllowIngestBehindTest
1038 : public CompactionIteratorTest {
1039 public:
AllowIngestBehind() const1040 bool AllowIngestBehind() const override { return true; }
1041 };
1042
1043 // When allow_ingest_behind is set, compaction iterator is not targeting
1044 // the bottommost level since there is no guarantee there won't be further
1045 // data ingested under the compaction output in future.
TEST_P(CompactionIteratorWithAllowIngestBehindTest,NoConvertToPutAtBottom)1046 TEST_P(CompactionIteratorWithAllowIngestBehindTest, NoConvertToPutAtBottom) {
1047 std::shared_ptr<MergeOperator> merge_op =
1048 MergeOperators::CreateStringAppendOperator();
1049 RunTest({test::KeyStr("a", 4, kTypeMerge), test::KeyStr("a", 3, kTypeMerge),
1050 test::KeyStr("a", 2, kTypeMerge), test::KeyStr("b", 1, kTypeValue)},
1051 {"a4", "a3", "a2", "b1"},
1052 {test::KeyStr("a", 4, kTypeMerge), test::KeyStr("b", 1, kTypeValue)},
1053 {"a2,a3,a4", "b1"}, kMaxSequenceNumber /*last_committed_seq*/,
1054 merge_op.get(), nullptr /*compaction_filter*/,
1055 true /*bottomost_level*/);
1056 }
1057
TEST_P(CompactionIteratorWithAllowIngestBehindTest,MergeToPutIfEncounteredPutAtBottom)1058 TEST_P(CompactionIteratorWithAllowIngestBehindTest,
1059 MergeToPutIfEncounteredPutAtBottom) {
1060 std::shared_ptr<MergeOperator> merge_op =
1061 MergeOperators::CreateStringAppendOperator();
1062 RunTest({test::KeyStr("a", 4, kTypeMerge), test::KeyStr("a", 3, kTypeMerge),
1063 test::KeyStr("a", 2, kTypeValue), test::KeyStr("b", 1, kTypeValue)},
1064 {"a4", "a3", "a2", "b1"},
1065 {test::KeyStr("a", 4, kTypeValue), test::KeyStr("b", 1, kTypeValue)},
1066 {"a2,a3,a4", "b1"}, kMaxSequenceNumber /*last_committed_seq*/,
1067 merge_op.get(), nullptr /*compaction_filter*/,
1068 true /*bottomost_level*/);
1069 }
1070
1071 INSTANTIATE_TEST_CASE_P(CompactionIteratorWithAllowIngestBehindTestInstance,
1072 CompactionIteratorWithAllowIngestBehindTest,
1073 testing::Values(true, false));
1074
1075 class CompactionIteratorTsGcTest : public CompactionIteratorTest {
1076 public:
CompactionIteratorTsGcTest()1077 CompactionIteratorTsGcTest()
1078 : CompactionIteratorTest(test::ComparatorWithU64Ts()) {}
1079 };
1080
TEST_P(CompactionIteratorTsGcTest,NoKeyEligibleForGC)1081 TEST_P(CompactionIteratorTsGcTest, NoKeyEligibleForGC) {
1082 constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
1083 const std::vector<std::string> input_keys = {
1084 test::KeyStr(/*ts=*/103, user_key[0], /*seq=*/4, kTypeValue),
1085 test::KeyStr(/*ts=*/102, user_key[0], /*seq=*/3,
1086 kTypeDeletionWithTimestamp),
1087 test::KeyStr(/*ts=*/104, user_key[1], /*seq=*/5, kTypeValue)};
1088 const std::vector<std::string> input_values = {"a3", "", "b2"};
1089 std::string full_history_ts_low;
1090 // All keys' timestamps are newer than or equal to 102, thus none of them
1091 // will be eligible for GC.
1092 PutFixed64(&full_history_ts_low, 102);
1093 const std::vector<std::string>& expected_keys = input_keys;
1094 const std::vector<std::string>& expected_values = input_values;
1095 const std::vector<std::pair<bool, bool>> params = {
1096 {false, false}, {false, true}, {true, true}};
1097 for (const std::pair<bool, bool>& param : params) {
1098 const bool bottommost_level = param.first;
1099 const bool key_not_exists_beyond_output_level = param.second;
1100 RunTest(input_keys, input_values, expected_keys, expected_values,
1101 /*last_committed_seq=*/kMaxSequenceNumber,
1102 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1103 bottommost_level,
1104 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1105 key_not_exists_beyond_output_level, &full_history_ts_low);
1106 }
1107 }
1108
TEST_P(CompactionIteratorTsGcTest,AllKeysOlderThanThreshold)1109 TEST_P(CompactionIteratorTsGcTest, AllKeysOlderThanThreshold) {
1110 constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
1111 const std::vector<std::string> input_keys = {
1112 test::KeyStr(/*ts=*/103, user_key[0], /*seq=*/4,
1113 kTypeDeletionWithTimestamp),
1114 test::KeyStr(/*ts=*/102, user_key[0], /*seq=*/3, kTypeValue),
1115 test::KeyStr(/*ts=*/101, user_key[0], /*seq=*/2, kTypeValue),
1116 test::KeyStr(/*ts=*/104, user_key[1], /*seq=*/5, kTypeValue)};
1117 const std::vector<std::string> input_values = {"", "a2", "a1", "b5"};
1118 std::string full_history_ts_low;
1119 PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
1120 {
1121 // With a snapshot at seq 3, both the deletion marker and the key at 3 must
1122 // be preserved.
1123 AddSnapshot(3);
1124 const std::vector<std::string> expected_keys = {
1125 input_keys[0], input_keys[1], input_keys[3]};
1126 const std::vector<std::string> expected_values = {"", "a2", "b5"};
1127 RunTest(input_keys, input_values, expected_keys, expected_values,
1128 /*last_committed_seq=*/kMaxSequenceNumber,
1129 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1130 /*bottommost_level=*/false,
1131 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1132 /*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
1133 ClearSnapshots();
1134 }
1135 {
1136 // No snapshot, the deletion marker should be preserved because the user
1137 // key may appear beyond output level.
1138 const std::vector<std::string> expected_keys = {input_keys[0],
1139 input_keys[3]};
1140 const std::vector<std::string> expected_values = {"", "b5"};
1141 RunTest(input_keys, input_values, expected_keys, expected_values,
1142 /*last_committed_seq=*/kMaxSequenceNumber,
1143 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1144 /*bottommost_level=*/false,
1145 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1146 /*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
1147 }
1148 {
1149 // No snapshot, the deletion marker can be dropped because the user key
1150 // does not appear in higher levels.
1151 const std::vector<std::string> expected_keys = {input_keys[3]};
1152 const std::vector<std::string> expected_values = {"b5"};
1153 RunTest(input_keys, input_values, expected_keys, expected_values,
1154 /*last_committed_seq=*/kMaxSequenceNumber,
1155 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1156 /*bottommost_level=*/false,
1157 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1158 /*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
1159 }
1160 }
1161
TEST_P(CompactionIteratorTsGcTest,NewHidesOldSameSnapshot)1162 TEST_P(CompactionIteratorTsGcTest, NewHidesOldSameSnapshot) {
1163 constexpr char user_key[] = "a";
1164 const std::vector<std::string> input_keys = {
1165 test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeDeletionWithTimestamp),
1166 test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
1167 test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeValue),
1168 test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
1169 const std::vector<std::string> input_values = {"", "a2", "a1", "a0"};
1170 {
1171 std::string full_history_ts_low;
1172 // Keys whose timestamps larger than or equal to 102 will be preserved.
1173 PutFixed64(&full_history_ts_low, 102);
1174 const std::vector<std::string> expected_keys = {input_keys[0],
1175 input_keys[1]};
1176 const std::vector<std::string> expected_values = {"", "a2"};
1177 RunTest(input_keys, input_values, expected_keys, expected_values,
1178 /*last_committed_seq=*/kMaxSequenceNumber,
1179 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1180 /*bottommost_level=*/false,
1181 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1182 /*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
1183 }
1184 }
1185
TEST_P(CompactionIteratorTsGcTest,DropTombstones)1186 TEST_P(CompactionIteratorTsGcTest, DropTombstones) {
1187 constexpr char user_key[] = "a";
1188 const std::vector<std::string> input_keys = {
1189 test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeDeletionWithTimestamp),
1190 test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
1191 test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeDeletionWithTimestamp),
1192 test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
1193 const std::vector<std::string> input_values = {"", "a2", "", "a0"};
1194 const std::vector<std::string> expected_keys = {input_keys[0], input_keys[1]};
1195 const std::vector<std::string> expected_values = {"", "a2"};
1196
1197 // Take a snapshot at seq 2.
1198 AddSnapshot(2);
1199
1200 {
1201 // Non-bottommost level, but key does not exist beyond output level.
1202 std::string full_history_ts_low;
1203 PutFixed64(&full_history_ts_low, 102);
1204 RunTest(input_keys, input_values, expected_keys, expected_values,
1205 /*last_committed_sequence=*/kMaxSequenceNumber,
1206 /*merge_op=*/nullptr, /*compaction_filter=*/nullptr,
1207 /*bottommost_level=*/false,
1208 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1209 /*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
1210 }
1211 {
1212 // Bottommost level
1213 std::string full_history_ts_low;
1214 PutFixed64(&full_history_ts_low, 102);
1215 RunTest(input_keys, input_values, expected_keys, expected_values,
1216 /*last_committed_seq=*/kMaxSequenceNumber,
1217 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1218 /*bottommost_level=*/true,
1219 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1220 /*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
1221 }
1222 }
1223
TEST_P(CompactionIteratorTsGcTest,RewriteTs)1224 TEST_P(CompactionIteratorTsGcTest, RewriteTs) {
1225 constexpr char user_key[] = "a";
1226 const std::vector<std::string> input_keys = {
1227 test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeDeletionWithTimestamp),
1228 test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
1229 test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeDeletionWithTimestamp),
1230 test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
1231 const std::vector<std::string> input_values = {"", "a2", "", "a0"};
1232 const std::vector<std::string> expected_keys = {
1233 input_keys[0], input_keys[1], input_keys[2],
1234 test::KeyStr(/*ts=*/0, user_key, /*seq=*/0, kTypeValue)};
1235 const std::vector<std::string> expected_values = {"", "a2", "", "a0"};
1236
1237 AddSnapshot(1);
1238 AddSnapshot(2);
1239
1240 {
1241 // Bottommost level and need to rewrite both ts and seq.
1242 std::string full_history_ts_low;
1243 PutFixed64(&full_history_ts_low, 102);
1244 RunTest(input_keys, input_values, expected_keys, expected_values,
1245 /*last_committed_seq=*/kMaxSequenceNumber,
1246 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1247 /*bottommost_level=*/true,
1248 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1249 /*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
1250 }
1251 }
1252
TEST_P(CompactionIteratorTsGcTest,SingleDeleteNoKeyEligibleForGC)1253 TEST_P(CompactionIteratorTsGcTest, SingleDeleteNoKeyEligibleForGC) {
1254 constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
1255 const std::vector<std::string> input_keys = {
1256 test::KeyStr(/*ts=*/104, user_key[0], /*seq=*/4, kTypeSingleDeletion),
1257 test::KeyStr(/*ts=*/103, user_key[0], /*seq=*/3, kTypeValue),
1258 test::KeyStr(/*ts=*/102, user_key[1], /*seq=*/2, kTypeValue)};
1259 const std::vector<std::string> input_values = {"", "a3", "b2"};
1260 std::string full_history_ts_low;
1261 // All keys' timestamps are newer than or equal to 102, thus none of them
1262 // will be eligible for GC.
1263 PutFixed64(&full_history_ts_low, 102);
1264 const std::vector<std::string>& expected_keys = input_keys;
1265 const std::vector<std::string>& expected_values = input_values;
1266 const std::vector<std::pair<bool, bool>> params = {
1267 {false, false}, {false, true}, {true, true}};
1268 for (const std::pair<bool, bool>& param : params) {
1269 const bool bottommost_level = param.first;
1270 const bool key_not_exists_beyond_output_level = param.second;
1271 RunTest(input_keys, input_values, expected_keys, expected_values,
1272 /*last_committed_seq=*/kMaxSequenceNumber,
1273 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1274 bottommost_level,
1275 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1276 key_not_exists_beyond_output_level, &full_history_ts_low);
1277 }
1278 }
1279
TEST_P(CompactionIteratorTsGcTest,SingleDeleteDropTombstones)1280 TEST_P(CompactionIteratorTsGcTest, SingleDeleteDropTombstones) {
1281 constexpr char user_key[] = "a";
1282 const std::vector<std::string> input_keys = {
1283 test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeSingleDeletion),
1284 test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
1285 test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeSingleDeletion),
1286 test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
1287 const std::vector<std::string> input_values = {"", "a2", "", "a0"};
1288 const std::vector<std::string> expected_keys = {input_keys[0], input_keys[1]};
1289 const std::vector<std::string> expected_values = {"", "a2"};
1290
1291 // Take a snapshot at seq 2.
1292 AddSnapshot(2);
1293 {
1294 const std::vector<std::pair<bool, bool>> params = {
1295 {false, false}, {false, true}, {true, true}};
1296 for (const std::pair<bool, bool>& param : params) {
1297 const bool bottommost_level = param.first;
1298 const bool key_not_exists_beyond_output_level = param.second;
1299 std::string full_history_ts_low;
1300 PutFixed64(&full_history_ts_low, 102);
1301 RunTest(input_keys, input_values, expected_keys, expected_values,
1302 /*last_committed_seq=*/kMaxSequenceNumber,
1303 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1304 bottommost_level,
1305 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1306 key_not_exists_beyond_output_level, &full_history_ts_low);
1307 }
1308 }
1309 }
1310
TEST_P(CompactionIteratorTsGcTest,SingleDeleteAllKeysOlderThanThreshold)1311 TEST_P(CompactionIteratorTsGcTest, SingleDeleteAllKeysOlderThanThreshold) {
1312 constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
1313 const std::vector<std::string> input_keys = {
1314 test::KeyStr(/*ts=*/103, user_key[0], /*seq=*/4, kTypeSingleDeletion),
1315 test::KeyStr(/*ts=*/102, user_key[0], /*seq=*/3, kTypeValue),
1316 test::KeyStr(/*ts=*/104, user_key[1], /*seq=*/5, kTypeValue)};
1317 const std::vector<std::string> input_values = {"", "a2", "b5"};
1318 std::string full_history_ts_low;
1319 PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
1320 {
1321 // With a snapshot at seq 3, both the deletion marker and the key at 3 must
1322 // be preserved.
1323 AddSnapshot(3);
1324 const std::vector<std::string> expected_keys = {
1325 input_keys[0], input_keys[1], input_keys[2]};
1326 const std::vector<std::string> expected_values = {"", "a2", "b5"};
1327 RunTest(input_keys, input_values, expected_keys, expected_values,
1328 /*last_committed_seq=*/kMaxSequenceNumber,
1329 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1330 /*bottommost_level=*/false,
1331 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1332 /*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
1333 ClearSnapshots();
1334 }
1335 {
1336 // No snapshot.
1337 const std::vector<std::string> expected_keys = {input_keys[2]};
1338 const std::vector<std::string> expected_values = {"b5"};
1339 RunTest(input_keys, input_values, expected_keys, expected_values,
1340 /*last_committed_seq=*/kMaxSequenceNumber,
1341 /*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
1342 /*bottommost_level=*/false,
1343 /*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
1344 /*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
1345 }
1346 }
1347
1348 INSTANTIATE_TEST_CASE_P(CompactionIteratorTsGcTestInstance,
1349 CompactionIteratorTsGcTest,
1350 testing::Values(true, false));
1351
1352 } // namespace ROCKSDB_NAMESPACE
1353
main(int argc,char ** argv)1354 int main(int argc, char** argv) {
1355 ::testing::InitGoogleTest(&argc, argv);
1356 return RUN_ALL_TESTS();
1357 }
1358