YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/compaction_job_test.cc
Line
Count
Source
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under the BSD-style license found in the
3
//  LICENSE file in the root directory of this source tree. An additional grant
4
//  of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
21
#ifndef ROCKSDB_LITE
22
23
#include <algorithm>
24
#include <map>
25
#include <string>
26
#include <tuple>
27
28
#include "yb/rocksdb/db/compaction_job.h"
29
#include "yb/rocksdb/db/column_family.h"
30
#include "yb/rocksdb/db/file_numbers.h"
31
#include "yb/rocksdb/db/filename.h"
32
#include "yb/rocksdb/db/version_set.h"
33
#include "yb/rocksdb/db/writebuffer.h"
34
#include "yb/rocksdb/cache.h"
35
#include "yb/rocksdb/db.h"
36
#include "yb/rocksdb/options.h"
37
#include "yb/rocksdb/table/mock_table.h"
38
#include "yb/rocksdb/util/file_reader_writer.h"
39
#include "yb/rocksdb/util/testharness.h"
40
#include "yb/rocksdb/util/testutil.h"
41
#include "yb/rocksdb/utilities/merge_operators.h"
42
43
#include "yb/util/string_util.h"
44
#include "yb/util/test_util.h"
45
46
namespace rocksdb {
47
48
namespace {
49
50
void VerifyInitializationOfCompactionJobStats(
51
18
      const CompactionJobStats& compaction_job_stats) {
52
18
#if !defined(IOS_CROSS_COMPILE)
53
18
  ASSERT_EQ(compaction_job_stats.elapsed_micros, 0U);
54
55
18
  ASSERT_EQ(compaction_job_stats.num_input_records, 0U);
56
18
  ASSERT_EQ(compaction_job_stats.num_input_files, 0U);
57
18
  ASSERT_EQ(compaction_job_stats.num_input_files_at_output_level, 0U);
58
59
18
  ASSERT_EQ(compaction_job_stats.num_output_records, 0U);
60
18
  ASSERT_EQ(compaction_job_stats.num_output_files, 0U);
61
62
18
  ASSERT_EQ(compaction_job_stats.is_manual_compaction, true);
63
64
18
  ASSERT_EQ(compaction_job_stats.total_input_bytes, 0U);
65
18
  ASSERT_EQ(compaction_job_stats.total_output_bytes, 0U);
66
67
18
  ASSERT_EQ(compaction_job_stats.total_input_raw_key_bytes, 0U);
68
18
  ASSERT_EQ(compaction_job_stats.total_input_raw_value_bytes, 0U);
69
70
18
  ASSERT_EQ(compaction_job_stats.smallest_output_key_prefix[0], 0);
71
18
  ASSERT_EQ(compaction_job_stats.largest_output_key_prefix[0], 0);
72
73
18
  ASSERT_EQ(compaction_job_stats.num_records_replaced, 0U);
74
75
18
  ASSERT_EQ(compaction_job_stats.num_input_deletion_records, 0U);
76
18
  ASSERT_EQ(compaction_job_stats.num_expired_deletion_records, 0U);
77
78
18
  ASSERT_EQ(compaction_job_stats.num_corrupt_keys, 0U);
79
18
#endif  // !defined(IOS_CROSS_COMPILE)
80
18
}
81
82
}  // namespace
83
84
// TODO(icanadi) Make it simpler once we mock out VersionSet
85
class CompactionJobTest : public RocksDBTest {
86
 public:
87
  CompactionJobTest()
88
      : env_(Env::Default()),
89
        dbname_(test::TmpDir() + "/compaction_job_test"),
90
        mutable_cf_options_(Options(), ImmutableCFOptions(Options())),
91
        table_cache_(NewLRUCache(50000, 16)),
92
        write_buffer_(db_options_.db_write_buffer_size),
93
        versions_(new VersionSet(dbname_, &db_options_, env_options_,
94
                                 table_cache_.get(), &write_buffer_,
95
                                 &write_controller_)),
96
        shutting_down_(false),
97
18
        mock_table_factory_(new mock::MockTableFactory()) {
98
18
    EXPECT_OK(env_->CreateDirIfMissing(dbname_));
99
18
    db_options_.boundary_extractor = test::MakeBoundaryValuesExtractor();
100
18
    db_options_.db_paths.emplace_back(dbname_,
101
18
                                      std::numeric_limits<uint64_t>::max());
102
18
  }
103
104
43
  std::string GenerateFileName(uint64_t file_number) {
105
43
    FileMetaData meta;
106
43
    std::vector<DbPath> db_paths;
107
43
    db_paths.emplace_back(dbname_, std::numeric_limits<uint64_t>::max());
108
43
    meta.fd = FileDescriptor(file_number, 0, 0, 0);
109
43
    return TableFileName(db_paths, meta.fd.GetNumber(), meta.fd.GetPathId());
110
43
  }
111
112
  std::string KeyStr(const std::string& user_key, const SequenceNumber seq_num,
113
293
      const ValueType t) {
114
293
    return InternalKey(user_key, seq_num, t).Encode().ToBuffer();
115
293
  }
116
117
43
  void AddMockFile(const stl_wrappers::KVMap& contents, int level = 0) {
118
43
    ASSERT_GT(contents.size(), 0);
119
120
43
    bool first_key = true;
121
43
    FileMetaData::BoundaryValues smallest_values, largest_values;
122
43
    smallest_values.seqno = kMaxSequenceNumber;
123
43
    largest_values.seqno = 0;
124
43
    std::string smallest, largest;
125
43
    test::BoundaryTestValues user_values;
126
127
40.2k
    for (auto kv : contents) {
128
40.2k
      ParsedInternalKey key;
129
40.2k
      std::string skey;
130
40.2k
      std::string value;
131
40.2k
      std::tie(skey, value) = kv;
132
40.2k
      ParseInternalKey(skey, &key);
133
134
40.2k
      user_values.Feed(key.user_key);
135
136
40.2k
      smallest_values.seqno = std::min(smallest_values.seqno, key.sequence);
137
40.2k
      largest_values.seqno = std::max(largest_values.seqno, key.sequence);
138
139
40.2k
      if (first_key ||
140
40.1k
          cfd_->user_comparator()->Compare(key.user_key, smallest) < 0) {
141
43
        smallest = key.user_key.ToBuffer();
142
43
        smallest_values.key = InternalKey::DecodeFrom(skey);
143
43
      }
144
40.2k
      if (first_key ||
145
40.1k
          cfd_->user_comparator()->Compare(key.user_key, largest) > 0) {
146
40.1k
        largest = key.user_key.ToBuffer();
147
40.1k
        largest_values.key = InternalKey::DecodeFrom(skey);
148
40.1k
      }
149
150
40.2k
      first_key = false;
151
40.2k
    }
152
153
43
    uint64_t file_number = versions_->NewFileNumber();
154
43
    EXPECT_OK(mock_table_factory_->CreateMockTable(
155
43
        env_, GenerateFileName(file_number), std::move(contents)));
156
157
43
    VersionEdit edit;
158
43
    smallest_values.user_values.push_back(test::MakeIntBoundaryValue(user_values.min_int));
159
43
    smallest_values.user_values.push_back(test::MakeStringBoundaryValue(user_values.min_string));
160
43
    smallest_values.user_frontier = test::TestUserFrontier(smallest_values.seqno).Clone();
161
43
    largest_values.user_values.push_back(test::MakeIntBoundaryValue(user_values.max_int));
162
43
    largest_values.user_values.push_back(test::MakeStringBoundaryValue(user_values.max_string));
163
43
    largest_values.user_frontier = test::TestUserFrontier(largest_values.seqno).Clone();
164
43
    edit.AddTestFile(level,
165
43
                     FileDescriptor(file_number, 0, 10, 10),
166
43
                     smallest_values,
167
43
                     largest_values,
168
43
                     /* marked_for_compaction */ false);
169
170
43
    mutex_.Lock();
171
43
    ASSERT_OK(versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
172
43
                                     mutable_cf_options_, &edit, &mutex_));
173
43
    mutex_.Unlock();
174
43
  }
175
176
18
  void SetLastSequence(const SequenceNumber sequence_number) {
177
18
    versions_->SetLastSequence(sequence_number + 1);
178
18
    test::TestUserFrontier frontier(sequence_number + 1);
179
18
    versions_->UpdateFlushedFrontier(frontier.Clone());
180
18
  }
181
182
  // returns expected result after compaction
183
2
  stl_wrappers::KVMap CreateTwoFiles(bool gen_corrupted_keys) {
184
2
    auto expected_results = mock::MakeMockFile();
185
2
    const int kKeysPerFile = 10000;
186
2
    const int kCorruptKeysPerFile = 200;
187
2
    const int kMatchingKeys = kKeysPerFile / 2;
188
2
    SequenceNumber sequence_number = 0;
189
190
50.0k
    auto corrupt_id = [&](int id) {
191
50.0k
      return gen_corrupted_keys && id > 0 && id <= kCorruptKeysPerFile;
192
50.0k
    };
193
194
6
    for (int i = 0; i < 2; ++i) {
195
4
      auto contents = mock::MakeMockFile();
196
40.0k
      for (int k = 0; k < kKeysPerFile; ++k) {
197
40.0k
        auto key = ToString(i * kMatchingKeys + k);
198
40.0k
        auto value = ToString(i * kKeysPerFile + k);
199
40.0k
        InternalKey internal_key(key, ++sequence_number, kTypeValue);
200
201
        // This is how the key will look like once it's written in bottommost
202
        // file
203
40.0k
        InternalKey bottommost_internal_key(
204
39.9k
            key, (key == "9999") ? sequence_number : 0, kTypeValue);
205
206
40.0k
        if (corrupt_id(k)) {
207
400
          test::CorruptKeyType(&internal_key);
208
400
          test::CorruptKeyType(&bottommost_internal_key);
209
400
        }
210
40.0k
        contents.insert({ internal_key.Encode().ToString(), value });
211
40.0k
        if (i == 1 || k < kMatchingKeys || corrupt_id(k - kMatchingKeys)) {
212
30.2k
          expected_results.insert(
213
30.2k
              { bottommost_internal_key.Encode().ToString(), value });
214
30.2k
        }
215
40.0k
      }
216
217
4
      AddMockFile(contents);
218
4
    }
219
220
2
    SetLastSequence(sequence_number);
221
222
2
    return expected_results;
223
2
  }
224
225
18
  void NewDB() {
226
18
    VersionEdit new_db;
227
18
    new_db.InitNewDB();
228
229
18
    const std::string manifest = DescriptorFileName(dbname_, 1);
230
18
    unique_ptr<WritableFile> file;
231
18
    Status s = env_->NewWritableFile(
232
18
        manifest, &file, env_->OptimizeForManifestWrite(env_options_));
233
18
    ASSERT_OK(s);
234
18
    unique_ptr<WritableFileWriter> file_writer(
235
18
        new WritableFileWriter(std::move(file), env_options_));
236
18
    {
237
18
      log::Writer log(std::move(file_writer), 0, false);
238
18
      std::string record;
239
18
      new_db.AppendEncodedTo(&record);
240
18
      s = log.AddRecord(record);
241
18
    }
242
18
    ASSERT_OK(s);
243
    // Make "CURRENT" file that points to the new manifest file.
244
18
    s = SetCurrentFile(env_, dbname_, 1, /* directory to fsync */ nullptr,
245
18
      db_options_.disableDataSync);
246
247
18
    std::vector<ColumnFamilyDescriptor> column_families;
248
18
    cf_options_.table_factory = mock_table_factory_;
249
18
    cf_options_.merge_operator = merge_op_;
250
18
    cf_options_.compaction_filter = compaction_filter_.get();
251
18
    column_families.emplace_back(kDefaultColumnFamilyName, cf_options_);
252
253
18
    EXPECT_OK(versions_->Recover(column_families, false));
254
18
    cfd_ = versions_->GetColumnFamilySet()->GetDefault();
255
18
  }
256
257
  void RunCompaction(
258
      const std::vector<std::vector<FileMetaData*>>& input_files,
259
      const stl_wrappers::KVMap& expected_results,
260
      const std::vector<SequenceNumber>& snapshots = {},
261
18
      SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
262
18
    auto cfd = versions_->GetColumnFamilySet()->GetDefault();
263
264
18
    size_t num_input_files = 0;
265
18
    std::vector<CompactionInputFiles> compaction_input_files;
266
37
    for (size_t level = 0; level < input_files.size(); level++) {
267
19
      auto level_files = input_files[level];
268
19
      CompactionInputFiles compaction_level;
269
19
      compaction_level.level = static_cast<int>(level);
270
19
      compaction_level.files.insert(compaction_level.files.end(),
271
19
          level_files.begin(), level_files.end());
272
19
      compaction_input_files.push_back(compaction_level);
273
19
      num_input_files += level_files.size();
274
19
    }
275
276
18
    auto compaction = Compaction::Create(
277
18
        cfd->current()->storage_info(), *cfd->GetLatestMutableCFOptions(), compaction_input_files,
278
18
        1, 1024 * 1024, 10, 0, kNoCompression, {}, db_options_.info_log.get(), true);
279
18
    compaction->SetInputVersion(cfd->current());
280
281
18
    LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
282
18
    mutex_.Lock();
283
18
    EventLogger event_logger(db_options_.info_log.get());
284
18
    FileNumbersProvider file_numbers_provider(versions_.get());
285
18
    CompactionJob compaction_job(
286
18
        0, compaction.get(), db_options_, env_options_, versions_.get(),
287
18
        &shutting_down_, &log_buffer, nullptr, nullptr, nullptr, &mutex_,
288
18
        &bg_error_, snapshots, earliest_write_conflict_snapshot, &file_numbers_provider,
289
18
        table_cache_, &event_logger, false, false, dbname_, &compaction_job_stats_);
290
291
18
    VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
292
293
18
    compaction_job.Prepare();
294
18
    mutex_.Unlock();
295
18
    ASSERT_OK(compaction_job.Run());
296
18
    mutex_.Lock();
297
18
    ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions()));
298
18
    mutex_.Unlock();
299
300
18
    if (expected_results.size() == 0) {
301
2
      ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
302
2
      ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
303
2
      ASSERT_EQ(compaction_job_stats_.num_output_files, 0U);
304
16
    } else {
305
16
      ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
306
16
      ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
307
16
      ASSERT_EQ(compaction_job_stats_.num_output_files, 1U);
308
16
      mock_table_factory_->AssertLatestFile(expected_results);
309
16
    }
310
18
  }
311
312
  Env* env_;
313
  std::string dbname_;
314
  EnvOptions env_options_;
315
  MutableCFOptions mutable_cf_options_;
316
  std::shared_ptr<Cache> table_cache_;
317
  WriteController write_controller_;
318
  DBOptions db_options_;
319
  ColumnFamilyOptions cf_options_;
320
  WriteBuffer write_buffer_;
321
  std::unique_ptr<VersionSet> versions_;
322
  InstrumentedMutex mutex_;
323
  std::atomic<bool> shutting_down_;
324
  std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
325
  CompactionJobStats compaction_job_stats_;
326
  ColumnFamilyData* cfd_;
327
  std::unique_ptr<CompactionFilter> compaction_filter_;
328
  std::shared_ptr<MergeOperator> merge_op_;
329
  Status bg_error_;
330
};
331
332
1
TEST_F(CompactionJobTest, Simple) {
333
1
  NewDB();
334
335
1
  auto expected_results = CreateTwoFiles(false);
336
1
  auto cfd = versions_->GetColumnFamilySet()->GetDefault();
337
1
  auto files = cfd->current()->storage_info()->LevelFiles(0);
338
1
  ASSERT_EQ(2U, files.size());
339
1
  RunCompaction({ files }, expected_results);
340
1
}
341
342
1
TEST_F(CompactionJobTest, SimpleCorrupted) {
343
1
  NewDB();
344
345
1
  auto expected_results = CreateTwoFiles(true);
346
1
  auto cfd = versions_->GetColumnFamilySet()->GetDefault();
347
1
  auto files = cfd->current()->storage_info()->LevelFiles(0);
348
1
  RunCompaction({files}, expected_results);
349
1
  ASSERT_EQ(compaction_job_stats_.num_corrupt_keys, 400U);
350
1
}
351
352
1
TEST_F(CompactionJobTest, SimpleDeletion) {
353
1
  NewDB();
354
355
1
  auto file1 = mock::MakeMockFile({{KeyStr("c", 4U, kTypeDeletion), ""},
356
1
                                   {KeyStr("c", 3U, kTypeValue), "val"}});
357
1
  AddMockFile(file1);
358
359
1
  auto file2 = mock::MakeMockFile({{KeyStr("b", 2U, kTypeValue), "val"},
360
1
                                   {KeyStr("b", 1U, kTypeValue), "val"}});
361
1
  AddMockFile(file2);
362
363
1
  auto expected_results =
364
1
      mock::MakeMockFile({{KeyStr("b", 0U, kTypeValue), "val"}});
365
366
1
  SetLastSequence(4U);
367
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
368
1
  RunCompaction({files}, expected_results);
369
1
}
370
371
namespace {
372
373
3
void VerifyFrontier(const FileMetaData& meta, SequenceNumber min, SequenceNumber max) {
374
3
  const auto& sfront = down_cast<test::TestUserFrontier&>(*meta.smallest.user_frontier);
375
3
  const auto& lfront = down_cast<test::TestUserFrontier&>(*meta.largest.user_frontier);
376
3
  ASSERT_EQ(min, sfront.Value());
377
3
  ASSERT_EQ(max, lfront.Value());
378
3
}
379
380
} // namespace
381
382
1
TEST_F(CompactionJobTest, SeqNoTrackingWithFewDeletes) {
383
1
  NewDB();
384
385
1
  auto file1 = mock::MakeMockFile({{KeyStr("c", 4U, kTypeDeletion), ""},
386
1
                                      {KeyStr("c", 3U, kTypeValue), "val"}});
387
1
  AddMockFile(file1);
388
389
1
  auto file2 = mock::MakeMockFile({{KeyStr("b", 2U, kTypeValue), "val"},
390
1
                                      {KeyStr("b", 1U, kTypeValue), "val"}});
391
1
  AddMockFile(file2);
392
393
1
  auto expected_results =
394
1
      mock::MakeMockFile({{KeyStr("b", 0U, kTypeValue), "val"}});
395
396
1
  SetLastSequence(4U);
397
1
  auto level0_files = cfd_->current()->storage_info()->LevelFiles(0);
398
1
  auto level1_files = cfd_->current()->storage_info()->LevelFiles(1);
399
1
  ASSERT_EQ(2, level0_files.size());
400
1
  ASSERT_EQ(0, level1_files.size());
401
1
  auto min_int = std::min(test::GetBoundaryInt(level0_files[0]->smallest.user_values),
402
1
                          test::GetBoundaryInt(level0_files[1]->smallest.user_values));
403
1
  auto max_int = std::max(test::GetBoundaryInt(level0_files[0]->largest.user_values),
404
1
                          test::GetBoundaryInt(level0_files[1]->largest.user_values));
405
1
  ASSERT_LE(min_int, max_int);
406
407
1
  auto min_string = std::min(test::GetBoundaryString(level0_files[0]->smallest.user_values),
408
1
                             test::GetBoundaryString(level0_files[1]->smallest.user_values));
409
1
  auto max_string = std::max(test::GetBoundaryString(level0_files[0]->largest.user_values),
410
1
                             test::GetBoundaryString(level0_files[1]->largest.user_values));
411
1
  ASSERT_LE(min_string, max_string);
412
1
  ASSERT_NO_FATAL_FAILURE(VerifyFrontier(*level0_files[0], 3, 4));
413
1
  ASSERT_NO_FATAL_FAILURE(VerifyFrontier(*level0_files[1], 1, 2));
414
415
1
  RunCompaction({level0_files}, expected_results);
416
417
1
  level0_files = cfd_->current()->storage_info()->LevelFiles(0);
418
1
  level1_files = cfd_->current()->storage_info()->LevelFiles(1);
419
420
1
  ASSERT_EQ(0, level0_files.size());
421
1
  ASSERT_EQ(1, level1_files.size());
422
1
  ASSERT_EQ(4, level1_files[0]->largest.seqno);
423
1
  ASSERT_EQ(0, level1_files[0]->smallest.seqno);  // kv's seq number gets zeroed out.
424
1
  ASSERT_EQ(min_int, test::GetBoundaryInt(level1_files[0]->smallest.user_values));
425
1
  ASSERT_EQ(max_int, test::GetBoundaryInt(level1_files[0]->largest.user_values));
426
1
  ASSERT_EQ(min_string, test::GetBoundaryString(level1_files[0]->smallest.user_values));
427
1
  ASSERT_EQ(max_string, test::GetBoundaryString(level1_files[0]->largest.user_values));
428
1
  ASSERT_NO_FATAL_FAILURE(VerifyFrontier(*level1_files[0], 1, 4));
429
1
}
430
431
432
1
TEST_F(CompactionJobTest, SeqNoTrackingWithDeleteAll) {
433
1
  NewDB();
434
435
1
  auto file1 = mock::MakeMockFile({{KeyStr("b", 4U, kTypeDeletion), ""},
436
1
                                      {KeyStr("c", 3U, kTypeDeletion), "val"}});
437
1
  AddMockFile(file1);
438
439
1
  auto file2 = mock::MakeMockFile({{KeyStr("b", 2U, kTypeValue), "val"},
440
1
                                      {KeyStr("c", 1U, kTypeValue), "val"}});
441
1
  AddMockFile(file2);
442
443
1
  stl_wrappers::KVMap empty_map;
444
1
  auto expected_results = empty_map;
445
446
1
  SetLastSequence(4U);
447
1
  auto level0_files = cfd_->current()->storage_info()->LevelFiles(0);
448
1
  auto level1_files = cfd_->current()->storage_info()->LevelFiles(1);
449
1
  ASSERT_EQ(2, level0_files.size());
450
1
  ASSERT_EQ(0, level1_files.size());
451
1
  ASSERT_EQ(4, level0_files[0]->largest.seqno);
452
1
  ASSERT_EQ(3, level0_files[0]->smallest.seqno);
453
1
  ASSERT_EQ(2, level0_files[1]->largest.seqno);
454
1
  ASSERT_EQ(1, level0_files[1]->smallest.seqno);
455
1
  RunCompaction({level0_files}, expected_results);
456
457
1
  level0_files = cfd_->current()->storage_info()->LevelFiles(0);
458
1
  level1_files = cfd_->current()->storage_info()->LevelFiles(1);
459
1
  ASSERT_EQ(0, level0_files.size());
460
1
  ASSERT_EQ(0, level1_files.size());
461
1
}
462
463
1
TEST_F(CompactionJobTest, SimpleOverwrite) {
464
1
  NewDB();
465
466
1
  auto file1 = mock::MakeMockFile({
467
1
      {KeyStr("a", 3U, kTypeValue), "val2"},
468
1
      {KeyStr("b", 4U, kTypeValue), "val3"},
469
1
  });
470
1
  AddMockFile(file1);
471
472
1
  auto file2 = mock::MakeMockFile({{KeyStr("a", 1U, kTypeValue), "val"},
473
1
                                   {KeyStr("b", 2U, kTypeValue), "val"}});
474
1
  AddMockFile(file2);
475
476
1
  auto expected_results =
477
1
      mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "val2"},
478
1
                          {KeyStr("b", 4U, kTypeValue), "val3"}});
479
480
1
  SetLastSequence(4U);
481
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
482
1
  RunCompaction({files}, expected_results);
483
1
}
484
485
1
TEST_F(CompactionJobTest, SimpleNonLastLevel) {
486
1
  NewDB();
487
488
1
  auto file1 = mock::MakeMockFile({
489
1
      {KeyStr("a", 5U, kTypeValue), "val2"},
490
1
      {KeyStr("b", 6U, kTypeValue), "val3"},
491
1
  });
492
1
  AddMockFile(file1);
493
494
1
  auto file2 = mock::MakeMockFile({{KeyStr("a", 3U, kTypeValue), "val"},
495
1
                                   {KeyStr("b", 4U, kTypeValue), "val"}});
496
1
  AddMockFile(file2, 1);
497
498
1
  auto file3 = mock::MakeMockFile({{KeyStr("a", 1U, kTypeValue), "val"},
499
1
                                   {KeyStr("b", 2U, kTypeValue), "val"}});
500
1
  AddMockFile(file3, 2);
501
502
  // Because level 1 is not the last level, the sequence numbers of a and b
503
  // cannot be set to 0
504
1
  auto expected_results =
505
1
      mock::MakeMockFile({{KeyStr("a", 5U, kTypeValue), "val2"},
506
1
                          {KeyStr("b", 6U, kTypeValue), "val3"}});
507
508
1
  SetLastSequence(6U);
509
1
  auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
510
1
  auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
511
1
  RunCompaction({lvl0_files, lvl1_files}, expected_results);
512
1
}
513
514
1
TEST_F(CompactionJobTest, SimpleMerge) {
515
1
  merge_op_ = MergeOperators::CreateStringAppendOperator();
516
1
  NewDB();
517
518
1
  auto file1 = mock::MakeMockFile({
519
1
      {KeyStr("a", 5U, kTypeMerge), "5"},
520
1
      {KeyStr("a", 4U, kTypeMerge), "4"},
521
1
      {KeyStr("a", 3U, kTypeValue), "3"},
522
1
  });
523
1
  AddMockFile(file1);
524
525
1
  auto file2 = mock::MakeMockFile(
526
1
      {{KeyStr("b", 2U, kTypeMerge), "2"}, {KeyStr("b", 1U, kTypeValue), "1"}});
527
1
  AddMockFile(file2);
528
529
1
  auto expected_results =
530
1
      mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "3,4,5"},
531
1
                          {KeyStr("b", 2U, kTypeValue), "1,2"}});
532
533
1
  SetLastSequence(5U);
534
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
535
1
  RunCompaction({files}, expected_results);
536
1
}
537
538
1
TEST_F(CompactionJobTest, NonAssocMerge) {
539
1
  merge_op_ = MergeOperators::CreateStringAppendTESTOperator();
540
1
  NewDB();
541
542
1
  auto file1 = mock::MakeMockFile({
543
1
      {KeyStr("a", 5U, kTypeMerge), "5"},
544
1
      {KeyStr("a", 4U, kTypeMerge), "4"},
545
1
      {KeyStr("a", 3U, kTypeMerge), "3"},
546
1
  });
547
1
  AddMockFile(file1);
548
549
1
  auto file2 = mock::MakeMockFile(
550
1
      {{KeyStr("b", 2U, kTypeMerge), "2"}, {KeyStr("b", 1U, kTypeMerge), "1"}});
551
1
  AddMockFile(file2);
552
553
1
  auto expected_results =
554
1
      mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "3,4,5"},
555
1
                          {KeyStr("b", 2U, kTypeMerge), "2"},
556
1
                          {KeyStr("b", 1U, kTypeMerge), "1"}});
557
558
1
  SetLastSequence(5U);
559
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
560
1
  RunCompaction({files}, expected_results);
561
1
}
562
563
// Filters merge operands with value 10.
564
1
TEST_F(CompactionJobTest, MergeOperandFilter) {
565
1
  merge_op_ = MergeOperators::CreateUInt64AddOperator();
566
1
  compaction_filter_.reset(new test::FilterNumber(10U));
567
1
  NewDB();
568
569
1
  auto file1 = mock::MakeMockFile(
570
1
      {{KeyStr("a", 5U, kTypeMerge), test::EncodeInt(5U)},
571
1
       {KeyStr("a", 4U, kTypeMerge), test::EncodeInt(10U)},  // Filtered
572
1
       {KeyStr("a", 3U, kTypeMerge), test::EncodeInt(3U)}});
573
1
  AddMockFile(file1);
574
575
1
  auto file2 = mock::MakeMockFile({
576
1
      {KeyStr("b", 2U, kTypeMerge), test::EncodeInt(2U)},
577
1
      {KeyStr("b", 1U, kTypeMerge), test::EncodeInt(10U)}  // Filtered
578
1
  });
579
1
  AddMockFile(file2);
580
581
1
  auto expected_results =
582
1
      mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), test::EncodeInt(8U)},
583
1
                          {KeyStr("b", 2U, kTypeMerge), test::EncodeInt(2U)}});
584
585
1
  SetLastSequence(5U);
586
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
587
1
  RunCompaction({files}, expected_results);
588
1
}
589
590
1
TEST_F(CompactionJobTest, FilterSomeMergeOperands) {
591
1
  merge_op_ = MergeOperators::CreateUInt64AddOperator();
592
1
  compaction_filter_.reset(new test::FilterNumber(10U));
593
1
  NewDB();
594
595
1
  auto file1 = mock::MakeMockFile(
596
1
      {{KeyStr("a", 5U, kTypeMerge), test::EncodeInt(5U)},
597
1
       {KeyStr("a", 4U, kTypeMerge), test::EncodeInt(10U)},  // Filtered
598
1
       {KeyStr("a", 3U, kTypeValue), test::EncodeInt(5U)},
599
1
       {KeyStr("d", 8U, kTypeMerge), test::EncodeInt(10U)}});
600
1
  AddMockFile(file1);
601
602
1
  auto file2 =
603
1
      mock::MakeMockFile({{KeyStr("b", 2U, kTypeMerge), test::EncodeInt(10U)},
604
1
                          {KeyStr("b", 1U, kTypeMerge), test::EncodeInt(10U)},
605
1
                          {KeyStr("c", 2U, kTypeMerge), test::EncodeInt(3U)},
606
1
                          {KeyStr("c", 1U, kTypeValue), test::EncodeInt(7U)},
607
1
                          {KeyStr("d", 1U, kTypeValue), test::EncodeInt(6U)}});
608
1
  AddMockFile(file2);
609
610
1
  auto file3 =
611
1
      mock::MakeMockFile({{KeyStr("a", 1U, kTypeMerge), test::EncodeInt(3U)}});
612
1
  AddMockFile(file3, 2);
613
614
1
  auto expected_results = mock::MakeMockFile({
615
1
      {KeyStr("a", 5U, kTypeValue), test::EncodeInt(10U)},
616
1
      {KeyStr("c", 2U, kTypeValue), test::EncodeInt(10U)},
617
1
      {KeyStr("d", 1U, kTypeValue), test::EncodeInt(6U)}
618
      // b does not appear because the operands are filtered
619
1
  });
620
621
1
  SetLastSequence(5U);
622
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
623
1
  RunCompaction({files}, expected_results);
624
1
}
625
626
// Test where all operands/merge results are filtered out.
627
1
TEST_F(CompactionJobTest, FilterAllMergeOperands) {
628
1
  merge_op_ = MergeOperators::CreateUInt64AddOperator();
629
1
  compaction_filter_.reset(new test::FilterNumber(10U));
630
1
  NewDB();
631
632
1
  auto file1 =
633
1
      mock::MakeMockFile({{KeyStr("a", 11U, kTypeMerge), test::EncodeInt(10U)},
634
1
                          {KeyStr("a", 10U, kTypeMerge), test::EncodeInt(10U)},
635
1
                          {KeyStr("a", 9U, kTypeMerge), test::EncodeInt(10U)}});
636
1
  AddMockFile(file1);
637
638
1
  auto file2 =
639
1
      mock::MakeMockFile({{KeyStr("b", 8U, kTypeMerge), test::EncodeInt(10U)},
640
1
                          {KeyStr("b", 7U, kTypeMerge), test::EncodeInt(10U)},
641
1
                          {KeyStr("b", 6U, kTypeMerge), test::EncodeInt(10U)},
642
1
                          {KeyStr("b", 5U, kTypeMerge), test::EncodeInt(10U)},
643
1
                          {KeyStr("b", 4U, kTypeMerge), test::EncodeInt(10U)},
644
1
                          {KeyStr("b", 3U, kTypeMerge), test::EncodeInt(10U)},
645
1
                          {KeyStr("b", 2U, kTypeMerge), test::EncodeInt(10U)},
646
1
                          {KeyStr("c", 2U, kTypeMerge), test::EncodeInt(10U)},
647
1
                          {KeyStr("c", 1U, kTypeMerge), test::EncodeInt(10U)}});
648
1
  AddMockFile(file2);
649
650
1
  auto file3 =
651
1
      mock::MakeMockFile({{KeyStr("a", 2U, kTypeMerge), test::EncodeInt(10U)},
652
1
                          {KeyStr("b", 1U, kTypeMerge), test::EncodeInt(10U)}});
653
1
  AddMockFile(file3, 2);
654
655
1
  SetLastSequence(11U);
656
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
657
658
1
  stl_wrappers::KVMap empty_map;
659
1
  RunCompaction({files}, empty_map);
660
1
}
661
662
1
TEST_F(CompactionJobTest, SimpleSingleDelete) {
663
1
  NewDB();
664
665
1
  auto file1 = mock::MakeMockFile({
666
1
      {KeyStr("a", 5U, kTypeDeletion), ""},
667
1
      {KeyStr("b", 6U, kTypeSingleDeletion), ""},
668
1
  });
669
1
  AddMockFile(file1);
670
671
1
  auto file2 = mock::MakeMockFile({{KeyStr("a", 3U, kTypeValue), "val"},
672
1
                                   {KeyStr("b", 4U, kTypeValue), "val"}});
673
1
  AddMockFile(file2);
674
675
1
  auto file3 = mock::MakeMockFile({
676
1
      {KeyStr("a", 1U, kTypeValue), "val"},
677
1
  });
678
1
  AddMockFile(file3, 2);
679
680
1
  auto expected_results =
681
1
      mock::MakeMockFile({{KeyStr("a", 5U, kTypeDeletion), ""}});
682
683
1
  SetLastSequence(6U);
684
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
685
1
  RunCompaction({files}, expected_results);
686
1
}
687
688
1
TEST_F(CompactionJobTest, SingleDeleteSnapshots) {
689
1
  NewDB();
690
691
1
  auto file1 = mock::MakeMockFile({
692
1
      {KeyStr("A", 12U, kTypeSingleDeletion), ""},
693
1
      {KeyStr("a", 12U, kTypeSingleDeletion), ""},
694
1
      {KeyStr("b", 21U, kTypeSingleDeletion), ""},
695
1
      {KeyStr("c", 22U, kTypeSingleDeletion), ""},
696
1
      {KeyStr("d", 9U, kTypeSingleDeletion), ""},
697
1
      {KeyStr("f", 21U, kTypeSingleDeletion), ""},
698
1
      {KeyStr("j", 11U, kTypeSingleDeletion), ""},
699
1
      {KeyStr("j", 9U, kTypeSingleDeletion), ""},
700
1
      {KeyStr("k", 12U, kTypeSingleDeletion), ""},
701
1
      {KeyStr("k", 11U, kTypeSingleDeletion), ""},
702
1
      {KeyStr("l", 3U, kTypeSingleDeletion), ""},
703
1
      {KeyStr("l", 2U, kTypeSingleDeletion), ""},
704
1
  });
705
1
  AddMockFile(file1);
706
707
1
  auto file2 = mock::MakeMockFile({
708
1
      {KeyStr("0", 2U, kTypeSingleDeletion), ""},
709
1
      {KeyStr("a", 11U, kTypeValue), "val1"},
710
1
      {KeyStr("b", 11U, kTypeValue), "val2"},
711
1
      {KeyStr("c", 21U, kTypeValue), "val3"},
712
1
      {KeyStr("d", 8U, kTypeValue), "val4"},
713
1
      {KeyStr("e", 2U, kTypeSingleDeletion), ""},
714
1
      {KeyStr("f", 1U, kTypeValue), "val1"},
715
1
      {KeyStr("g", 11U, kTypeSingleDeletion), ""},
716
1
      {KeyStr("h", 2U, kTypeSingleDeletion), ""},
717
1
      {KeyStr("m", 12U, kTypeValue), "val1"},
718
1
      {KeyStr("m", 11U, kTypeSingleDeletion), ""},
719
1
      {KeyStr("m", 8U, kTypeValue), "val2"},
720
1
  });
721
1
  AddMockFile(file2);
722
723
1
  auto file3 = mock::MakeMockFile({
724
1
      {KeyStr("A", 1U, kTypeValue), "val"},
725
1
      {KeyStr("e", 1U, kTypeValue), "val"},
726
1
  });
727
1
  AddMockFile(file3, 2);
728
729
1
  auto expected_results = mock::MakeMockFile({
730
1
      {KeyStr("A", 12U, kTypeSingleDeletion), ""},
731
1
      {KeyStr("a", 12U, kTypeSingleDeletion), ""},
732
1
      {KeyStr("a", 11U, kTypeValue), ""},
733
1
      {KeyStr("b", 21U, kTypeSingleDeletion), ""},
734
1
      {KeyStr("b", 11U, kTypeValue), "val2"},
735
1
      {KeyStr("c", 22U, kTypeSingleDeletion), ""},
736
1
      {KeyStr("c", 21U, kTypeValue), ""},
737
1
      {KeyStr("e", 2U, kTypeSingleDeletion), ""},
738
1
      {KeyStr("f", 21U, kTypeSingleDeletion), ""},
739
1
      {KeyStr("f", 1U, kTypeValue), "val1"},
740
1
      {KeyStr("g", 11U, kTypeSingleDeletion), ""},
741
1
      {KeyStr("j", 11U, kTypeSingleDeletion), ""},
742
1
      {KeyStr("k", 11U, kTypeSingleDeletion), ""},
743
1
      {KeyStr("m", 12U, kTypeValue), "val1"},
744
1
      {KeyStr("m", 11U, kTypeSingleDeletion), ""},
745
1
      {KeyStr("m", 8U, kTypeValue), "val2"},
746
1
  });
747
748
1
  SetLastSequence(22U);
749
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
750
1
  RunCompaction({files}, expected_results, {10U, 20U}, 10U);
751
1
}
752
753
1
TEST_F(CompactionJobTest, EarliestWriteConflictSnapshot) {
754
1
  NewDB();
755
756
  // Test multiple snapshots where the earliest snapshot is not a
757
  // write-conflic-snapshot.
758
759
1
  auto file1 = mock::MakeMockFile({
760
1
      {KeyStr("A", 24U, kTypeSingleDeletion), ""},
761
1
      {KeyStr("A", 23U, kTypeValue), "val"},
762
1
      {KeyStr("B", 24U, kTypeSingleDeletion), ""},
763
1
      {KeyStr("B", 23U, kTypeValue), "val"},
764
1
      {KeyStr("D", 24U, kTypeSingleDeletion), ""},
765
1
      {KeyStr("G", 32U, kTypeSingleDeletion), ""},
766
1
      {KeyStr("G", 31U, kTypeValue), "val"},
767
1
      {KeyStr("G", 24U, kTypeSingleDeletion), ""},
768
1
      {KeyStr("G", 23U, kTypeValue), "val2"},
769
1
      {KeyStr("H", 31U, kTypeValue), "val"},
770
1
      {KeyStr("H", 24U, kTypeSingleDeletion), ""},
771
1
      {KeyStr("H", 23U, kTypeValue), "val"},
772
1
      {KeyStr("I", 35U, kTypeSingleDeletion), ""},
773
1
      {KeyStr("I", 34U, kTypeValue), "val2"},
774
1
      {KeyStr("I", 33U, kTypeSingleDeletion), ""},
775
1
      {KeyStr("I", 32U, kTypeValue), "val3"},
776
1
      {KeyStr("I", 31U, kTypeSingleDeletion), ""},
777
1
      {KeyStr("J", 34U, kTypeValue), "val"},
778
1
      {KeyStr("J", 33U, kTypeSingleDeletion), ""},
779
1
      {KeyStr("J", 25U, kTypeValue), "val2"},
780
1
      {KeyStr("J", 24U, kTypeSingleDeletion), ""},
781
1
  });
782
1
  AddMockFile(file1);
783
784
1
  auto file2 = mock::MakeMockFile({
785
1
      {KeyStr("A", 14U, kTypeSingleDeletion), ""},
786
1
      {KeyStr("A", 13U, kTypeValue), "val2"},
787
1
      {KeyStr("C", 14U, kTypeSingleDeletion), ""},
788
1
      {KeyStr("C", 13U, kTypeValue), "val"},
789
1
      {KeyStr("E", 12U, kTypeSingleDeletion), ""},
790
1
      {KeyStr("F", 4U, kTypeSingleDeletion), ""},
791
1
      {KeyStr("F", 3U, kTypeValue), "val"},
792
1
      {KeyStr("G", 14U, kTypeSingleDeletion), ""},
793
1
      {KeyStr("G", 13U, kTypeValue), "val3"},
794
1
      {KeyStr("H", 14U, kTypeSingleDeletion), ""},
795
1
      {KeyStr("H", 13U, kTypeValue), "val2"},
796
1
      {KeyStr("I", 13U, kTypeValue), "val4"},
797
1
      {KeyStr("I", 12U, kTypeSingleDeletion), ""},
798
1
      {KeyStr("I", 11U, kTypeValue), "val5"},
799
1
      {KeyStr("J", 15U, kTypeValue), "val3"},
800
1
      {KeyStr("J", 14U, kTypeSingleDeletion), ""},
801
1
  });
802
1
  AddMockFile(file2);
803
804
1
  auto expected_results = mock::MakeMockFile({
805
1
      {KeyStr("A", 24U, kTypeSingleDeletion), ""},
806
1
      {KeyStr("A", 23U, kTypeValue), ""},
807
1
      {KeyStr("B", 24U, kTypeSingleDeletion), ""},
808
1
      {KeyStr("B", 23U, kTypeValue), ""},
809
1
      {KeyStr("D", 24U, kTypeSingleDeletion), ""},
810
1
      {KeyStr("E", 12U, kTypeSingleDeletion), ""},
811
1
      {KeyStr("G", 32U, kTypeSingleDeletion), ""},
812
1
      {KeyStr("G", 31U, kTypeValue), ""},
813
1
      {KeyStr("H", 31U, kTypeValue), "val"},
814
1
      {KeyStr("I", 35U, kTypeSingleDeletion), ""},
815
1
      {KeyStr("I", 34U, kTypeValue), ""},
816
1
      {KeyStr("I", 31U, kTypeSingleDeletion), ""},
817
1
      {KeyStr("I", 13U, kTypeValue), "val4"},
818
1
      {KeyStr("J", 34U, kTypeValue), "val"},
819
1
      {KeyStr("J", 33U, kTypeSingleDeletion), ""},
820
1
      {KeyStr("J", 25U, kTypeValue), "val2"},
821
1
      {KeyStr("J", 24U, kTypeSingleDeletion), ""},
822
1
      {KeyStr("J", 15U, kTypeValue), "val3"},
823
1
      {KeyStr("J", 14U, kTypeSingleDeletion), ""},
824
1
  });
825
826
1
  SetLastSequence(24U);
827
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
828
1
  RunCompaction({files}, expected_results, {10U, 20U, 30U}, 20U);
829
1
}
830
831
1
TEST_F(CompactionJobTest, SingleDeleteZeroSeq) {
832
1
  NewDB();
833
834
1
  auto file1 = mock::MakeMockFile({
835
1
      {KeyStr("A", 10U, kTypeSingleDeletion), ""},
836
1
      {KeyStr("dummy", 5U, kTypeValue), "val2"},
837
1
  });
838
1
  AddMockFile(file1);
839
840
1
  auto file2 = mock::MakeMockFile({
841
1
      {KeyStr("A", 0U, kTypeValue), "val"},
842
1
  });
843
1
  AddMockFile(file2);
844
845
1
  auto expected_results = mock::MakeMockFile({
846
1
      {KeyStr("dummy", 5U, kTypeValue), "val2"},
847
1
  });
848
849
1
  SetLastSequence(22U);
850
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
851
1
  RunCompaction({files}, expected_results, {});
852
1
}
853
854
1
TEST_F(CompactionJobTest, MultiSingleDelete) {
855
  // Tests three scenarios involving multiple single delete/put pairs:
856
  //
857
  // A: Put Snapshot SDel Put SDel -> Put Snapshot SDel
858
  // B: Snapshot Put SDel Put SDel Snapshot -> Snapshot SDel Snapshot
859
  // C: SDel Put SDel Snapshot Put -> Snapshot Put
860
  // D: (Put) SDel Snapshot Put SDel -> (Put) SDel Snapshot SDel
861
  // E: Put SDel Snapshot Put SDel -> Snapshot SDel
862
  // F: Put SDel Put Sdel Snapshot -> removed
863
  // G: Snapshot SDel Put SDel Put -> Snapshot Put SDel
864
  // H: (Put) Put SDel Put Sdel Snapshot -> Removed
865
  // I: (Put) Snapshot Put SDel Put SDel -> SDel
866
  // J: Put Put SDel Put SDel SDel Snapshot Put Put SDel SDel Put
867
  //      -> Snapshot Put
868
  // K: SDel SDel Put SDel Put Put Snapshot SDel Put SDel SDel Put SDel
869
  //      -> Snapshot Put Snapshot SDel
870
  // L: SDel Put Del Put SDel Snapshot Del Put Del SDel Put SDel
871
  //      -> Snapshot SDel
872
  // M: (Put) SDel Put Del Put SDel Snapshot Put Del SDel Put SDel Del
873
  //      -> SDel Snapshot Del
874
1
  NewDB();
875
876
1
  auto file1 = mock::MakeMockFile({
877
1
      {KeyStr("A", 14U, kTypeSingleDeletion), ""},
878
1
      {KeyStr("A", 13U, kTypeValue), "val5"},
879
1
      {KeyStr("A", 12U, kTypeSingleDeletion), ""},
880
1
      {KeyStr("B", 14U, kTypeSingleDeletion), ""},
881
1
      {KeyStr("B", 13U, kTypeValue), "val2"},
882
1
      {KeyStr("C", 14U, kTypeValue), "val3"},
883
1
      {KeyStr("D", 12U, kTypeSingleDeletion), ""},
884
1
      {KeyStr("D", 11U, kTypeValue), "val4"},
885
1
      {KeyStr("G", 15U, kTypeValue), "val"},
886
1
      {KeyStr("G", 14U, kTypeSingleDeletion), ""},
887
1
      {KeyStr("G", 13U, kTypeValue), "val"},
888
1
      {KeyStr("I", 14U, kTypeSingleDeletion), ""},
889
1
      {KeyStr("I", 13U, kTypeValue), "val"},
890
1
      {KeyStr("J", 15U, kTypeValue), "val"},
891
1
      {KeyStr("J", 14U, kTypeSingleDeletion), ""},
892
1
      {KeyStr("J", 13U, kTypeSingleDeletion), ""},
893
1
      {KeyStr("J", 12U, kTypeValue), "val"},
894
1
      {KeyStr("J", 11U, kTypeValue), "val"},
895
1
      {KeyStr("K", 16U, kTypeSingleDeletion), ""},
896
1
      {KeyStr("K", 15U, kTypeValue), "val1"},
897
1
      {KeyStr("K", 14U, kTypeSingleDeletion), ""},
898
1
      {KeyStr("K", 13U, kTypeSingleDeletion), ""},
899
1
      {KeyStr("K", 12U, kTypeValue), "val2"},
900
1
      {KeyStr("K", 11U, kTypeSingleDeletion), ""},
901
1
      {KeyStr("L", 16U, kTypeSingleDeletion), ""},
902
1
      {KeyStr("L", 15U, kTypeValue), "val"},
903
1
      {KeyStr("L", 14U, kTypeSingleDeletion), ""},
904
1
      {KeyStr("L", 13U, kTypeDeletion), ""},
905
1
      {KeyStr("L", 12U, kTypeValue), "val"},
906
1
      {KeyStr("L", 11U, kTypeDeletion), ""},
907
1
      {KeyStr("M", 16U, kTypeDeletion), ""},
908
1
      {KeyStr("M", 15U, kTypeSingleDeletion), ""},
909
1
      {KeyStr("M", 14U, kTypeValue), "val"},
910
1
      {KeyStr("M", 13U, kTypeSingleDeletion), ""},
911
1
      {KeyStr("M", 12U, kTypeDeletion), ""},
912
1
      {KeyStr("M", 11U, kTypeValue), "val"},
913
1
  });
914
1
  AddMockFile(file1);
915
916
1
  auto file2 = mock::MakeMockFile({
917
1
      {KeyStr("A", 10U, kTypeValue), "val"},
918
1
      {KeyStr("B", 12U, kTypeSingleDeletion), ""},
919
1
      {KeyStr("B", 11U, kTypeValue), "val2"},
920
1
      {KeyStr("C", 10U, kTypeSingleDeletion), ""},
921
1
      {KeyStr("C", 9U, kTypeValue), "val6"},
922
1
      {KeyStr("C", 8U, kTypeSingleDeletion), ""},
923
1
      {KeyStr("D", 10U, kTypeSingleDeletion), ""},
924
1
      {KeyStr("E", 12U, kTypeSingleDeletion), ""},
925
1
      {KeyStr("E", 11U, kTypeValue), "val"},
926
1
      {KeyStr("E", 5U, kTypeSingleDeletion), ""},
927
1
      {KeyStr("E", 4U, kTypeValue), "val"},
928
1
      {KeyStr("F", 6U, kTypeSingleDeletion), ""},
929
1
      {KeyStr("F", 5U, kTypeValue), "val"},
930
1
      {KeyStr("F", 4U, kTypeSingleDeletion), ""},
931
1
      {KeyStr("F", 3U, kTypeValue), "val"},
932
1
      {KeyStr("G", 12U, kTypeSingleDeletion), ""},
933
1
      {KeyStr("H", 6U, kTypeSingleDeletion), ""},
934
1
      {KeyStr("H", 5U, kTypeValue), "val"},
935
1
      {KeyStr("H", 4U, kTypeSingleDeletion), ""},
936
1
      {KeyStr("H", 3U, kTypeValue), "val"},
937
1
      {KeyStr("I", 12U, kTypeSingleDeletion), ""},
938
1
      {KeyStr("I", 11U, kTypeValue), "val"},
939
1
      {KeyStr("J", 6U, kTypeSingleDeletion), ""},
940
1
      {KeyStr("J", 5U, kTypeSingleDeletion), ""},
941
1
      {KeyStr("J", 4U, kTypeValue), "val"},
942
1
      {KeyStr("J", 3U, kTypeSingleDeletion), ""},
943
1
      {KeyStr("J", 2U, kTypeValue), "val"},
944
1
      {KeyStr("K", 8U, kTypeValue), "val3"},
945
1
      {KeyStr("K", 7U, kTypeValue), "val4"},
946
1
      {KeyStr("K", 6U, kTypeSingleDeletion), ""},
947
1
      {KeyStr("K", 5U, kTypeValue), "val5"},
948
1
      {KeyStr("K", 2U, kTypeSingleDeletion), ""},
949
1
      {KeyStr("K", 1U, kTypeSingleDeletion), ""},
950
1
      {KeyStr("L", 5U, kTypeSingleDeletion), ""},
951
1
      {KeyStr("L", 4U, kTypeValue), "val"},
952
1
      {KeyStr("L", 3U, kTypeDeletion), ""},
953
1
      {KeyStr("L", 2U, kTypeValue), "val"},
954
1
      {KeyStr("L", 1U, kTypeSingleDeletion), ""},
955
1
      {KeyStr("M", 10U, kTypeSingleDeletion), ""},
956
1
      {KeyStr("M", 7U, kTypeValue), "val"},
957
1
      {KeyStr("M", 5U, kTypeDeletion), ""},
958
1
      {KeyStr("M", 4U, kTypeValue), "val"},
959
1
      {KeyStr("M", 3U, kTypeSingleDeletion), ""},
960
1
  });
961
1
  AddMockFile(file2);
962
963
1
  auto file3 = mock::MakeMockFile({
964
1
      {KeyStr("D", 1U, kTypeValue), "val"},
965
1
      {KeyStr("H", 1U, kTypeValue), "val"},
966
1
      {KeyStr("I", 2U, kTypeValue), "val"},
967
1
  });
968
1
  AddMockFile(file3, 2);
969
970
1
  auto file4 = mock::MakeMockFile({
971
1
      {KeyStr("M", 1U, kTypeValue), "val"},
972
1
  });
973
1
  AddMockFile(file4, 2);
974
975
1
  auto expected_results =
976
1
      mock::MakeMockFile({{KeyStr("A", 14U, kTypeSingleDeletion), ""},
977
1
                          {KeyStr("A", 13U, kTypeValue), ""},
978
1
                          {KeyStr("A", 12U, kTypeSingleDeletion), ""},
979
1
                          {KeyStr("A", 10U, kTypeValue), "val"},
980
1
                          {KeyStr("B", 14U, kTypeSingleDeletion), ""},
981
1
                          {KeyStr("B", 13U, kTypeValue), ""},
982
1
                          {KeyStr("C", 14U, kTypeValue), "val3"},
983
1
                          {KeyStr("D", 12U, kTypeSingleDeletion), ""},
984
1
                          {KeyStr("D", 11U, kTypeValue), ""},
985
1
                          {KeyStr("D", 10U, kTypeSingleDeletion), ""},
986
1
                          {KeyStr("E", 12U, kTypeSingleDeletion), ""},
987
1
                          {KeyStr("E", 11U, kTypeValue), ""},
988
1
                          {KeyStr("G", 15U, kTypeValue), "val"},
989
1
                          {KeyStr("G", 12U, kTypeSingleDeletion), ""},
990
1
                          {KeyStr("I", 14U, kTypeSingleDeletion), ""},
991
1
                          {KeyStr("I", 13U, kTypeValue), ""},
992
1
                          {KeyStr("J", 15U, kTypeValue), "val"},
993
1
                          {KeyStr("K", 16U, kTypeSingleDeletion), ""},
994
1
                          {KeyStr("K", 15U, kTypeValue), ""},
995
1
                          {KeyStr("K", 11U, kTypeSingleDeletion), ""},
996
1
                          {KeyStr("K", 8U, kTypeValue), "val3"},
997
1
                          {KeyStr("L", 16U, kTypeSingleDeletion), ""},
998
1
                          {KeyStr("L", 15U, kTypeValue), ""},
999
1
                          {KeyStr("M", 16U, kTypeDeletion), ""},
1000
1
                          {KeyStr("M", 3U, kTypeSingleDeletion), ""}});
1001
1002
1
  SetLastSequence(22U);
1003
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
1004
1
  RunCompaction({files}, expected_results, {10U}, 10U);
1005
1
}
1006
1007
// This test documents the behavior where a corrupt key follows a deletion or a
1008
// single deletion and the (single) deletion gets removed while the corrupt key
1009
// gets written out. TODO(noetzli): We probably want a better way to treat
1010
// corrupt keys.
1011
1
TEST_F(CompactionJobTest, CorruptionAfterDeletion) {
1012
1
  NewDB();
1013
1014
1
  auto file1 =
1015
1
      mock::MakeMockFile({{test::KeyStr("A", 6U, kTypeValue), "val3"},
1016
1
                          {test::KeyStr("a", 5U, kTypeDeletion), ""},
1017
1
                          {test::KeyStr("a", 4U, kTypeValue, true), "val"}});
1018
1
  AddMockFile(file1);
1019
1020
1
  auto file2 =
1021
1
      mock::MakeMockFile({{test::KeyStr("b", 3U, kTypeSingleDeletion), ""},
1022
1
                          {test::KeyStr("b", 2U, kTypeValue, true), "val"},
1023
1
                          {test::KeyStr("c", 1U, kTypeValue), "val2"}});
1024
1
  AddMockFile(file2);
1025
1026
1
  auto expected_results =
1027
1
      mock::MakeMockFile({{test::KeyStr("A", 0U, kTypeValue), "val3"},
1028
1
                          {test::KeyStr("a", 0U, kTypeValue, true), "val"},
1029
1
                          {test::KeyStr("b", 0U, kTypeValue, true), "val"},
1030
1
                          {test::KeyStr("c", 1U, kTypeValue), "val2"}});
1031
1032
1
  SetLastSequence(6U);
1033
1
  auto files = cfd_->current()->storage_info()->LevelFiles(0);
1034
1
  RunCompaction({files}, expected_results);
1035
1
}
1036
1037
}  // namespace rocksdb
1038
1039
13.2k
int main(int argc, char** argv) {
1040
13.2k
  ::testing::InitGoogleTest(&argc, argv);
1041
13.2k
  return RUN_ALL_TESTS();
1042
13.2k
}
1043
1044
#else
1045
#include <stdio.h>
1046
1047
int main(int argc, char** argv) {
1048
  fprintf(stderr,
1049
          "SKIPPED as CompactionJobStats is not supported in ROCKSDB_LITE\n");
1050
  return 0;
1051
}
1052
1053
#endif  // ROCKSDB_LITE