]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
2 | // This source code is licensed under both the GPLv2 (found in the |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | // |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | ||
10 | #include "db/version_set.h" | |
11fdf7f2 TL |
11 | #include "db/log_writer.h" |
12 | #include "table/mock_table.h" | |
7c673cae | 13 | #include "util/logging.h" |
11fdf7f2 | 14 | #include "util/string_util.h" |
7c673cae FG |
15 | #include "util/testharness.h" |
16 | #include "util/testutil.h" | |
17 | ||
18 | namespace rocksdb { | |
19 | ||
20 | class GenerateLevelFilesBriefTest : public testing::Test { | |
21 | public: | |
22 | std::vector<FileMetaData*> files_; | |
23 | LevelFilesBrief file_level_; | |
24 | Arena arena_; | |
25 | ||
26 | GenerateLevelFilesBriefTest() { } | |
27 | ||
494da23a | 28 | ~GenerateLevelFilesBriefTest() override { |
7c673cae FG |
29 | for (size_t i = 0; i < files_.size(); i++) { |
30 | delete files_[i]; | |
31 | } | |
32 | } | |
33 | ||
34 | void Add(const char* smallest, const char* largest, | |
35 | SequenceNumber smallest_seq = 100, | |
36 | SequenceNumber largest_seq = 100) { | |
37 | FileMetaData* f = new FileMetaData; | |
38 | f->fd = FileDescriptor(files_.size() + 1, 0, 0); | |
39 | f->smallest = InternalKey(smallest, smallest_seq, kTypeValue); | |
40 | f->largest = InternalKey(largest, largest_seq, kTypeValue); | |
41 | files_.push_back(f); | |
42 | } | |
43 | ||
44 | int Compare() { | |
45 | int diff = 0; | |
46 | for (size_t i = 0; i < files_.size(); i++) { | |
47 | if (file_level_.files[i].fd.GetNumber() != files_[i]->fd.GetNumber()) { | |
48 | diff++; | |
49 | } | |
50 | } | |
51 | return diff; | |
52 | } | |
53 | }; | |
54 | ||
55 | TEST_F(GenerateLevelFilesBriefTest, Empty) { | |
56 | DoGenerateLevelFilesBrief(&file_level_, files_, &arena_); | |
57 | ASSERT_EQ(0u, file_level_.num_files); | |
58 | ASSERT_EQ(0, Compare()); | |
59 | } | |
60 | ||
61 | TEST_F(GenerateLevelFilesBriefTest, Single) { | |
62 | Add("p", "q"); | |
63 | DoGenerateLevelFilesBrief(&file_level_, files_, &arena_); | |
64 | ASSERT_EQ(1u, file_level_.num_files); | |
65 | ASSERT_EQ(0, Compare()); | |
66 | } | |
67 | ||
68 | TEST_F(GenerateLevelFilesBriefTest, Multiple) { | |
69 | Add("150", "200"); | |
70 | Add("200", "250"); | |
71 | Add("300", "350"); | |
72 | Add("400", "450"); | |
73 | DoGenerateLevelFilesBrief(&file_level_, files_, &arena_); | |
74 | ASSERT_EQ(4u, file_level_.num_files); | |
75 | ASSERT_EQ(0, Compare()); | |
76 | } | |
77 | ||
78 | class CountingLogger : public Logger { | |
79 | public: | |
80 | CountingLogger() : log_count(0) {} | |
81 | using Logger::Logv; | |
494da23a | 82 | void Logv(const char* /*format*/, va_list /*ap*/) override { log_count++; } |
7c673cae FG |
83 | int log_count; |
84 | }; | |
85 | ||
86 | Options GetOptionsWithNumLevels(int num_levels, | |
87 | std::shared_ptr<CountingLogger> logger) { | |
88 | Options opt; | |
89 | opt.num_levels = num_levels; | |
90 | opt.info_log = logger; | |
91 | return opt; | |
92 | } | |
93 | ||
94 | class VersionStorageInfoTest : public testing::Test { | |
95 | public: | |
96 | const Comparator* ucmp_; | |
97 | InternalKeyComparator icmp_; | |
98 | std::shared_ptr<CountingLogger> logger_; | |
99 | Options options_; | |
100 | ImmutableCFOptions ioptions_; | |
101 | MutableCFOptions mutable_cf_options_; | |
102 | VersionStorageInfo vstorage_; | |
103 | ||
104 | InternalKey GetInternalKey(const char* ukey, | |
105 | SequenceNumber smallest_seq = 100) { | |
106 | return InternalKey(ukey, smallest_seq, kTypeValue); | |
107 | } | |
108 | ||
109 | VersionStorageInfoTest() | |
110 | : ucmp_(BytewiseComparator()), | |
111 | icmp_(ucmp_), | |
112 | logger_(new CountingLogger()), | |
113 | options_(GetOptionsWithNumLevels(6, logger_)), | |
114 | ioptions_(options_), | |
115 | mutable_cf_options_(options_), | |
116 | vstorage_(&icmp_, ucmp_, 6, kCompactionStyleLevel, nullptr, false) {} | |
117 | ||
494da23a | 118 | ~VersionStorageInfoTest() override { |
7c673cae FG |
119 | for (int i = 0; i < vstorage_.num_levels(); i++) { |
120 | for (auto* f : vstorage_.LevelFiles(i)) { | |
121 | if (--f->refs == 0) { | |
122 | delete f; | |
123 | } | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | void Add(int level, uint32_t file_number, const char* smallest, | |
129 | const char* largest, uint64_t file_size = 0) { | |
130 | assert(level < vstorage_.num_levels()); | |
131 | FileMetaData* f = new FileMetaData; | |
132 | f->fd = FileDescriptor(file_number, 0, file_size); | |
133 | f->smallest = GetInternalKey(smallest, 0); | |
134 | f->largest = GetInternalKey(largest, 0); | |
135 | f->compensated_file_size = file_size; | |
136 | f->refs = 0; | |
137 | f->num_entries = 0; | |
138 | f->num_deletions = 0; | |
139 | vstorage_.AddFile(level, f); | |
140 | } | |
11fdf7f2 TL |
141 | |
142 | void Add(int level, uint32_t file_number, const InternalKey& smallest, | |
143 | const InternalKey& largest, uint64_t file_size = 0) { | |
144 | assert(level < vstorage_.num_levels()); | |
145 | FileMetaData* f = new FileMetaData; | |
146 | f->fd = FileDescriptor(file_number, 0, file_size); | |
147 | f->smallest = smallest; | |
148 | f->largest = largest; | |
149 | f->compensated_file_size = file_size; | |
150 | f->refs = 0; | |
151 | f->num_entries = 0; | |
152 | f->num_deletions = 0; | |
153 | vstorage_.AddFile(level, f); | |
154 | } | |
155 | ||
156 | std::string GetOverlappingFiles(int level, const InternalKey& begin, | |
157 | const InternalKey& end) { | |
158 | std::vector<FileMetaData*> inputs; | |
159 | vstorage_.GetOverlappingInputs(level, &begin, &end, &inputs); | |
160 | ||
161 | std::string result; | |
162 | for (size_t i = 0; i < inputs.size(); ++i) { | |
163 | if (i > 0) { | |
164 | result += ","; | |
165 | } | |
166 | AppendNumberTo(&result, inputs[i]->fd.GetNumber()); | |
167 | } | |
168 | return result; | |
169 | } | |
7c673cae FG |
170 | }; |
171 | ||
172 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelStatic) { | |
173 | ioptions_.level_compaction_dynamic_level_bytes = false; | |
174 | mutable_cf_options_.max_bytes_for_level_base = 10; | |
175 | mutable_cf_options_.max_bytes_for_level_multiplier = 5; | |
176 | Add(4, 100U, "1", "2"); | |
177 | Add(5, 101U, "1", "2"); | |
178 | ||
179 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
180 | ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 10U); | |
181 | ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 50U); | |
182 | ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 250U); | |
183 | ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1250U); | |
184 | ||
185 | ASSERT_EQ(0, logger_->log_count); | |
186 | } | |
187 | ||
188 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamic) { | |
189 | ioptions_.level_compaction_dynamic_level_bytes = true; | |
190 | mutable_cf_options_.max_bytes_for_level_base = 1000; | |
191 | mutable_cf_options_.max_bytes_for_level_multiplier = 5; | |
192 | Add(5, 1U, "1", "2", 500U); | |
193 | ||
194 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
195 | ASSERT_EQ(0, logger_->log_count); | |
196 | ASSERT_EQ(vstorage_.base_level(), 5); | |
197 | ||
198 | Add(5, 2U, "3", "4", 550U); | |
199 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
200 | ASSERT_EQ(0, logger_->log_count); | |
11fdf7f2 | 201 | ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1000U); |
7c673cae FG |
202 | ASSERT_EQ(vstorage_.base_level(), 4); |
203 | ||
204 | Add(4, 3U, "3", "4", 550U); | |
205 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
206 | ASSERT_EQ(0, logger_->log_count); | |
11fdf7f2 | 207 | ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1000U); |
7c673cae FG |
208 | ASSERT_EQ(vstorage_.base_level(), 4); |
209 | ||
210 | Add(3, 4U, "3", "4", 250U); | |
211 | Add(3, 5U, "5", "7", 300U); | |
212 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
213 | ASSERT_EQ(1, logger_->log_count); | |
214 | ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1005U); | |
11fdf7f2 | 215 | ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 1000U); |
7c673cae FG |
216 | ASSERT_EQ(vstorage_.base_level(), 3); |
217 | ||
218 | Add(1, 6U, "3", "4", 5U); | |
219 | Add(1, 7U, "8", "9", 5U); | |
220 | logger_->log_count = 0; | |
221 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
222 | ASSERT_EQ(1, logger_->log_count); | |
223 | ASSERT_GT(vstorage_.MaxBytesForLevel(4), 1005U); | |
224 | ASSERT_GT(vstorage_.MaxBytesForLevel(3), 1005U); | |
225 | ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 1005U); | |
11fdf7f2 | 226 | ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 1000U); |
7c673cae FG |
227 | ASSERT_EQ(vstorage_.base_level(), 1); |
228 | } | |
229 | ||
230 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLotsOfData) { | |
231 | ioptions_.level_compaction_dynamic_level_bytes = true; | |
232 | mutable_cf_options_.max_bytes_for_level_base = 100; | |
233 | mutable_cf_options_.max_bytes_for_level_multiplier = 2; | |
234 | Add(0, 1U, "1", "2", 50U); | |
235 | Add(1, 2U, "1", "2", 50U); | |
236 | Add(2, 3U, "1", "2", 500U); | |
237 | Add(3, 4U, "1", "2", 500U); | |
238 | Add(4, 5U, "1", "2", 1700U); | |
239 | Add(5, 6U, "1", "2", 500U); | |
240 | ||
241 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
242 | ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 800U); | |
243 | ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 400U); | |
244 | ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 200U); | |
245 | ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 100U); | |
246 | ASSERT_EQ(vstorage_.base_level(), 1); | |
247 | ASSERT_EQ(0, logger_->log_count); | |
248 | } | |
249 | ||
250 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLargeLevel) { | |
251 | uint64_t kOneGB = 1000U * 1000U * 1000U; | |
252 | ioptions_.level_compaction_dynamic_level_bytes = true; | |
253 | mutable_cf_options_.max_bytes_for_level_base = 10U * kOneGB; | |
254 | mutable_cf_options_.max_bytes_for_level_multiplier = 10; | |
255 | Add(0, 1U, "1", "2", 50U); | |
256 | Add(3, 4U, "1", "2", 32U * kOneGB); | |
257 | Add(4, 5U, "1", "2", 500U * kOneGB); | |
258 | Add(5, 6U, "1", "2", 3000U * kOneGB); | |
259 | ||
260 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
261 | ASSERT_EQ(vstorage_.MaxBytesForLevel(5), 3000U * kOneGB); | |
262 | ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 300U * kOneGB); | |
263 | ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 30U * kOneGB); | |
11fdf7f2 | 264 | ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 10U * kOneGB); |
7c673cae FG |
265 | ASSERT_EQ(vstorage_.base_level(), 2); |
266 | ASSERT_EQ(0, logger_->log_count); | |
267 | } | |
268 | ||
494da23a TL |
269 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicWithLargeL0_1) { |
270 | ioptions_.level_compaction_dynamic_level_bytes = true; | |
271 | mutable_cf_options_.max_bytes_for_level_base = 40000; | |
272 | mutable_cf_options_.max_bytes_for_level_multiplier = 5; | |
273 | mutable_cf_options_.level0_file_num_compaction_trigger = 2; | |
274 | ||
275 | Add(0, 1U, "1", "2", 10000U); | |
276 | Add(0, 2U, "1", "2", 10000U); | |
277 | Add(0, 3U, "1", "2", 10000U); | |
278 | ||
279 | Add(5, 4U, "1", "2", 1286250U); | |
280 | Add(4, 5U, "1", "2", 200000U); | |
281 | Add(3, 6U, "1", "2", 40000U); | |
282 | Add(2, 7U, "1", "2", 8000U); | |
283 | ||
284 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
285 | ASSERT_EQ(0, logger_->log_count); | |
286 | ASSERT_EQ(2, vstorage_.base_level()); | |
287 | // level multiplier should be 3.5 | |
288 | ASSERT_EQ(vstorage_.level_multiplier(), 5.0); | |
289 | // Level size should be around 30,000, 105,000, 367,500 | |
290 | ASSERT_EQ(40000U, vstorage_.MaxBytesForLevel(2)); | |
291 | ASSERT_EQ(51450U, vstorage_.MaxBytesForLevel(3)); | |
292 | ASSERT_EQ(257250U, vstorage_.MaxBytesForLevel(4)); | |
293 | } | |
294 | ||
295 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicWithLargeL0_2) { | |
296 | ioptions_.level_compaction_dynamic_level_bytes = true; | |
297 | mutable_cf_options_.max_bytes_for_level_base = 10000; | |
298 | mutable_cf_options_.max_bytes_for_level_multiplier = 5; | |
299 | mutable_cf_options_.level0_file_num_compaction_trigger = 2; | |
300 | ||
301 | Add(0, 11U, "1", "2", 10000U); | |
302 | Add(0, 12U, "1", "2", 10000U); | |
303 | Add(0, 13U, "1", "2", 10000U); | |
304 | ||
305 | Add(5, 4U, "1", "2", 1286250U); | |
306 | Add(4, 5U, "1", "2", 200000U); | |
307 | Add(3, 6U, "1", "2", 40000U); | |
308 | Add(2, 7U, "1", "2", 8000U); | |
309 | ||
310 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
311 | ASSERT_EQ(0, logger_->log_count); | |
312 | ASSERT_EQ(2, vstorage_.base_level()); | |
313 | // level multiplier should be 3.5 | |
314 | ASSERT_LT(vstorage_.level_multiplier(), 3.6); | |
315 | ASSERT_GT(vstorage_.level_multiplier(), 3.4); | |
316 | // Level size should be around 30,000, 105,000, 367,500 | |
317 | ASSERT_EQ(30000U, vstorage_.MaxBytesForLevel(2)); | |
318 | ASSERT_LT(vstorage_.MaxBytesForLevel(3), 110000U); | |
319 | ASSERT_GT(vstorage_.MaxBytesForLevel(3), 100000U); | |
320 | ASSERT_LT(vstorage_.MaxBytesForLevel(4), 370000U); | |
321 | ASSERT_GT(vstorage_.MaxBytesForLevel(4), 360000U); | |
322 | } | |
323 | ||
324 | TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicWithLargeL0_3) { | |
325 | ioptions_.level_compaction_dynamic_level_bytes = true; | |
326 | mutable_cf_options_.max_bytes_for_level_base = 10000; | |
327 | mutable_cf_options_.max_bytes_for_level_multiplier = 5; | |
328 | mutable_cf_options_.level0_file_num_compaction_trigger = 2; | |
329 | ||
330 | Add(0, 11U, "1", "2", 5000U); | |
331 | Add(0, 12U, "1", "2", 5000U); | |
332 | Add(0, 13U, "1", "2", 5000U); | |
333 | Add(0, 14U, "1", "2", 5000U); | |
334 | Add(0, 15U, "1", "2", 5000U); | |
335 | Add(0, 16U, "1", "2", 5000U); | |
336 | ||
337 | Add(5, 4U, "1", "2", 1286250U); | |
338 | Add(4, 5U, "1", "2", 200000U); | |
339 | Add(3, 6U, "1", "2", 40000U); | |
340 | Add(2, 7U, "1", "2", 8000U); | |
341 | ||
342 | vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_); | |
343 | ASSERT_EQ(0, logger_->log_count); | |
344 | ASSERT_EQ(2, vstorage_.base_level()); | |
345 | // level multiplier should be 3.5 | |
346 | ASSERT_LT(vstorage_.level_multiplier(), 3.6); | |
347 | ASSERT_GT(vstorage_.level_multiplier(), 3.4); | |
348 | // Level size should be around 30,000, 105,000, 367,500 | |
349 | ASSERT_EQ(30000U, vstorage_.MaxBytesForLevel(2)); | |
350 | ASSERT_LT(vstorage_.MaxBytesForLevel(3), 110000U); | |
351 | ASSERT_GT(vstorage_.MaxBytesForLevel(3), 100000U); | |
352 | ASSERT_LT(vstorage_.MaxBytesForLevel(4), 370000U); | |
353 | ASSERT_GT(vstorage_.MaxBytesForLevel(4), 360000U); | |
354 | } | |
355 | ||
7c673cae FG |
356 | TEST_F(VersionStorageInfoTest, EstimateLiveDataSize) { |
357 | // Test whether the overlaps are detected as expected | |
358 | Add(1, 1U, "4", "7", 1U); // Perfect overlap with last level | |
359 | Add(2, 2U, "3", "5", 1U); // Partial overlap with last level | |
360 | Add(2, 3U, "6", "8", 1U); // Partial overlap with last level | |
361 | Add(3, 4U, "1", "9", 1U); // Contains range of last level | |
362 | Add(4, 5U, "4", "5", 1U); // Inside range of last level | |
363 | Add(4, 5U, "6", "7", 1U); // Inside range of last level | |
364 | Add(5, 6U, "4", "7", 10U); | |
365 | ASSERT_EQ(10U, vstorage_.EstimateLiveDataSize()); | |
366 | } | |
367 | ||
368 | TEST_F(VersionStorageInfoTest, EstimateLiveDataSize2) { | |
369 | Add(0, 1U, "9", "9", 1U); // Level 0 is not ordered | |
370 | Add(0, 1U, "5", "6", 1U); // Ignored because of [5,6] in l1 | |
371 | Add(1, 1U, "1", "2", 1U); // Ignored because of [2,3] in l2 | |
372 | Add(1, 2U, "3", "4", 1U); // Ignored because of [2,3] in l2 | |
373 | Add(1, 3U, "5", "6", 1U); | |
374 | Add(2, 4U, "2", "3", 1U); | |
375 | Add(3, 5U, "7", "8", 1U); | |
376 | ASSERT_EQ(4U, vstorage_.EstimateLiveDataSize()); | |
377 | } | |
378 | ||
11fdf7f2 TL |
379 | TEST_F(VersionStorageInfoTest, GetOverlappingInputs) { |
380 | // Two files that overlap at the range deletion tombstone sentinel. | |
381 | Add(1, 1U, {"a", 0, kTypeValue}, {"b", kMaxSequenceNumber, kTypeRangeDeletion}, 1); | |
382 | Add(1, 2U, {"b", 0, kTypeValue}, {"c", 0, kTypeValue}, 1); | |
383 | // Two files that overlap at the same user key. | |
384 | Add(1, 3U, {"d", 0, kTypeValue}, {"e", kMaxSequenceNumber, kTypeValue}, 1); | |
385 | Add(1, 4U, {"e", 0, kTypeValue}, {"f", 0, kTypeValue}, 1); | |
386 | // Two files that do not overlap. | |
387 | Add(1, 5U, {"g", 0, kTypeValue}, {"h", 0, kTypeValue}, 1); | |
388 | Add(1, 6U, {"i", 0, kTypeValue}, {"j", 0, kTypeValue}, 1); | |
389 | vstorage_.UpdateNumNonEmptyLevels(); | |
390 | vstorage_.GenerateLevelFilesBrief(); | |
391 | ||
392 | ASSERT_EQ("1,2", GetOverlappingFiles( | |
393 | 1, {"a", 0, kTypeValue}, {"b", 0, kTypeValue})); | |
394 | ASSERT_EQ("1", GetOverlappingFiles( | |
395 | 1, {"a", 0, kTypeValue}, {"b", kMaxSequenceNumber, kTypeRangeDeletion})); | |
396 | ASSERT_EQ("2", GetOverlappingFiles( | |
397 | 1, {"b", kMaxSequenceNumber, kTypeValue}, {"c", 0, kTypeValue})); | |
398 | ASSERT_EQ("3,4", GetOverlappingFiles( | |
399 | 1, {"d", 0, kTypeValue}, {"e", 0, kTypeValue})); | |
400 | ASSERT_EQ("3", GetOverlappingFiles( | |
401 | 1, {"d", 0, kTypeValue}, {"e", kMaxSequenceNumber, kTypeRangeDeletion})); | |
402 | ASSERT_EQ("3,4", GetOverlappingFiles( | |
403 | 1, {"e", kMaxSequenceNumber, kTypeValue}, {"f", 0, kTypeValue})); | |
404 | ASSERT_EQ("3,4", GetOverlappingFiles( | |
405 | 1, {"e", 0, kTypeValue}, {"f", 0, kTypeValue})); | |
406 | ASSERT_EQ("5", GetOverlappingFiles( | |
407 | 1, {"g", 0, kTypeValue}, {"h", 0, kTypeValue})); | |
408 | ASSERT_EQ("6", GetOverlappingFiles( | |
409 | 1, {"i", 0, kTypeValue}, {"j", 0, kTypeValue})); | |
410 | } | |
411 | ||
412 | ||
7c673cae FG |
413 | class FindLevelFileTest : public testing::Test { |
414 | public: | |
415 | LevelFilesBrief file_level_; | |
416 | bool disjoint_sorted_files_; | |
417 | Arena arena_; | |
418 | ||
419 | FindLevelFileTest() : disjoint_sorted_files_(true) { } | |
420 | ||
494da23a | 421 | ~FindLevelFileTest() override {} |
7c673cae FG |
422 | |
423 | void LevelFileInit(size_t num = 0) { | |
424 | char* mem = arena_.AllocateAligned(num * sizeof(FdWithKeyRange)); | |
425 | file_level_.files = new (mem)FdWithKeyRange[num]; | |
426 | file_level_.num_files = 0; | |
427 | } | |
428 | ||
429 | void Add(const char* smallest, const char* largest, | |
430 | SequenceNumber smallest_seq = 100, | |
431 | SequenceNumber largest_seq = 100) { | |
432 | InternalKey smallest_key = InternalKey(smallest, smallest_seq, kTypeValue); | |
433 | InternalKey largest_key = InternalKey(largest, largest_seq, kTypeValue); | |
434 | ||
435 | Slice smallest_slice = smallest_key.Encode(); | |
436 | Slice largest_slice = largest_key.Encode(); | |
437 | ||
438 | char* mem = arena_.AllocateAligned( | |
439 | smallest_slice.size() + largest_slice.size()); | |
440 | memcpy(mem, smallest_slice.data(), smallest_slice.size()); | |
441 | memcpy(mem + smallest_slice.size(), largest_slice.data(), | |
442 | largest_slice.size()); | |
443 | ||
444 | // add to file_level_ | |
445 | size_t num = file_level_.num_files; | |
446 | auto& file = file_level_.files[num]; | |
447 | file.fd = FileDescriptor(num + 1, 0, 0); | |
448 | file.smallest_key = Slice(mem, smallest_slice.size()); | |
449 | file.largest_key = Slice(mem + smallest_slice.size(), | |
450 | largest_slice.size()); | |
451 | file_level_.num_files++; | |
452 | } | |
453 | ||
454 | int Find(const char* key) { | |
455 | InternalKey target(key, 100, kTypeValue); | |
456 | InternalKeyComparator cmp(BytewiseComparator()); | |
457 | return FindFile(cmp, file_level_, target.Encode()); | |
458 | } | |
459 | ||
460 | bool Overlaps(const char* smallest, const char* largest) { | |
461 | InternalKeyComparator cmp(BytewiseComparator()); | |
462 | Slice s(smallest != nullptr ? smallest : ""); | |
463 | Slice l(largest != nullptr ? largest : ""); | |
464 | return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, file_level_, | |
465 | (smallest != nullptr ? &s : nullptr), | |
466 | (largest != nullptr ? &l : nullptr)); | |
467 | } | |
468 | }; | |
469 | ||
470 | TEST_F(FindLevelFileTest, LevelEmpty) { | |
471 | LevelFileInit(0); | |
472 | ||
473 | ASSERT_EQ(0, Find("foo")); | |
474 | ASSERT_TRUE(! Overlaps("a", "z")); | |
475 | ASSERT_TRUE(! Overlaps(nullptr, "z")); | |
476 | ASSERT_TRUE(! Overlaps("a", nullptr)); | |
477 | ASSERT_TRUE(! Overlaps(nullptr, nullptr)); | |
478 | } | |
479 | ||
480 | TEST_F(FindLevelFileTest, LevelSingle) { | |
481 | LevelFileInit(1); | |
482 | ||
483 | Add("p", "q"); | |
484 | ASSERT_EQ(0, Find("a")); | |
485 | ASSERT_EQ(0, Find("p")); | |
486 | ASSERT_EQ(0, Find("p1")); | |
487 | ASSERT_EQ(0, Find("q")); | |
488 | ASSERT_EQ(1, Find("q1")); | |
489 | ASSERT_EQ(1, Find("z")); | |
490 | ||
491 | ASSERT_TRUE(! Overlaps("a", "b")); | |
492 | ASSERT_TRUE(! Overlaps("z1", "z2")); | |
493 | ASSERT_TRUE(Overlaps("a", "p")); | |
494 | ASSERT_TRUE(Overlaps("a", "q")); | |
495 | ASSERT_TRUE(Overlaps("a", "z")); | |
496 | ASSERT_TRUE(Overlaps("p", "p1")); | |
497 | ASSERT_TRUE(Overlaps("p", "q")); | |
498 | ASSERT_TRUE(Overlaps("p", "z")); | |
499 | ASSERT_TRUE(Overlaps("p1", "p2")); | |
500 | ASSERT_TRUE(Overlaps("p1", "z")); | |
501 | ASSERT_TRUE(Overlaps("q", "q")); | |
502 | ASSERT_TRUE(Overlaps("q", "q1")); | |
503 | ||
504 | ASSERT_TRUE(! Overlaps(nullptr, "j")); | |
505 | ASSERT_TRUE(! Overlaps("r", nullptr)); | |
506 | ASSERT_TRUE(Overlaps(nullptr, "p")); | |
507 | ASSERT_TRUE(Overlaps(nullptr, "p1")); | |
508 | ASSERT_TRUE(Overlaps("q", nullptr)); | |
509 | ASSERT_TRUE(Overlaps(nullptr, nullptr)); | |
510 | } | |
511 | ||
512 | TEST_F(FindLevelFileTest, LevelMultiple) { | |
513 | LevelFileInit(4); | |
514 | ||
515 | Add("150", "200"); | |
516 | Add("200", "250"); | |
517 | Add("300", "350"); | |
518 | Add("400", "450"); | |
519 | ASSERT_EQ(0, Find("100")); | |
520 | ASSERT_EQ(0, Find("150")); | |
521 | ASSERT_EQ(0, Find("151")); | |
522 | ASSERT_EQ(0, Find("199")); | |
523 | ASSERT_EQ(0, Find("200")); | |
524 | ASSERT_EQ(1, Find("201")); | |
525 | ASSERT_EQ(1, Find("249")); | |
526 | ASSERT_EQ(1, Find("250")); | |
527 | ASSERT_EQ(2, Find("251")); | |
528 | ASSERT_EQ(2, Find("299")); | |
529 | ASSERT_EQ(2, Find("300")); | |
530 | ASSERT_EQ(2, Find("349")); | |
531 | ASSERT_EQ(2, Find("350")); | |
532 | ASSERT_EQ(3, Find("351")); | |
533 | ASSERT_EQ(3, Find("400")); | |
534 | ASSERT_EQ(3, Find("450")); | |
535 | ASSERT_EQ(4, Find("451")); | |
536 | ||
537 | ASSERT_TRUE(! Overlaps("100", "149")); | |
538 | ASSERT_TRUE(! Overlaps("251", "299")); | |
539 | ASSERT_TRUE(! Overlaps("451", "500")); | |
540 | ASSERT_TRUE(! Overlaps("351", "399")); | |
541 | ||
542 | ASSERT_TRUE(Overlaps("100", "150")); | |
543 | ASSERT_TRUE(Overlaps("100", "200")); | |
544 | ASSERT_TRUE(Overlaps("100", "300")); | |
545 | ASSERT_TRUE(Overlaps("100", "400")); | |
546 | ASSERT_TRUE(Overlaps("100", "500")); | |
547 | ASSERT_TRUE(Overlaps("375", "400")); | |
548 | ASSERT_TRUE(Overlaps("450", "450")); | |
549 | ASSERT_TRUE(Overlaps("450", "500")); | |
550 | } | |
551 | ||
552 | TEST_F(FindLevelFileTest, LevelMultipleNullBoundaries) { | |
553 | LevelFileInit(4); | |
554 | ||
555 | Add("150", "200"); | |
556 | Add("200", "250"); | |
557 | Add("300", "350"); | |
558 | Add("400", "450"); | |
559 | ASSERT_TRUE(! Overlaps(nullptr, "149")); | |
560 | ASSERT_TRUE(! Overlaps("451", nullptr)); | |
561 | ASSERT_TRUE(Overlaps(nullptr, nullptr)); | |
562 | ASSERT_TRUE(Overlaps(nullptr, "150")); | |
563 | ASSERT_TRUE(Overlaps(nullptr, "199")); | |
564 | ASSERT_TRUE(Overlaps(nullptr, "200")); | |
565 | ASSERT_TRUE(Overlaps(nullptr, "201")); | |
566 | ASSERT_TRUE(Overlaps(nullptr, "400")); | |
567 | ASSERT_TRUE(Overlaps(nullptr, "800")); | |
568 | ASSERT_TRUE(Overlaps("100", nullptr)); | |
569 | ASSERT_TRUE(Overlaps("200", nullptr)); | |
570 | ASSERT_TRUE(Overlaps("449", nullptr)); | |
571 | ASSERT_TRUE(Overlaps("450", nullptr)); | |
572 | } | |
573 | ||
574 | TEST_F(FindLevelFileTest, LevelOverlapSequenceChecks) { | |
575 | LevelFileInit(1); | |
576 | ||
577 | Add("200", "200", 5000, 3000); | |
578 | ASSERT_TRUE(! Overlaps("199", "199")); | |
579 | ASSERT_TRUE(! Overlaps("201", "300")); | |
580 | ASSERT_TRUE(Overlaps("200", "200")); | |
581 | ASSERT_TRUE(Overlaps("190", "200")); | |
582 | ASSERT_TRUE(Overlaps("200", "210")); | |
583 | } | |
584 | ||
585 | TEST_F(FindLevelFileTest, LevelOverlappingFiles) { | |
586 | LevelFileInit(2); | |
587 | ||
588 | Add("150", "600"); | |
589 | Add("400", "500"); | |
590 | disjoint_sorted_files_ = false; | |
591 | ASSERT_TRUE(! Overlaps("100", "149")); | |
592 | ASSERT_TRUE(! Overlaps("601", "700")); | |
593 | ASSERT_TRUE(Overlaps("100", "150")); | |
594 | ASSERT_TRUE(Overlaps("100", "200")); | |
595 | ASSERT_TRUE(Overlaps("100", "300")); | |
596 | ASSERT_TRUE(Overlaps("100", "400")); | |
597 | ASSERT_TRUE(Overlaps("100", "500")); | |
598 | ASSERT_TRUE(Overlaps("375", "400")); | |
599 | ASSERT_TRUE(Overlaps("450", "450")); | |
600 | ASSERT_TRUE(Overlaps("450", "500")); | |
601 | ASSERT_TRUE(Overlaps("450", "700")); | |
602 | ASSERT_TRUE(Overlaps("600", "700")); | |
603 | } | |
604 | ||
494da23a | 605 | class VersionSetTestBase { |
11fdf7f2 | 606 | public: |
494da23a TL |
607 | const static std::string kColumnFamilyName1; |
608 | const static std::string kColumnFamilyName2; | |
609 | const static std::string kColumnFamilyName3; | |
610 | ||
611 | VersionSetTestBase() | |
11fdf7f2 TL |
612 | : env_(Env::Default()), |
613 | dbname_(test::PerThreadDBPath("version_set_test")), | |
614 | db_options_(), | |
615 | mutable_cf_options_(cf_options_), | |
616 | table_cache_(NewLRUCache(50000, 16)), | |
617 | write_buffer_manager_(db_options_.db_write_buffer_size), | |
618 | versions_(new VersionSet(dbname_, &db_options_, env_options_, | |
619 | table_cache_.get(), &write_buffer_manager_, | |
620 | &write_controller_)), | |
621 | shutting_down_(false), | |
622 | mock_table_factory_(std::make_shared<mock::MockTableFactory>()) { | |
623 | EXPECT_OK(env_->CreateDirIfMissing(dbname_)); | |
624 | db_options_.db_paths.emplace_back(dbname_, | |
625 | std::numeric_limits<uint64_t>::max()); | |
626 | } | |
627 | ||
494da23a TL |
628 | void PrepareManifest(std::vector<ColumnFamilyDescriptor>* column_families, |
629 | SequenceNumber* last_seqno, | |
630 | std::unique_ptr<log::Writer>* log_writer) { | |
631 | assert(column_families != nullptr); | |
632 | assert(last_seqno != nullptr); | |
633 | assert(log_writer != nullptr); | |
11fdf7f2 TL |
634 | VersionEdit new_db; |
635 | new_db.SetLogNumber(0); | |
636 | new_db.SetNextFile(2); | |
637 | new_db.SetLastSequence(0); | |
638 | ||
494da23a TL |
639 | const std::vector<std::string> cf_names = { |
640 | kDefaultColumnFamilyName, kColumnFamilyName1, kColumnFamilyName2, | |
641 | kColumnFamilyName3}; | |
11fdf7f2 TL |
642 | const int kInitialNumOfCfs = static_cast<int>(cf_names.size()); |
643 | autovector<VersionEdit> new_cfs; | |
644 | uint64_t last_seq = 1; | |
645 | uint32_t cf_id = 1; | |
646 | for (int i = 1; i != kInitialNumOfCfs; ++i) { | |
647 | VersionEdit new_cf; | |
648 | new_cf.AddColumnFamily(cf_names[i]); | |
649 | new_cf.SetColumnFamily(cf_id++); | |
650 | new_cf.SetLogNumber(0); | |
651 | new_cf.SetNextFile(2); | |
652 | new_cf.SetLastSequence(last_seq++); | |
653 | new_cfs.emplace_back(new_cf); | |
654 | } | |
494da23a | 655 | *last_seqno = last_seq; |
11fdf7f2 TL |
656 | |
657 | const std::string manifest = DescriptorFileName(dbname_, 1); | |
494da23a | 658 | std::unique_ptr<WritableFile> file; |
11fdf7f2 TL |
659 | Status s = env_->NewWritableFile( |
660 | manifest, &file, env_->OptimizeForManifestWrite(env_options_)); | |
661 | ASSERT_OK(s); | |
494da23a | 662 | std::unique_ptr<WritableFileWriter> file_writer( |
11fdf7f2 TL |
663 | new WritableFileWriter(std::move(file), manifest, env_options_)); |
664 | { | |
494da23a | 665 | log_writer->reset(new log::Writer(std::move(file_writer), 0, false)); |
11fdf7f2 TL |
666 | std::string record; |
667 | new_db.EncodeTo(&record); | |
494da23a | 668 | s = (*log_writer)->AddRecord(record); |
11fdf7f2 | 669 | for (const auto& e : new_cfs) { |
494da23a | 670 | record.clear(); |
11fdf7f2 | 671 | e.EncodeTo(&record); |
494da23a | 672 | s = (*log_writer)->AddRecord(record); |
11fdf7f2 TL |
673 | ASSERT_OK(s); |
674 | } | |
675 | } | |
676 | ASSERT_OK(s); | |
11fdf7f2 | 677 | |
11fdf7f2 TL |
678 | cf_options_.table_factory = mock_table_factory_; |
679 | for (const auto& cf_name : cf_names) { | |
494da23a | 680 | column_families->emplace_back(cf_name, cf_options_); |
11fdf7f2 | 681 | } |
494da23a TL |
682 | } |
683 | ||
684 | // Create DB with 3 column families. | |
685 | void NewDB() { | |
686 | std::vector<ColumnFamilyDescriptor> column_families; | |
687 | SequenceNumber last_seqno; | |
688 | std::unique_ptr<log::Writer> log_writer; | |
689 | ||
690 | PrepareManifest(&column_families, &last_seqno, &log_writer); | |
691 | log_writer.reset(); | |
692 | // Make "CURRENT" file point to the new manifest file. | |
693 | Status s = SetCurrentFile(env_, dbname_, 1, nullptr); | |
694 | ASSERT_OK(s); | |
11fdf7f2 TL |
695 | |
696 | EXPECT_OK(versions_->Recover(column_families, false)); | |
494da23a | 697 | EXPECT_EQ(column_families.size(), |
11fdf7f2 | 698 | versions_->GetColumnFamilySet()->NumberOfColumnFamilies()); |
11fdf7f2 TL |
699 | } |
700 | ||
701 | Env* env_; | |
702 | const std::string dbname_; | |
703 | EnvOptions env_options_; | |
704 | ImmutableDBOptions db_options_; | |
705 | ColumnFamilyOptions cf_options_; | |
706 | MutableCFOptions mutable_cf_options_; | |
707 | std::shared_ptr<Cache> table_cache_; | |
708 | WriteController write_controller_; | |
709 | WriteBufferManager write_buffer_manager_; | |
494da23a | 710 | std::shared_ptr<VersionSet> versions_; |
11fdf7f2 TL |
711 | InstrumentedMutex mutex_; |
712 | std::atomic<bool> shutting_down_; | |
713 | std::shared_ptr<mock::MockTableFactory> mock_table_factory_; | |
11fdf7f2 TL |
714 | }; |
715 | ||
494da23a TL |
716 | const std::string VersionSetTestBase::kColumnFamilyName1 = "alice"; |
717 | const std::string VersionSetTestBase::kColumnFamilyName2 = "bob"; | |
718 | const std::string VersionSetTestBase::kColumnFamilyName3 = "charles"; | |
719 | ||
720 | class VersionSetTest : public VersionSetTestBase, public testing::Test { | |
721 | public: | |
722 | VersionSetTest() : VersionSetTestBase() {} | |
723 | }; | |
724 | ||
725 | TEST_F(VersionSetTest, SameColumnFamilyGroupCommit) { | |
11fdf7f2 TL |
726 | NewDB(); |
727 | const int kGroupSize = 5; | |
494da23a TL |
728 | autovector<VersionEdit> edits; |
729 | for (int i = 0; i != kGroupSize; ++i) { | |
730 | edits.emplace_back(VersionEdit()); | |
731 | } | |
732 | autovector<ColumnFamilyData*> cfds; | |
733 | autovector<const MutableCFOptions*> all_mutable_cf_options; | |
734 | autovector<autovector<VersionEdit*>> edit_lists; | |
11fdf7f2 | 735 | for (int i = 0; i != kGroupSize; ++i) { |
494da23a TL |
736 | cfds.emplace_back(versions_->GetColumnFamilySet()->GetDefault()); |
737 | all_mutable_cf_options.emplace_back(&mutable_cf_options_); | |
738 | autovector<VersionEdit*> edit_list; | |
739 | edit_list.emplace_back(&edits[i]); | |
740 | edit_lists.emplace_back(edit_list); | |
11fdf7f2 TL |
741 | } |
742 | ||
494da23a TL |
743 | SyncPoint::GetInstance()->DisableProcessing(); |
744 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
11fdf7f2 TL |
745 | int count = 0; |
746 | SyncPoint::GetInstance()->SetCallBack( | |
747 | "VersionSet::ProcessManifestWrites:SameColumnFamily", [&](void* arg) { | |
748 | uint32_t* cf_id = reinterpret_cast<uint32_t*>(arg); | |
749 | EXPECT_EQ(0, *cf_id); | |
750 | ++count; | |
751 | }); | |
752 | SyncPoint::GetInstance()->EnableProcessing(); | |
753 | mutex_.Lock(); | |
754 | Status s = | |
755 | versions_->LogAndApply(cfds, all_mutable_cf_options, edit_lists, &mutex_); | |
756 | mutex_.Unlock(); | |
757 | EXPECT_OK(s); | |
758 | EXPECT_EQ(kGroupSize - 1, count); | |
759 | } | |
494da23a TL |
760 | |
761 | TEST_F(VersionSetTest, HandleValidAtomicGroup) { | |
762 | std::vector<ColumnFamilyDescriptor> column_families; | |
763 | SequenceNumber last_seqno; | |
764 | std::unique_ptr<log::Writer> log_writer; | |
765 | PrepareManifest(&column_families, &last_seqno, &log_writer); | |
766 | ||
767 | // Append multiple version edits that form an atomic group | |
768 | const int kAtomicGroupSize = 3; | |
769 | std::vector<VersionEdit> edits(kAtomicGroupSize); | |
770 | int remaining = kAtomicGroupSize; | |
771 | for (size_t i = 0; i != edits.size(); ++i) { | |
772 | edits[i].SetLogNumber(0); | |
773 | edits[i].SetNextFile(2); | |
774 | edits[i].MarkAtomicGroup(--remaining); | |
775 | edits[i].SetLastSequence(last_seqno++); | |
776 | } | |
777 | Status s; | |
778 | for (const auto& edit : edits) { | |
779 | std::string record; | |
780 | edit.EncodeTo(&record); | |
781 | s = log_writer->AddRecord(record); | |
782 | ASSERT_OK(s); | |
783 | } | |
784 | log_writer.reset(); | |
785 | ||
786 | s = SetCurrentFile(env_, dbname_, 1, nullptr); | |
787 | ASSERT_OK(s); | |
788 | ||
789 | SyncPoint::GetInstance()->DisableProcessing(); | |
790 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
791 | ||
792 | bool first_in_atomic_group = false; | |
793 | bool last_in_atomic_group = false; | |
794 | ||
795 | SyncPoint::GetInstance()->SetCallBack( | |
796 | "VersionSet::Recover:FirstInAtomicGroup", [&](void* arg) { | |
797 | VersionEdit* e = reinterpret_cast<VersionEdit*>(arg); | |
798 | EXPECT_EQ(edits.front().DebugString(), | |
799 | e->DebugString()); // compare based on value | |
800 | first_in_atomic_group = true; | |
801 | }); | |
802 | SyncPoint::GetInstance()->SetCallBack( | |
803 | "VersionSet::Recover:LastInAtomicGroup", [&](void* arg) { | |
804 | VersionEdit* e = reinterpret_cast<VersionEdit*>(arg); | |
805 | EXPECT_EQ(edits.back().DebugString(), | |
806 | e->DebugString()); // compare based on value | |
807 | EXPECT_TRUE(first_in_atomic_group); | |
808 | last_in_atomic_group = true; | |
809 | }); | |
810 | SyncPoint::GetInstance()->EnableProcessing(); | |
811 | ||
812 | EXPECT_OK(versions_->Recover(column_families, false)); | |
813 | EXPECT_EQ(column_families.size(), | |
814 | versions_->GetColumnFamilySet()->NumberOfColumnFamilies()); | |
815 | EXPECT_TRUE(first_in_atomic_group); | |
816 | EXPECT_TRUE(last_in_atomic_group); | |
817 | } | |
818 | ||
819 | TEST_F(VersionSetTest, HandleIncompleteTrailingAtomicGroup) { | |
820 | std::vector<ColumnFamilyDescriptor> column_families; | |
821 | SequenceNumber last_seqno; | |
822 | std::unique_ptr<log::Writer> log_writer; | |
823 | PrepareManifest(&column_families, &last_seqno, &log_writer); | |
824 | ||
825 | // Append multiple version edits that form an atomic group | |
826 | const int kAtomicGroupSize = 4; | |
827 | const int kNumberOfPersistedVersionEdits = kAtomicGroupSize - 1; | |
828 | std::vector<VersionEdit> edits(kNumberOfPersistedVersionEdits); | |
829 | int remaining = kAtomicGroupSize; | |
830 | for (size_t i = 0; i != edits.size(); ++i) { | |
831 | edits[i].SetLogNumber(0); | |
832 | edits[i].SetNextFile(2); | |
833 | edits[i].MarkAtomicGroup(--remaining); | |
834 | edits[i].SetLastSequence(last_seqno++); | |
835 | } | |
836 | Status s; | |
837 | for (const auto& edit : edits) { | |
838 | std::string record; | |
839 | edit.EncodeTo(&record); | |
840 | s = log_writer->AddRecord(record); | |
841 | ASSERT_OK(s); | |
842 | } | |
843 | log_writer.reset(); | |
844 | ||
845 | s = SetCurrentFile(env_, dbname_, 1, nullptr); | |
846 | ASSERT_OK(s); | |
847 | ||
848 | SyncPoint::GetInstance()->DisableProcessing(); | |
849 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
850 | ||
851 | bool first_in_atomic_group = false; | |
852 | bool last_in_atomic_group = false; | |
853 | size_t num = 0; | |
854 | ||
855 | SyncPoint::GetInstance()->SetCallBack( | |
856 | "VersionSet::Recover:FirstInAtomicGroup", [&](void* arg) { | |
857 | VersionEdit* e = reinterpret_cast<VersionEdit*>(arg); | |
858 | EXPECT_EQ(edits.front().DebugString(), | |
859 | e->DebugString()); // compare based on value | |
860 | first_in_atomic_group = true; | |
861 | }); | |
862 | SyncPoint::GetInstance()->SetCallBack( | |
863 | "VersionSet::Recover:LastInAtomicGroup", | |
864 | [&](void* /* arg */) { last_in_atomic_group = true; }); | |
865 | SyncPoint::GetInstance()->SetCallBack("VersionSet::Recover:AtomicGroup", | |
866 | [&](void* /* arg */) { ++num; }); | |
867 | SyncPoint::GetInstance()->EnableProcessing(); | |
868 | ||
869 | EXPECT_OK(versions_->Recover(column_families, false)); | |
870 | EXPECT_EQ(column_families.size(), | |
871 | versions_->GetColumnFamilySet()->NumberOfColumnFamilies()); | |
872 | EXPECT_TRUE(first_in_atomic_group); | |
873 | EXPECT_FALSE(last_in_atomic_group); | |
874 | EXPECT_EQ(kNumberOfPersistedVersionEdits, num); | |
875 | } | |
876 | ||
877 | TEST_F(VersionSetTest, HandleCorruptedAtomicGroup) { | |
878 | std::vector<ColumnFamilyDescriptor> column_families; | |
879 | SequenceNumber last_seqno; | |
880 | std::unique_ptr<log::Writer> log_writer; | |
881 | PrepareManifest(&column_families, &last_seqno, &log_writer); | |
882 | ||
883 | // Append multiple version edits that form an atomic group | |
884 | const int kAtomicGroupSize = 4; | |
885 | std::vector<VersionEdit> edits(kAtomicGroupSize); | |
886 | int remaining = kAtomicGroupSize; | |
887 | for (size_t i = 0; i != edits.size(); ++i) { | |
888 | edits[i].SetLogNumber(0); | |
889 | edits[i].SetNextFile(2); | |
890 | if (i != (kAtomicGroupSize / 2)) { | |
891 | edits[i].MarkAtomicGroup(--remaining); | |
892 | } | |
893 | edits[i].SetLastSequence(last_seqno++); | |
894 | } | |
895 | Status s; | |
896 | for (const auto& edit : edits) { | |
897 | std::string record; | |
898 | edit.EncodeTo(&record); | |
899 | s = log_writer->AddRecord(record); | |
900 | ASSERT_OK(s); | |
901 | } | |
902 | log_writer.reset(); | |
903 | ||
904 | s = SetCurrentFile(env_, dbname_, 1, nullptr); | |
905 | ASSERT_OK(s); | |
906 | ||
907 | SyncPoint::GetInstance()->DisableProcessing(); | |
908 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
909 | ||
910 | bool mixed = false; | |
911 | SyncPoint::GetInstance()->SetCallBack( | |
912 | "VersionSet::Recover:AtomicGroupMixedWithNormalEdits", [&](void* arg) { | |
913 | VersionEdit* e = reinterpret_cast<VersionEdit*>(arg); | |
914 | EXPECT_EQ(edits[kAtomicGroupSize / 2].DebugString(), e->DebugString()); | |
915 | mixed = true; | |
916 | }); | |
917 | SyncPoint::GetInstance()->EnableProcessing(); | |
918 | EXPECT_NOK(versions_->Recover(column_families, false)); | |
919 | EXPECT_EQ(column_families.size(), | |
920 | versions_->GetColumnFamilySet()->NumberOfColumnFamilies()); | |
921 | EXPECT_TRUE(mixed); | |
922 | } | |
923 | ||
924 | TEST_F(VersionSetTest, HandleIncorrectAtomicGroupSize) { | |
925 | std::vector<ColumnFamilyDescriptor> column_families; | |
926 | SequenceNumber last_seqno; | |
927 | std::unique_ptr<log::Writer> log_writer; | |
928 | PrepareManifest(&column_families, &last_seqno, &log_writer); | |
929 | ||
930 | // Append multiple version edits that form an atomic group | |
931 | const int kAtomicGroupSize = 4; | |
932 | std::vector<VersionEdit> edits(kAtomicGroupSize); | |
933 | int remaining = kAtomicGroupSize; | |
934 | for (size_t i = 0; i != edits.size(); ++i) { | |
935 | edits[i].SetLogNumber(0); | |
936 | edits[i].SetNextFile(2); | |
937 | if (i != 1) { | |
938 | edits[i].MarkAtomicGroup(--remaining); | |
939 | } else { | |
940 | edits[i].MarkAtomicGroup(remaining--); | |
941 | } | |
942 | edits[i].SetLastSequence(last_seqno++); | |
943 | } | |
944 | Status s; | |
945 | for (const auto& edit : edits) { | |
946 | std::string record; | |
947 | edit.EncodeTo(&record); | |
948 | s = log_writer->AddRecord(record); | |
949 | ASSERT_OK(s); | |
950 | } | |
951 | log_writer.reset(); | |
952 | ||
953 | s = SetCurrentFile(env_, dbname_, 1, nullptr); | |
954 | ASSERT_OK(s); | |
955 | ||
956 | SyncPoint::GetInstance()->DisableProcessing(); | |
957 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
958 | ||
959 | bool incorrect_group_size = false; | |
960 | SyncPoint::GetInstance()->SetCallBack( | |
961 | "VersionSet::Recover:IncorrectAtomicGroupSize", [&](void* arg) { | |
962 | VersionEdit* e = reinterpret_cast<VersionEdit*>(arg); | |
963 | EXPECT_EQ(edits[1].DebugString(), e->DebugString()); | |
964 | incorrect_group_size = true; | |
965 | }); | |
966 | SyncPoint::GetInstance()->EnableProcessing(); | |
967 | EXPECT_NOK(versions_->Recover(column_families, false)); | |
968 | EXPECT_EQ(column_families.size(), | |
969 | versions_->GetColumnFamilySet()->NumberOfColumnFamilies()); | |
970 | EXPECT_TRUE(incorrect_group_size); | |
971 | } | |
972 | ||
973 | class VersionSetTestDropOneCF : public VersionSetTestBase, | |
974 | public testing::TestWithParam<std::string> { | |
975 | public: | |
976 | VersionSetTestDropOneCF() : VersionSetTestBase() {} | |
977 | }; | |
978 | ||
979 | // This test simulates the following execution sequence | |
980 | // Time thread1 bg_flush_thr | |
981 | // | Prepare version edits (e1,e2,e3) for atomic | |
982 | // | flush cf1, cf2, cf3 | |
983 | // | Enqueue e to drop cfi | |
984 | // | to manifest_writers_ | |
985 | // | Enqueue (e1,e2,e3) to manifest_writers_ | |
986 | // | | |
987 | // | Apply e, | |
988 | // | cfi.IsDropped() is true | |
989 | // | Apply (e1,e2,e3), | |
990 | // | since cfi.IsDropped() == true, we need to | |
991 | // | drop ei and write the rest to MANIFEST. | |
992 | // V | |
993 | // | |
994 | // Repeat the test for i = 1, 2, 3 to simulate dropping the first, middle and | |
995 | // last column family in an atomic group. | |
996 | TEST_P(VersionSetTestDropOneCF, HandleDroppedColumnFamilyInAtomicGroup) { | |
997 | std::vector<ColumnFamilyDescriptor> column_families; | |
998 | SequenceNumber last_seqno; | |
999 | std::unique_ptr<log::Writer> log_writer; | |
1000 | PrepareManifest(&column_families, &last_seqno, &log_writer); | |
1001 | Status s = SetCurrentFile(env_, dbname_, 1, nullptr); | |
1002 | ASSERT_OK(s); | |
1003 | ||
1004 | EXPECT_OK(versions_->Recover(column_families, false /* read_only */)); | |
1005 | EXPECT_EQ(column_families.size(), | |
1006 | versions_->GetColumnFamilySet()->NumberOfColumnFamilies()); | |
1007 | ||
1008 | const int kAtomicGroupSize = 3; | |
1009 | const std::vector<std::string> non_default_cf_names = { | |
1010 | kColumnFamilyName1, kColumnFamilyName2, kColumnFamilyName3}; | |
1011 | ||
1012 | // Drop one column family | |
1013 | VersionEdit drop_cf_edit; | |
1014 | drop_cf_edit.DropColumnFamily(); | |
1015 | const std::string cf_to_drop_name(GetParam()); | |
1016 | auto cfd_to_drop = | |
1017 | versions_->GetColumnFamilySet()->GetColumnFamily(cf_to_drop_name); | |
1018 | ASSERT_NE(nullptr, cfd_to_drop); | |
1019 | // Increase its refcount because cfd_to_drop is used later, and we need to | |
1020 | // prevent it from being deleted. | |
1021 | cfd_to_drop->Ref(); | |
1022 | drop_cf_edit.SetColumnFamily(cfd_to_drop->GetID()); | |
1023 | mutex_.Lock(); | |
1024 | s = versions_->LogAndApply(cfd_to_drop, | |
1025 | *cfd_to_drop->GetLatestMutableCFOptions(), | |
1026 | &drop_cf_edit, &mutex_); | |
1027 | mutex_.Unlock(); | |
1028 | ASSERT_OK(s); | |
1029 | ||
1030 | std::vector<VersionEdit> edits(kAtomicGroupSize); | |
1031 | uint32_t remaining = kAtomicGroupSize; | |
1032 | size_t i = 0; | |
1033 | autovector<ColumnFamilyData*> cfds; | |
1034 | autovector<const MutableCFOptions*> mutable_cf_options_list; | |
1035 | autovector<autovector<VersionEdit*>> edit_lists; | |
1036 | for (const auto& cf_name : non_default_cf_names) { | |
1037 | auto cfd = (cf_name != cf_to_drop_name) | |
1038 | ? versions_->GetColumnFamilySet()->GetColumnFamily(cf_name) | |
1039 | : cfd_to_drop; | |
1040 | ASSERT_NE(nullptr, cfd); | |
1041 | cfds.push_back(cfd); | |
1042 | mutable_cf_options_list.emplace_back(cfd->GetLatestMutableCFOptions()); | |
1043 | edits[i].SetColumnFamily(cfd->GetID()); | |
1044 | edits[i].SetLogNumber(0); | |
1045 | edits[i].SetNextFile(2); | |
1046 | edits[i].MarkAtomicGroup(--remaining); | |
1047 | edits[i].SetLastSequence(last_seqno++); | |
1048 | autovector<VersionEdit*> tmp_edits; | |
1049 | tmp_edits.push_back(&edits[i]); | |
1050 | edit_lists.emplace_back(tmp_edits); | |
1051 | ++i; | |
1052 | } | |
1053 | int called = 0; | |
1054 | SyncPoint::GetInstance()->DisableProcessing(); | |
1055 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
1056 | SyncPoint::GetInstance()->SetCallBack( | |
1057 | "VersionSet::ProcessManifestWrites:CheckOneAtomicGroup", [&](void* arg) { | |
1058 | std::vector<VersionEdit*>* tmp_edits = | |
1059 | reinterpret_cast<std::vector<VersionEdit*>*>(arg); | |
1060 | EXPECT_EQ(kAtomicGroupSize - 1, tmp_edits->size()); | |
1061 | for (const auto e : *tmp_edits) { | |
1062 | bool found = false; | |
1063 | for (const auto& e2 : edits) { | |
1064 | if (&e2 == e) { | |
1065 | found = true; | |
1066 | break; | |
1067 | } | |
1068 | } | |
1069 | ASSERT_TRUE(found); | |
1070 | } | |
1071 | ++called; | |
1072 | }); | |
1073 | SyncPoint::GetInstance()->EnableProcessing(); | |
1074 | mutex_.Lock(); | |
1075 | s = versions_->LogAndApply(cfds, mutable_cf_options_list, edit_lists, | |
1076 | &mutex_); | |
1077 | mutex_.Unlock(); | |
1078 | ASSERT_OK(s); | |
1079 | ASSERT_EQ(1, called); | |
1080 | if (cfd_to_drop->Unref()) { | |
1081 | delete cfd_to_drop; | |
1082 | cfd_to_drop = nullptr; | |
1083 | } | |
1084 | } | |
1085 | ||
1086 | INSTANTIATE_TEST_CASE_P( | |
1087 | AtomicGroup, VersionSetTestDropOneCF, | |
1088 | testing::Values(VersionSetTestBase::kColumnFamilyName1, | |
1089 | VersionSetTestBase::kColumnFamilyName2, | |
1090 | VersionSetTestBase::kColumnFamilyName3)); | |
1091 | ||
7c673cae FG |
1092 | } // namespace rocksdb |
1093 | ||
1094 | int main(int argc, char** argv) { | |
1095 | ::testing::InitGoogleTest(&argc, argv); | |
1096 | return RUN_ALL_TESTS(); | |
1097 | } |