/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 |