/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/comparator_db_test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Use of this source code is governed by a BSD-style license that can be |
2 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
3 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
4 | | // This source code is licensed under the BSD-style license found in the |
5 | | // LICENSE file in the root directory of this source tree. An additional grant |
6 | | // of patent rights can be found in the PATENTS file in the same directory. |
7 | | // |
8 | | // The following only applies to changes made to this file as part of YugaByte development. |
9 | | // |
10 | | // Portions Copyright (c) YugaByte, Inc. |
11 | | // |
12 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
13 | | // in compliance with the License. You may obtain a copy of the License at |
14 | | // |
15 | | // http://www.apache.org/licenses/LICENSE-2.0 |
16 | | // |
17 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
18 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
19 | | // or implied. See the License for the specific language governing permissions and limitations |
20 | | // under the License. |
21 | | // |
22 | | |
23 | | #include <map> |
24 | | #include <string> |
25 | | |
26 | | #include <gtest/gtest.h> |
27 | | |
28 | | #include "yb/rocksdb/db.h" |
29 | | #include "yb/rocksdb/env.h" |
30 | | #include "yb/rocksdb/util/hash.h" |
31 | | #include "yb/rocksdb/util/kv_map.h" |
32 | | #include "yb/rocksdb/util/testharness.h" |
33 | | #include "yb/rocksdb/util/testutil.h" |
34 | | |
35 | | #include "yb/util/string_util.h" |
36 | | #include "yb/util/test_macros.h" |
37 | | |
38 | | using std::unique_ptr; |
39 | | |
40 | | namespace rocksdb { |
41 | | namespace { |
42 | | |
43 | | static const Comparator* comparator; |
44 | | |
45 | | class KVIter : public Iterator { |
46 | | public: |
47 | | explicit KVIter(const stl_wrappers::KVMap* map) |
48 | 80 | : map_(map), iter_(map_->end()) {} |
49 | 65.6k | bool Valid() const override { return iter_ != map_->end(); } |
50 | 11.3k | void SeekToFirst() override { iter_ = map_->begin(); } |
51 | 11.3k | void SeekToLast() override { |
52 | 11.3k | if (map_->empty()) { |
53 | 0 | iter_ = map_->end(); |
54 | 11.3k | } else { |
55 | 11.3k | iter_ = map_->find(map_->rbegin()->first); |
56 | 11.3k | } |
57 | 11.3k | } |
58 | 11.7k | void Seek(const Slice& k) override { |
59 | 11.7k | iter_ = map_->lower_bound(k.ToString()); |
60 | 11.7k | } |
61 | 9.88k | void Next() override { ++iter_; } |
62 | 9.65k | void Prev() override { |
63 | 9.65k | if (iter_ == map_->begin()) { |
64 | 2.43k | iter_ = map_->end(); |
65 | 2.43k | return; |
66 | 2.43k | } |
67 | 7.21k | --iter_; |
68 | 7.21k | } |
69 | | |
70 | 58.3k | Slice key() const override { return iter_->first; } |
71 | 58.3k | Slice value() const override { return iter_->second; } |
72 | 0 | Status status() const override { return Status::OK(); } |
73 | | |
74 | | private: |
75 | | const stl_wrappers::KVMap* const map_; |
76 | | stl_wrappers::KVMap::const_iterator iter_; |
77 | | }; |
78 | | |
79 | 65.6k | void AssertItersEqual(Iterator* iter1, Iterator* iter2) { |
80 | 65.6k | ASSERT_EQ(iter1->Valid(), iter2->Valid()); |
81 | 65.6k | if (iter1->Valid()) { |
82 | 58.3k | auto key1 = iter1->key().ToBuffer(); |
83 | 58.3k | auto key2 = iter2->key().ToBuffer(); |
84 | 58.3k | auto value1 = iter1->value().ToBuffer(); |
85 | 58.3k | auto value2 = iter2->value().ToBuffer(); |
86 | 58.3k | ASSERT_EQ(key1, key2); |
87 | 58.3k | ASSERT_EQ(value1, value2); |
88 | 58.3k | } |
89 | 65.6k | } |
90 | | |
91 | | // Measuring operations on DB (expect to be empty). |
92 | | // source_strings are candidate keys |
93 | | void DoRandomIteraratorTest(DB* db, std::vector<std::string> source_strings, |
94 | | Random* rnd, int num_writes, int num_iter_ops, |
95 | 80 | int num_trigger_flush) { |
96 | 80 | stl_wrappers::KVMap map((stl_wrappers::LessOfComparator(comparator))); |
97 | | |
98 | 12.5k | for (int i = 0; i < num_writes; i++) { |
99 | 12.4k | if (num_trigger_flush > 0 && i != 0 && i % num_trigger_flush == 0) { |
100 | 190 | ASSERT_OK(db->Flush(FlushOptions())); |
101 | 190 | } |
102 | | |
103 | 12.4k | int type = rnd->Uniform(2); |
104 | 12.4k | int index = rnd->Uniform(static_cast<int>(source_strings.size())); |
105 | 12.4k | auto& key = source_strings[index]; |
106 | 12.4k | switch (type) { |
107 | 6.22k | case 0: |
108 | | // put |
109 | 6.22k | map[key] = key; |
110 | 6.22k | ASSERT_OK(db->Put(WriteOptions(), key, key)); |
111 | 6.22k | break; |
112 | 6.27k | case 1: |
113 | | // delete |
114 | 6.27k | if (map.find(key) != map.end()) { |
115 | 1.77k | map.erase(key); |
116 | 1.77k | } |
117 | 6.27k | ASSERT_OK(db->Delete(WriteOptions(), key)); |
118 | 6.27k | break; |
119 | 0 | default: |
120 | 0 | assert(false); |
121 | 12.4k | } |
122 | 12.4k | } |
123 | | |
124 | 80 | std::unique_ptr<Iterator> iter(db->NewIterator(ReadOptions())); |
125 | 80 | std::unique_ptr<Iterator> result_iter(new KVIter(&map)); |
126 | | |
127 | 80 | bool is_valid = false; |
128 | 69.5k | for (int i = 0; i < num_iter_ops; i++) { |
129 | | // Random walk and make sure iter and result_iter returns the |
130 | | // same key and value |
131 | 69.5k | int type = rnd->Uniform(6); |
132 | 69.5k | ASSERT_OK(iter->status()); |
133 | 69.5k | switch (type) { |
134 | 11.3k | case 0: |
135 | | // Seek to First |
136 | 11.3k | iter->SeekToFirst(); |
137 | 11.3k | result_iter->SeekToFirst(); |
138 | 11.3k | break; |
139 | 11.3k | case 1: |
140 | | // Seek to last |
141 | 11.3k | iter->SeekToLast(); |
142 | 11.3k | result_iter->SeekToLast(); |
143 | 11.3k | break; |
144 | 11.7k | case 2: { |
145 | | // Seek to random key |
146 | 11.7k | auto key_idx = rnd->Uniform(static_cast<int>(source_strings.size())); |
147 | 11.7k | auto key = source_strings[key_idx]; |
148 | 11.7k | iter->Seek(key); |
149 | 11.7k | result_iter->Seek(key); |
150 | 11.7k | break; |
151 | 0 | } |
152 | 11.8k | case 3: |
153 | | // Next |
154 | 11.8k | if (is_valid) { |
155 | 9.88k | iter->Next(); |
156 | 9.88k | result_iter->Next(); |
157 | 1.93k | } else { |
158 | 1.93k | continue; |
159 | 1.93k | } |
160 | 9.88k | break; |
161 | 11.5k | case 4: |
162 | | // Prev |
163 | 11.5k | if (is_valid) { |
164 | 9.65k | iter->Prev(); |
165 | 9.65k | result_iter->Prev(); |
166 | 1.87k | } else { |
167 | 1.87k | continue; |
168 | 1.87k | } |
169 | 9.65k | break; |
170 | 11.7k | default: { |
171 | 11.7k | assert(type == 5); |
172 | 11.7k | auto key_idx = rnd->Uniform(static_cast<int>(source_strings.size())); |
173 | 11.7k | auto key = source_strings[key_idx]; |
174 | 11.7k | std::string result; |
175 | 11.7k | auto status = db->Get(ReadOptions(), key, &result); |
176 | 11.7k | if (map.find(key) == map.end()) { |
177 | 6.82k | ASSERT_TRUE(status.IsNotFound()); |
178 | 4.88k | } else { |
179 | 4.88k | ASSERT_EQ(map[key], result); |
180 | 4.88k | } |
181 | 11.7k | break; |
182 | 65.6k | } |
183 | 65.6k | } |
184 | 65.6k | AssertItersEqual(iter.get(), result_iter.get()); |
185 | 65.6k | is_valid = iter->Valid(); |
186 | 65.6k | } |
187 | 80 | } |
188 | | |
189 | | class DoubleComparator : public Comparator { |
190 | | public: |
191 | 1 | DoubleComparator() {} |
192 | | |
193 | 76 | const char* Name() const override { return "DoubleComparator"; } |
194 | | |
195 | 568k | int Compare(const Slice& a, const Slice& b) const override { |
196 | 568k | #ifndef CYGWIN |
197 | 568k | double da = std::stod(a.ToString()); |
198 | 568k | double db = std::stod(b.ToString()); |
199 | | #else |
200 | | double da = std::strtod(a.ToString().c_str(), 0 /* endptr */); |
201 | | double db = std::strtod(a.ToString().c_str(), 0 /* endptr */); |
202 | | #endif |
203 | 568k | if (da == db) { |
204 | 104k | return a.compare(b); |
205 | 464k | } else if (da > db) { |
206 | 143k | return 1; |
207 | 320k | } else { |
208 | 320k | return -1; |
209 | 320k | } |
210 | 568k | } |
211 | | virtual void FindShortestSeparator(std::string* start, |
212 | 0 | const Slice& limit) const override {} |
213 | | |
214 | 45 | void FindShortSuccessor(std::string* key) const override {} |
215 | | }; |
216 | | |
217 | | class HashComparator : public Comparator { |
218 | | public: |
219 | 1 | HashComparator() {} |
220 | | |
221 | 76 | const char* Name() const override { return "HashComparator"; } |
222 | | |
223 | 569k | int Compare(const Slice& a, const Slice& b) const override { |
224 | 569k | uint32_t ha = Hash(a.data(), a.size(), 66); |
225 | 569k | uint32_t hb = Hash(b.data(), b.size(), 66); |
226 | 569k | if (ha == hb) { |
227 | 105k | return a.compare(b); |
228 | 464k | } else if (ha > hb) { |
229 | 141k | return 1; |
230 | 322k | } else { |
231 | 322k | return -1; |
232 | 322k | } |
233 | 569k | } |
234 | | virtual void FindShortestSeparator(std::string* start, |
235 | 0 | const Slice& limit) const override {} |
236 | | |
237 | 45 | void FindShortSuccessor(std::string* key) const override {} |
238 | | }; |
239 | | |
240 | | class TwoStrComparator : public Comparator { |
241 | | public: |
242 | 1 | TwoStrComparator() {} |
243 | | |
244 | 76 | const char* Name() const override { return "TwoStrComparator"; } |
245 | | |
246 | 544k | int Compare(const Slice& a, const Slice& b) const override { |
247 | 544k | assert(a.size() >= 2); |
248 | 544k | assert(b.size() >= 2); |
249 | 544k | size_t size_a1 = static_cast<size_t>(a[0]); |
250 | 544k | size_t size_b1 = static_cast<size_t>(b[0]); |
251 | 544k | size_t size_a2 = static_cast<size_t>(a[1]); |
252 | 544k | size_t size_b2 = static_cast<size_t>(b[1]); |
253 | 544k | assert(size_a1 + size_a2 + 2 == a.size()); |
254 | 544k | assert(size_b1 + size_b2 + 2 == b.size()); |
255 | | |
256 | 544k | Slice a1 = Slice(a.data() + 2, size_a1); |
257 | 544k | Slice b1 = Slice(b.data() + 2, size_b1); |
258 | 544k | Slice a2 = Slice(a.data() + 2 + size_a1, size_a2); |
259 | 544k | Slice b2 = Slice(b.data() + 2 + size_b1, size_b2); |
260 | | |
261 | 544k | if (a1 != b1) { |
262 | 403k | return a1.compare(b1); |
263 | 403k | } |
264 | 140k | return a2.compare(b2); |
265 | 140k | } |
266 | | virtual void FindShortestSeparator(std::string* start, |
267 | 0 | const Slice& limit) const override {} |
268 | | |
269 | 45 | void FindShortSuccessor(std::string* key) const override {} |
270 | | }; |
271 | | } // namespace |
272 | | |
273 | | class ComparatorDBTest : public RocksDBTest { |
274 | | private: |
275 | | std::string dbname_; |
276 | | Env* env_; |
277 | | DB* db_; |
278 | | Options last_options_; |
279 | | std::unique_ptr<const Comparator> comparator_guard; |
280 | | |
281 | | public: |
282 | 6 | ComparatorDBTest() : env_(Env::Default()), db_(nullptr) { |
283 | 6 | comparator = BytewiseComparator(); |
284 | 6 | dbname_ = test::TmpDir() + "/comparator_db_test"; |
285 | 6 | EXPECT_OK(DestroyDB(dbname_, last_options_)); |
286 | 6 | } |
287 | | |
288 | 6 | ~ComparatorDBTest() { |
289 | 6 | delete db_; |
290 | 6 | EXPECT_OK(DestroyDB(dbname_, last_options_)); |
291 | 6 | comparator = BytewiseComparator(); |
292 | 6 | } |
293 | | |
294 | 80 | DB* GetDB() { return db_; } |
295 | | |
296 | 4 | void SetOwnedComparator(const Comparator* cmp) { |
297 | 4 | comparator_guard.reset(cmp); |
298 | 4 | SetComparator(cmp); |
299 | 4 | } |
300 | | |
301 | 5 | void SetComparator(const Comparator* cmp) { |
302 | 5 | comparator = cmp; |
303 | 5 | last_options_.comparator = cmp; |
304 | 5 | } |
305 | | |
306 | | // Return the current option configuration. |
307 | 75 | Options* GetOptions() { return &last_options_; } |
308 | | |
309 | 80 | void DestroyAndReopen() { |
310 | | // Destroy using last options |
311 | 80 | Destroy(); |
312 | 80 | ASSERT_OK(TryReopen()); |
313 | 80 | } |
314 | | |
315 | 80 | void Destroy() { |
316 | 80 | delete db_; |
317 | 80 | db_ = nullptr; |
318 | 80 | ASSERT_OK(DestroyDB(dbname_, last_options_)); |
319 | 80 | } |
320 | | |
321 | 80 | Status TryReopen() { |
322 | 80 | delete db_; |
323 | 80 | db_ = nullptr; |
324 | 80 | last_options_.create_if_missing = true; |
325 | | |
326 | 80 | return DB::Open(last_options_, dbname_, &db_); |
327 | 80 | } |
328 | | }; |
329 | | |
330 | 1 | TEST_F(ComparatorDBTest, Bytewise) { |
331 | 6 | for (int rand_seed = 301; rand_seed < 306; rand_seed++) { |
332 | 5 | DestroyAndReopen(); |
333 | 5 | Random rnd(rand_seed); |
334 | 5 | DoRandomIteraratorTest(GetDB(), |
335 | 5 | {"a", "b", "c", "d", "e", "f", "g", "h", "i"}, &rnd, |
336 | 5 | 8, 100, 3); |
337 | 5 | } |
338 | 1 | } |
339 | | |
340 | 1 | TEST_F(ComparatorDBTest, SimpleSuffixReverseComparator) { |
341 | 1 | SetOwnedComparator(new test::SimpleSuffixReverseComparator()); |
342 | | |
343 | 16 | for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) { |
344 | 15 | Options* opt = GetOptions(); |
345 | 15 | opt->comparator = comparator; |
346 | 15 | DestroyAndReopen(); |
347 | 15 | Random rnd(rnd_seed); |
348 | | |
349 | 15 | std::vector<std::string> source_strings; |
350 | 15 | std::vector<std::string> source_prefixes; |
351 | | // Randomly generate 5 prefixes |
352 | 90 | for (int i = 0; i < 5; i++) { |
353 | 75 | source_prefixes.push_back(test::RandomHumanReadableString(&rnd, 8)); |
354 | 75 | } |
355 | 315 | for (int j = 0; j < 20; j++) { |
356 | 300 | int prefix_index = rnd.Uniform(static_cast<int>(source_prefixes.size())); |
357 | 300 | std::string key = source_prefixes[prefix_index] + |
358 | 300 | test::RandomHumanReadableString(&rnd, rnd.Uniform(8)); |
359 | 300 | source_strings.push_back(key); |
360 | 300 | } |
361 | | |
362 | 15 | DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 30, 600, 66); |
363 | 15 | } |
364 | 1 | } |
365 | | |
366 | 1 | TEST_F(ComparatorDBTest, Uint64Comparator) { |
367 | 1 | SetComparator(Uint64Comparator()); |
368 | | |
369 | 16 | for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) { |
370 | 15 | Options* opt = GetOptions(); |
371 | 15 | opt->comparator = comparator; |
372 | 15 | DestroyAndReopen(); |
373 | 15 | Random rnd(rnd_seed); |
374 | 15 | Random64 rnd64(rnd_seed); |
375 | | |
376 | 15 | std::vector<std::string> source_strings; |
377 | | // Randomly generate source keys |
378 | 1.51k | for (int i = 0; i < 100; i++) { |
379 | 1.50k | uint64_t r = rnd64.Next(); |
380 | 1.50k | std::string str; |
381 | 1.50k | str.resize(8); |
382 | 1.50k | memcpy(&str[0], static_cast<void*>(&r), 8); |
383 | 1.50k | source_strings.push_back(str); |
384 | 1.50k | } |
385 | | |
386 | 15 | DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66); |
387 | 15 | } |
388 | 1 | } |
389 | | |
390 | 1 | TEST_F(ComparatorDBTest, DoubleComparator) { |
391 | 1 | SetOwnedComparator(new DoubleComparator()); |
392 | | |
393 | 16 | for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) { |
394 | 15 | Options* opt = GetOptions(); |
395 | 15 | opt->comparator = comparator; |
396 | 15 | DestroyAndReopen(); |
397 | 15 | Random rnd(rnd_seed); |
398 | | |
399 | 15 | std::vector<std::string> source_strings; |
400 | | // Randomly generate source keys |
401 | 1.51k | for (int i = 0; i < 100; i++) { |
402 | 1.50k | uint32_t r = rnd.Next(); |
403 | 1.50k | uint32_t divide_order = rnd.Uniform(8); |
404 | 1.50k | double to_divide = 1.0; |
405 | 6.78k | for (uint32_t j = 0; j < divide_order; j++) { |
406 | 5.28k | to_divide *= 10.0; |
407 | 5.28k | } |
408 | 1.50k | source_strings.push_back(ToString(r / to_divide)); |
409 | 1.50k | } |
410 | | |
411 | 15 | DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66); |
412 | 15 | } |
413 | 1 | } |
414 | | |
415 | 1 | TEST_F(ComparatorDBTest, HashComparator) { |
416 | 1 | SetOwnedComparator(new HashComparator()); |
417 | | |
418 | 16 | for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) { |
419 | 15 | Options* opt = GetOptions(); |
420 | 15 | opt->comparator = comparator; |
421 | 15 | DestroyAndReopen(); |
422 | 15 | Random rnd(rnd_seed); |
423 | | |
424 | 15 | std::vector<std::string> source_strings; |
425 | | // Randomly generate source keys |
426 | 1.51k | for (int i = 0; i < 100; i++) { |
427 | 1.50k | source_strings.push_back(test::RandomKey(&rnd, 8)); |
428 | 1.50k | } |
429 | | |
430 | 15 | DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66); |
431 | 15 | } |
432 | 1 | } |
433 | | |
434 | 1 | TEST_F(ComparatorDBTest, TwoStrComparator) { |
435 | 1 | SetOwnedComparator(new TwoStrComparator()); |
436 | | |
437 | 16 | for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) { |
438 | 15 | Options* opt = GetOptions(); |
439 | 15 | opt->comparator = comparator; |
440 | 15 | DestroyAndReopen(); |
441 | 15 | Random rnd(rnd_seed); |
442 | | |
443 | 15 | std::vector<std::string> source_strings; |
444 | | // Randomly generate source keys |
445 | 1.51k | for (int i = 0; i < 100; i++) { |
446 | 1.50k | std::string str; |
447 | 1.50k | uint32_t size1 = rnd.Uniform(8); |
448 | 1.50k | uint32_t size2 = rnd.Uniform(8); |
449 | 1.50k | str.append(1, static_cast<char>(size1)); |
450 | 1.50k | str.append(1, static_cast<char>(size2)); |
451 | 1.50k | str.append(test::RandomKey(&rnd, size1)); |
452 | 1.50k | str.append(test::RandomKey(&rnd, size2)); |
453 | 1.50k | source_strings.push_back(str); |
454 | 1.50k | } |
455 | | |
456 | 15 | DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66); |
457 | 15 | } |
458 | 1 | } |
459 | | |
460 | | } // namespace rocksdb |
461 | | |
462 | 13.2k | int main(int argc, char** argv) { |
463 | 13.2k | ::testing::InitGoogleTest(&argc, argv); |
464 | 13.2k | return RUN_ALL_TESTS(); |
465 | 13.2k | } |