1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
11 fprintf(stderr
, "Please install gflags to run this test... Skipping...\n");
20 #include "db/db_impl/db_impl.h"
21 #include "monitoring/histogram.h"
22 #include "rocksdb/comparator.h"
23 #include "rocksdb/db.h"
24 #include "rocksdb/filter_policy.h"
25 #include "rocksdb/memtablerep.h"
26 #include "rocksdb/perf_context.h"
27 #include "rocksdb/slice_transform.h"
28 #include "rocksdb/table.h"
29 #include "test_util/testharness.h"
30 #include "util/cast_util.h"
31 #include "util/coding.h"
32 #include "util/gflags_compat.h"
33 #include "util/random.h"
34 #include "util/stop_watch.h"
35 #include "util/string_util.h"
36 #include "utilities/merge_operators.h"
38 using GFLAGS_NAMESPACE::ParseCommandLineFlags
;
40 DEFINE_bool(trigger_deadlock
, false,
41 "issue delete in range scan to trigger PrefixHashMap deadlock");
42 DEFINE_int32(bucket_count
, 100000, "number of buckets");
43 DEFINE_uint64(num_locks
, 10001, "number of locks");
44 DEFINE_bool(random_prefix
, false, "randomize prefix");
45 DEFINE_uint64(total_prefixes
, 100000, "total number of prefixes");
46 DEFINE_uint64(items_per_prefix
, 1, "total number of values per prefix");
47 DEFINE_int64(write_buffer_size
, 33554432, "");
48 DEFINE_int32(max_write_buffer_number
, 2, "");
49 DEFINE_int32(min_write_buffer_number_to_merge
, 1, "");
50 DEFINE_int32(skiplist_height
, 4, "");
51 DEFINE_double(memtable_prefix_bloom_size_ratio
, 0.1, "");
52 DEFINE_int32(memtable_huge_page_size
, 2 * 1024 * 1024, "");
53 DEFINE_int32(value_size
, 40, "");
54 DEFINE_bool(enable_print
, false, "Print options generated to console.");
56 // Path to the database on file system
57 const std::string kDbName
=
58 ROCKSDB_NAMESPACE::test::PerThreadDBPath("prefix_test");
60 namespace ROCKSDB_NAMESPACE
{
66 TestKey(uint64_t _prefix
, uint64_t _sorted
)
67 : prefix(_prefix
), sorted(_sorted
) {}
70 // return a slice backed by test_key
71 inline Slice
TestKeyToSlice(std::string
&s
, const TestKey
& test_key
) {
73 PutFixed64(&s
, test_key
.prefix
);
74 PutFixed64(&s
, test_key
.sorted
);
75 return Slice(s
.c_str(), s
.size());
78 inline const TestKey
SliceToTestKey(const Slice
& slice
) {
79 return TestKey(DecodeFixed64(slice
.data()),
80 DecodeFixed64(slice
.data() + 8));
83 class TestKeyComparator
: public Comparator
{
86 // Compare needs to be aware of the possibility of a and/or b is
88 int Compare(const Slice
& a
, const Slice
& b
) const override
{
89 const TestKey kkey_a
= SliceToTestKey(a
);
90 const TestKey kkey_b
= SliceToTestKey(b
);
91 const TestKey
*key_a
= &kkey_a
;
92 const TestKey
*key_b
= &kkey_b
;
93 if (key_a
->prefix
!= key_b
->prefix
) {
94 if (key_a
->prefix
< key_b
->prefix
) return -1;
95 if (key_a
->prefix
> key_b
->prefix
) return 1;
97 EXPECT_TRUE(key_a
->prefix
== key_b
->prefix
);
98 // note, both a and b could be prefix only
99 if (a
.size() != b
.size()) {
100 // one of them is prefix
102 (a
.size() == sizeof(uint64_t) && b
.size() == sizeof(TestKey
)) ||
103 (b
.size() == sizeof(uint64_t) && a
.size() == sizeof(TestKey
)));
104 if (a
.size() < b
.size()) return -1;
105 if (a
.size() > b
.size()) return 1;
107 // both a and b are prefix
108 if (a
.size() == sizeof(uint64_t)) {
112 // both a and b are whole key
113 EXPECT_TRUE(a
.size() == sizeof(TestKey
) && b
.size() == sizeof(TestKey
));
114 if (key_a
->sorted
< key_b
->sorted
) return -1;
115 if (key_a
->sorted
> key_b
->sorted
) return 1;
116 if (key_a
->sorted
== key_b
->sorted
) return 0;
122 bool operator()(const TestKey
& a
, const TestKey
& b
) const {
124 return Compare(TestKeyToSlice(sa
, a
), TestKeyToSlice(sb
, b
)) < 0;
127 const char* Name() const override
{ return "TestKeyComparator"; }
129 void FindShortestSeparator(std::string
* /*start*/,
130 const Slice
& /*limit*/) const override
{}
132 void FindShortSuccessor(std::string
* /*key*/) const override
{}
136 void PutKey(DB
* db
, WriteOptions write_options
, uint64_t prefix
,
137 uint64_t suffix
, const Slice
& value
) {
138 TestKey
test_key(prefix
, suffix
);
140 Slice key
= TestKeyToSlice(s
, test_key
);
141 ASSERT_OK(db
->Put(write_options
, key
, value
));
144 void PutKey(DB
* db
, WriteOptions write_options
, const TestKey
& test_key
,
145 const Slice
& value
) {
147 Slice key
= TestKeyToSlice(s
, test_key
);
148 ASSERT_OK(db
->Put(write_options
, key
, value
));
151 void MergeKey(DB
* db
, WriteOptions write_options
, const TestKey
& test_key
,
152 const Slice
& value
) {
154 Slice key
= TestKeyToSlice(s
, test_key
);
155 ASSERT_OK(db
->Merge(write_options
, key
, value
));
158 void DeleteKey(DB
* db
, WriteOptions write_options
, const TestKey
& test_key
) {
160 Slice key
= TestKeyToSlice(s
, test_key
);
161 ASSERT_OK(db
->Delete(write_options
, key
));
164 void SeekIterator(Iterator
* iter
, uint64_t prefix
, uint64_t suffix
) {
165 TestKey
test_key(prefix
, suffix
);
167 Slice key
= TestKeyToSlice(s
, test_key
);
171 const std::string kNotFoundResult
= "NOT_FOUND";
173 std::string
Get(DB
* db
, const ReadOptions
& read_options
, uint64_t prefix
,
175 TestKey
test_key(prefix
, suffix
);
177 Slice key
= TestKeyToSlice(s2
, test_key
);
180 Status s
= db
->Get(read_options
, key
, &result
);
181 if (s
.IsNotFound()) {
182 result
= kNotFoundResult
;
183 } else if (!s
.ok()) {
184 result
= s
.ToString();
189 class SamePrefixTransform
: public SliceTransform
{
195 explicit SamePrefixTransform(const Slice
& prefix
)
196 : prefix_(prefix
), name_("rocksdb.SamePrefix." + prefix
.ToString()) {}
198 const char* Name() const override
{ return name_
.c_str(); }
200 Slice
Transform(const Slice
& src
) const override
{
201 assert(InDomain(src
));
205 bool InDomain(const Slice
& src
) const override
{
206 if (src
.size() >= prefix_
.size()) {
207 return Slice(src
.data(), prefix_
.size()) == prefix_
;
212 bool InRange(const Slice
& dst
) const override
{ return dst
== prefix_
; }
214 bool FullLengthEnabled(size_t* /*len*/) const override
{ return false; }
219 class PrefixTest
: public testing::Test
{
221 std::shared_ptr
<DB
> OpenDb() {
224 options
.create_if_missing
= true;
225 options
.write_buffer_size
= FLAGS_write_buffer_size
;
226 options
.max_write_buffer_number
= FLAGS_max_write_buffer_number
;
227 options
.min_write_buffer_number_to_merge
=
228 FLAGS_min_write_buffer_number_to_merge
;
230 options
.memtable_prefix_bloom_size_ratio
=
231 FLAGS_memtable_prefix_bloom_size_ratio
;
232 options
.memtable_huge_page_size
= FLAGS_memtable_huge_page_size
;
234 options
.prefix_extractor
.reset(NewFixedPrefixTransform(8));
235 BlockBasedTableOptions bbto
;
236 bbto
.filter_policy
.reset(NewBloomFilterPolicy(10, false));
237 bbto
.whole_key_filtering
= false;
238 options
.table_factory
.reset(NewBlockBasedTableFactory(bbto
));
239 options
.allow_concurrent_memtable_write
= false;
241 Status s
= DB::Open(options
, kDbName
, &db
);
243 return std::shared_ptr
<DB
>(db
);
247 option_config_
= kBegin
;
250 bool NextOptions(int bucket_count
) {
253 if (option_config_
< kEnd
) {
254 options
.prefix_extractor
.reset(NewFixedPrefixTransform(8));
255 switch(option_config_
) {
257 options
.memtable_factory
.reset(
258 NewHashSkipListRepFactory(bucket_count
, FLAGS_skiplist_height
));
261 options
.memtable_factory
.reset(
262 NewHashLinkListRepFactory(bucket_count
));
264 case kHashLinkListHugePageTlb
:
265 options
.memtable_factory
.reset(
266 NewHashLinkListRepFactory(bucket_count
, 2 * 1024 * 1024));
268 case kHashLinkListTriggerSkipList
:
269 options
.memtable_factory
.reset(
270 NewHashLinkListRepFactory(bucket_count
, 0, 3));
279 PrefixTest() : option_config_(kBegin
) {
280 options
.comparator
= new TestKeyComparator();
282 ~PrefixTest() override
{ delete options
.comparator
; }
289 kHashLinkListHugePageTlb
,
290 kHashLinkListTriggerSkipList
,
297 TEST(SamePrefixTest
, InDomainTest
) {
300 options
.create_if_missing
= true;
301 options
.prefix_extractor
.reset(new SamePrefixTransform("HHKB"));
302 BlockBasedTableOptions bbto
;
303 bbto
.filter_policy
.reset(NewBloomFilterPolicy(10, false));
304 bbto
.whole_key_filtering
= false;
305 options
.table_factory
.reset(NewBlockBasedTableFactory(bbto
));
306 WriteOptions write_options
;
307 ReadOptions read_options
;
309 ASSERT_OK(DestroyDB(kDbName
, Options()));
310 ASSERT_OK(DB::Open(options
, kDbName
, &db
));
311 ASSERT_OK(db
->Put(write_options
, "HHKB pro2", "Mar 24, 2006"));
312 ASSERT_OK(db
->Put(write_options
, "HHKB pro2 Type-S", "June 29, 2011"));
313 ASSERT_OK(db
->Put(write_options
, "Realforce 87u", "idk"));
314 db
->Flush(FlushOptions());
316 auto db_iter
= db
->NewIterator(ReadOptions());
318 db_iter
->Seek("Realforce 87u");
319 ASSERT_TRUE(db_iter
->Valid());
320 ASSERT_OK(db_iter
->status());
321 ASSERT_EQ(db_iter
->key(), "Realforce 87u");
322 ASSERT_EQ(db_iter
->value(), "idk");
326 ASSERT_OK(DestroyDB(kDbName
, Options()));
330 ASSERT_OK(DB::Open(options
, kDbName
, &db
));
331 ASSERT_OK(db
->Put(write_options
, "pikachu", "1"));
332 ASSERT_OK(db
->Put(write_options
, "Meowth", "1"));
333 ASSERT_OK(db
->Put(write_options
, "Mewtwo", "idk"));
334 db
->Flush(FlushOptions());
336 auto db_iter
= db
->NewIterator(ReadOptions());
338 db_iter
->Seek("Mewtwo");
339 ASSERT_TRUE(db_iter
->Valid());
340 ASSERT_OK(db_iter
->status());
343 ASSERT_OK(DestroyDB(kDbName
, Options()));
347 TEST_F(PrefixTest
, TestResult
) {
348 for (int num_buckets
= 1; num_buckets
<= 2; num_buckets
++) {
350 while (NextOptions(num_buckets
)) {
351 std::cout
<< "*** Mem table: " << options
.memtable_factory
->Name()
352 << " number of buckets: " << num_buckets
354 DestroyDB(kDbName
, Options());
356 WriteOptions write_options
;
357 ReadOptions read_options
;
359 // 1. Insert one row.
361 PutKey(db
.get(), write_options
, 1, 6, v16
);
362 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
363 SeekIterator(iter
.get(), 1, 6);
364 ASSERT_TRUE(iter
->Valid());
365 ASSERT_TRUE(v16
== iter
->value());
366 SeekIterator(iter
.get(), 1, 5);
367 ASSERT_TRUE(iter
->Valid());
368 ASSERT_TRUE(v16
== iter
->value());
369 SeekIterator(iter
.get(), 1, 5);
370 ASSERT_TRUE(iter
->Valid());
371 ASSERT_TRUE(v16
== iter
->value());
373 ASSERT_TRUE(!iter
->Valid());
374 ASSERT_OK(iter
->status());
376 SeekIterator(iter
.get(), 2, 0);
377 ASSERT_TRUE(!iter
->Valid());
378 ASSERT_OK(iter
->status());
380 ASSERT_EQ(v16
.ToString(), Get(db
.get(), read_options
, 1, 6));
381 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 1, 5));
382 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 1, 7));
383 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 0, 6));
384 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 2, 6));
386 // 2. Insert an entry for the same prefix as the last entry in the bucket.
388 PutKey(db
.get(), write_options
, 1, 7, v17
);
389 iter
.reset(db
->NewIterator(read_options
));
390 SeekIterator(iter
.get(), 1, 7);
391 ASSERT_TRUE(iter
->Valid());
392 ASSERT_TRUE(v17
== iter
->value());
394 SeekIterator(iter
.get(), 1, 6);
395 ASSERT_TRUE(iter
->Valid());
396 ASSERT_TRUE(v16
== iter
->value());
398 ASSERT_TRUE(iter
->Valid());
399 ASSERT_TRUE(v17
== iter
->value());
401 ASSERT_TRUE(!iter
->Valid());
402 ASSERT_OK(iter
->status());
404 SeekIterator(iter
.get(), 2, 0);
405 ASSERT_TRUE(!iter
->Valid());
406 ASSERT_OK(iter
->status());
408 // 3. Insert an entry for the same prefix as the head of the bucket.
410 PutKey(db
.get(), write_options
, 1, 5, v15
);
411 iter
.reset(db
->NewIterator(read_options
));
413 SeekIterator(iter
.get(), 1, 7);
414 ASSERT_TRUE(iter
->Valid());
415 ASSERT_TRUE(v17
== iter
->value());
417 SeekIterator(iter
.get(), 1, 5);
418 ASSERT_TRUE(iter
->Valid());
419 ASSERT_TRUE(v15
== iter
->value());
421 ASSERT_TRUE(iter
->Valid());
422 ASSERT_TRUE(v16
== iter
->value());
424 ASSERT_TRUE(iter
->Valid());
425 ASSERT_TRUE(v17
== iter
->value());
427 SeekIterator(iter
.get(), 1, 5);
428 ASSERT_TRUE(iter
->Valid());
429 ASSERT_TRUE(v15
== iter
->value());
431 ASSERT_EQ(v15
.ToString(), Get(db
.get(), read_options
, 1, 5));
432 ASSERT_EQ(v16
.ToString(), Get(db
.get(), read_options
, 1, 6));
433 ASSERT_EQ(v17
.ToString(), Get(db
.get(), read_options
, 1, 7));
435 // 4. Insert an entry with a larger prefix
437 PutKey(db
.get(), write_options
, 2, 2, v22
);
438 iter
.reset(db
->NewIterator(read_options
));
440 SeekIterator(iter
.get(), 2, 2);
441 ASSERT_TRUE(iter
->Valid());
442 ASSERT_TRUE(v22
== iter
->value());
443 SeekIterator(iter
.get(), 2, 0);
444 ASSERT_TRUE(iter
->Valid());
445 ASSERT_TRUE(v22
== iter
->value());
447 SeekIterator(iter
.get(), 1, 5);
448 ASSERT_TRUE(iter
->Valid());
449 ASSERT_TRUE(v15
== iter
->value());
451 SeekIterator(iter
.get(), 1, 7);
452 ASSERT_TRUE(iter
->Valid());
453 ASSERT_TRUE(v17
== iter
->value());
455 // 5. Insert an entry with a smaller prefix
457 PutKey(db
.get(), write_options
, 0, 2, v02
);
458 iter
.reset(db
->NewIterator(read_options
));
460 SeekIterator(iter
.get(), 0, 2);
461 ASSERT_TRUE(iter
->Valid());
462 ASSERT_TRUE(v02
== iter
->value());
463 SeekIterator(iter
.get(), 0, 0);
464 ASSERT_TRUE(iter
->Valid());
465 ASSERT_TRUE(v02
== iter
->value());
467 SeekIterator(iter
.get(), 2, 0);
468 ASSERT_TRUE(iter
->Valid());
469 ASSERT_TRUE(v22
== iter
->value());
471 SeekIterator(iter
.get(), 1, 5);
472 ASSERT_TRUE(iter
->Valid());
473 ASSERT_TRUE(v15
== iter
->value());
475 SeekIterator(iter
.get(), 1, 7);
476 ASSERT_TRUE(iter
->Valid());
477 ASSERT_TRUE(v17
== iter
->value());
479 // 6. Insert to the beginning and the end of the first prefix
482 PutKey(db
.get(), write_options
, 1, 3, v13
);
483 PutKey(db
.get(), write_options
, 1, 8, v18
);
484 iter
.reset(db
->NewIterator(read_options
));
485 SeekIterator(iter
.get(), 1, 7);
486 ASSERT_TRUE(iter
->Valid());
487 ASSERT_TRUE(v17
== iter
->value());
489 SeekIterator(iter
.get(), 1, 3);
490 ASSERT_TRUE(iter
->Valid());
491 ASSERT_TRUE(v13
== iter
->value());
493 ASSERT_TRUE(iter
->Valid());
494 ASSERT_TRUE(v15
== iter
->value());
496 ASSERT_TRUE(iter
->Valid());
497 ASSERT_TRUE(v16
== iter
->value());
499 ASSERT_TRUE(iter
->Valid());
500 ASSERT_TRUE(v17
== iter
->value());
502 ASSERT_TRUE(iter
->Valid());
503 ASSERT_TRUE(v18
== iter
->value());
505 SeekIterator(iter
.get(), 0, 0);
506 ASSERT_TRUE(iter
->Valid());
507 ASSERT_TRUE(v02
== iter
->value());
509 SeekIterator(iter
.get(), 2, 0);
510 ASSERT_TRUE(iter
->Valid());
511 ASSERT_TRUE(v22
== iter
->value());
513 ASSERT_EQ(v22
.ToString(), Get(db
.get(), read_options
, 2, 2));
514 ASSERT_EQ(v02
.ToString(), Get(db
.get(), read_options
, 0, 2));
515 ASSERT_EQ(v13
.ToString(), Get(db
.get(), read_options
, 1, 3));
516 ASSERT_EQ(v15
.ToString(), Get(db
.get(), read_options
, 1, 5));
517 ASSERT_EQ(v16
.ToString(), Get(db
.get(), read_options
, 1, 6));
518 ASSERT_EQ(v17
.ToString(), Get(db
.get(), read_options
, 1, 7));
519 ASSERT_EQ(v18
.ToString(), Get(db
.get(), read_options
, 1, 8));
524 // Show results in prefix
525 TEST_F(PrefixTest
, PrefixValid
) {
526 for (int num_buckets
= 1; num_buckets
<= 2; num_buckets
++) {
528 while (NextOptions(num_buckets
)) {
529 std::cout
<< "*** Mem table: " << options
.memtable_factory
->Name()
530 << " number of buckets: " << num_buckets
<< std::endl
;
531 DestroyDB(kDbName
, Options());
533 WriteOptions write_options
;
534 ReadOptions read_options
;
536 // Insert keys with common prefix and one key with different
541 PutKey(db
.get(), write_options
, 12345, 6, v16
);
542 PutKey(db
.get(), write_options
, 12345, 7, v17
);
543 PutKey(db
.get(), write_options
, 12345, 8, v18
);
544 PutKey(db
.get(), write_options
, 12345, 9, v19
);
545 PutKey(db
.get(), write_options
, 12346, 8, v16
);
546 db
->Flush(FlushOptions());
547 TestKey
test_key(12346, 8);
549 ASSERT_OK(db
->Delete(write_options
, TestKeyToSlice(s
, test_key
)));
550 ASSERT_OK(db
->Flush(FlushOptions()));
551 read_options
.prefix_same_as_start
= true;
552 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
553 SeekIterator(iter
.get(), 12345, 6);
554 ASSERT_TRUE(iter
->Valid());
555 ASSERT_TRUE(v16
== iter
->value());
558 ASSERT_TRUE(iter
->Valid());
559 ASSERT_TRUE(v17
== iter
->value());
562 ASSERT_TRUE(iter
->Valid());
563 ASSERT_TRUE(v18
== iter
->value());
566 ASSERT_TRUE(iter
->Valid());
567 ASSERT_TRUE(v19
== iter
->value());
569 ASSERT_FALSE(iter
->Valid());
570 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 12346, 8));
572 // Verify seeking past the prefix won't return a result.
573 SeekIterator(iter
.get(), 12345, 10);
574 ASSERT_TRUE(!iter
->Valid());
575 ASSERT_OK(iter
->status());
580 TEST_F(PrefixTest
, DynamicPrefixIterator
) {
581 while (NextOptions(FLAGS_bucket_count
)) {
582 std::cout
<< "*** Mem table: " << options
.memtable_factory
->Name()
584 DestroyDB(kDbName
, Options());
586 WriteOptions write_options
;
587 ReadOptions read_options
;
589 std::vector
<uint64_t> prefixes
;
590 for (uint64_t i
= 0; i
< FLAGS_total_prefixes
; ++i
) {
591 prefixes
.push_back(i
);
594 if (FLAGS_random_prefix
) {
595 RandomShuffle(prefixes
.begin(), prefixes
.end());
598 HistogramImpl hist_put_time
;
599 HistogramImpl hist_put_comparison
;
601 // insert x random prefix, each with y continuous element.
602 for (auto prefix
: prefixes
) {
603 for (uint64_t sorted
= 0; sorted
< FLAGS_items_per_prefix
; sorted
++) {
604 TestKey
test_key(prefix
, sorted
);
607 Slice key
= TestKeyToSlice(s
, test_key
);
608 std::string
value(FLAGS_value_size
, 0);
610 get_perf_context()->Reset();
611 StopWatchNano
timer(Env::Default(), true);
612 ASSERT_OK(db
->Put(write_options
, key
, value
));
613 hist_put_time
.Add(timer
.ElapsedNanos());
614 hist_put_comparison
.Add(get_perf_context()->user_key_comparison_count
);
618 std::cout
<< "Put key comparison: \n" << hist_put_comparison
.ToString()
619 << "Put time: \n" << hist_put_time
.ToString();
621 // test seek existing keys
622 HistogramImpl hist_seek_time
;
623 HistogramImpl hist_seek_comparison
;
625 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
627 for (auto prefix
: prefixes
) {
628 TestKey
test_key(prefix
, FLAGS_items_per_prefix
/ 2);
630 Slice key
= TestKeyToSlice(s
, test_key
);
631 std::string value
= "v" + ToString(0);
633 get_perf_context()->Reset();
634 StopWatchNano
timer(Env::Default(), true);
635 auto key_prefix
= options
.prefix_extractor
->Transform(key
);
636 uint64_t total_keys
= 0;
637 for (iter
->Seek(key
);
638 iter
->Valid() && iter
->key().starts_with(key_prefix
);
640 if (FLAGS_trigger_deadlock
) {
641 std::cout
<< "Behold the deadlock!\n";
642 db
->Delete(write_options
, iter
->key());
646 hist_seek_time
.Add(timer
.ElapsedNanos());
647 hist_seek_comparison
.Add(get_perf_context()->user_key_comparison_count
);
648 ASSERT_EQ(total_keys
, FLAGS_items_per_prefix
- FLAGS_items_per_prefix
/2);
651 std::cout
<< "Seek key comparison: \n"
652 << hist_seek_comparison
.ToString()
654 << hist_seek_time
.ToString();
656 // test non-existing keys
657 HistogramImpl hist_no_seek_time
;
658 HistogramImpl hist_no_seek_comparison
;
660 for (auto prefix
= FLAGS_total_prefixes
;
661 prefix
< FLAGS_total_prefixes
+ 10000;
663 TestKey
test_key(prefix
, 0);
665 Slice key
= TestKeyToSlice(s
, test_key
);
667 get_perf_context()->Reset();
668 StopWatchNano
timer(Env::Default(), true);
670 hist_no_seek_time
.Add(timer
.ElapsedNanos());
671 hist_no_seek_comparison
.Add(get_perf_context()->user_key_comparison_count
);
672 ASSERT_TRUE(!iter
->Valid());
673 ASSERT_OK(iter
->status());
676 std::cout
<< "non-existing Seek key comparison: \n"
677 << hist_no_seek_comparison
.ToString()
678 << "non-existing Seek time: \n"
679 << hist_no_seek_time
.ToString();
683 TEST_F(PrefixTest
, PrefixSeekModePrev
) {
684 // Only for SkipListFactory
685 options
.memtable_factory
.reset(new SkipListFactory
);
686 options
.merge_operator
= MergeOperators::CreatePutOperator();
687 options
.write_buffer_size
= 1024 * 1024;
689 for (size_t m
= 1; m
< 100; m
++) {
690 std::cout
<< "[" + std::to_string(m
) + "]" + "*** Mem table: "
691 << options
.memtable_factory
->Name() << std::endl
;
692 DestroyDB(kDbName
, Options());
694 WriteOptions write_options
;
695 ReadOptions read_options
;
696 std::map
<TestKey
, std::string
, TestKeyComparator
> entry_maps
[3], whole_map
;
697 for (uint64_t i
= 0; i
< 10; i
++) {
699 for (uint64_t j
= 0; j
< 10; j
++) {
700 whole_map
[TestKey(i
, j
)] = entry_maps
[rnd
.Uniform(div
)][TestKey(i
, j
)] =
701 'v' + std::to_string(i
) + std::to_string(j
);
705 std::map
<TestKey
, std::string
, TestKeyComparator
> type_map
;
706 for (size_t i
= 0; i
< 3; i
++) {
707 for (auto& kv
: entry_maps
[i
]) {
709 PutKey(db
.get(), write_options
, kv
.first
, kv
.second
);
710 type_map
[kv
.first
] = "value";
712 MergeKey(db
.get(), write_options
, kv
.first
, kv
.second
);
713 type_map
[kv
.first
] = "merge";
717 db
->Flush(FlushOptions());
721 for (size_t i
= 0; i
< 2; i
++) {
722 for (auto& kv
: entry_maps
[i
]) {
724 whole_map
.erase(kv
.first
);
725 DeleteKey(db
.get(), write_options
, kv
.first
);
726 entry_maps
[2][kv
.first
] = "delete";
731 if (FLAGS_enable_print
) {
732 for (size_t i
= 0; i
< 3; i
++) {
733 for (auto& kv
: entry_maps
[i
]) {
734 std::cout
<< "[" << i
<< "]" << kv
.first
.prefix
<< kv
.first
.sorted
735 << " " << kv
.second
+ " " + type_map
[kv
.first
] << std::endl
;
740 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
741 for (uint64_t prefix
= 0; prefix
< 10; prefix
++) {
742 uint64_t start_suffix
= rnd
.Uniform(9);
743 SeekIterator(iter
.get(), prefix
, start_suffix
);
744 auto it
= whole_map
.find(TestKey(prefix
, start_suffix
));
745 if (it
== whole_map
.end()) {
748 ASSERT_NE(it
, whole_map
.end());
749 ASSERT_TRUE(iter
->Valid());
750 if (FLAGS_enable_print
) {
751 std::cout
<< "round " << prefix
752 << " iter: " << SliceToTestKey(iter
->key()).prefix
753 << SliceToTestKey(iter
->key()).sorted
754 << " | map: " << it
->first
.prefix
<< it
->first
.sorted
<< " | "
755 << iter
->value().ToString() << " " << it
->second
<< std::endl
;
757 ASSERT_EQ(iter
->value(), it
->second
);
758 uint64_t stored_prefix
= prefix
;
759 for (size_t k
= 0; k
< 9; k
++) {
760 if (rnd
.OneIn(2) || it
== whole_map
.begin()) {
763 if (FLAGS_enable_print
) {
764 std::cout
<< "Next >> ";
769 if (FLAGS_enable_print
) {
770 std::cout
<< "Prev >> ";
773 if (!iter
->Valid() ||
774 SliceToTestKey(iter
->key()).prefix
!= stored_prefix
) {
777 ASSERT_OK(iter
->status());
778 stored_prefix
= SliceToTestKey(iter
->key()).prefix
;
779 ASSERT_TRUE(iter
->Valid());
780 ASSERT_NE(it
, whole_map
.end());
781 ASSERT_EQ(iter
->value(), it
->second
);
782 if (FLAGS_enable_print
) {
783 std::cout
<< "iter: " << SliceToTestKey(iter
->key()).prefix
784 << SliceToTestKey(iter
->key()).sorted
785 << " | map: " << it
->first
.prefix
<< it
->first
.sorted
786 << " | " << iter
->value().ToString() << " " << it
->second
794 TEST_F(PrefixTest
, PrefixSeekModePrev2
) {
795 // Only for SkipListFactory
798 // | prefix | suffix | | prefix | suffix |
799 // | 1 | 1 | | 1 | 2 |
800 // | 1 | 3 | | 1 | 4 |
801 // | 2 | 1 | | 3 | 3 |
802 // | 2 | 2 | | 3 | 4 |
803 // after seek(15), iter1 will be at 21 and iter2 will be 33.
804 // Then if call Prev() in prefix mode where SeekForPrev(21) gets called,
805 // iter2 should turn to invalid state because of bloom filter.
806 options
.memtable_factory
.reset(new SkipListFactory
);
807 options
.write_buffer_size
= 1024 * 1024;
808 std::string
v13("v13");
809 ASSERT_OK(DestroyDB(kDbName
, Options()));
811 WriteOptions write_options
;
812 ReadOptions read_options
;
813 PutKey(db
.get(), write_options
, TestKey(1, 2), "v12");
814 PutKey(db
.get(), write_options
, TestKey(1, 4), "v14");
815 PutKey(db
.get(), write_options
, TestKey(3, 3), "v33");
816 PutKey(db
.get(), write_options
, TestKey(3, 4), "v34");
817 ASSERT_OK(db
->Flush(FlushOptions()));
819 static_cast_with_check
<DBImpl
>(db
.get())->TEST_WaitForFlushMemTable());
820 PutKey(db
.get(), write_options
, TestKey(1, 1), "v11");
821 PutKey(db
.get(), write_options
, TestKey(1, 3), "v13");
822 PutKey(db
.get(), write_options
, TestKey(2, 1), "v21");
823 PutKey(db
.get(), write_options
, TestKey(2, 2), "v22");
824 ASSERT_OK(db
->Flush(FlushOptions()));
826 static_cast_with_check
<DBImpl
>(db
.get())->TEST_WaitForFlushMemTable());
827 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
828 SeekIterator(iter
.get(), 1, 5);
830 ASSERT_TRUE(iter
->Valid());
831 ASSERT_EQ(iter
->value(), v13
);
834 TEST_F(PrefixTest
, PrefixSeekModePrev3
) {
835 // Only for SkipListFactory
836 // test SeekToLast() with iterate_upper_bound_ in prefix_seek_mode
837 options
.memtable_factory
.reset(new SkipListFactory
);
838 options
.write_buffer_size
= 1024 * 1024;
839 std::string
v14("v14");
840 TestKey upper_bound_key
= TestKey(1, 5);
842 Slice upper_bound
= TestKeyToSlice(s
, upper_bound_key
);
845 ASSERT_OK(DestroyDB(kDbName
, Options()));
847 WriteOptions write_options
;
848 ReadOptions read_options
;
849 read_options
.iterate_upper_bound
= &upper_bound
;
850 PutKey(db
.get(), write_options
, TestKey(1, 2), "v12");
851 PutKey(db
.get(), write_options
, TestKey(1, 4), "v14");
852 ASSERT_OK(db
->Flush(FlushOptions()));
854 static_cast_with_check
<DBImpl
>(db
.get())->TEST_WaitForFlushMemTable());
855 PutKey(db
.get(), write_options
, TestKey(1, 1), "v11");
856 PutKey(db
.get(), write_options
, TestKey(1, 3), "v13");
857 PutKey(db
.get(), write_options
, TestKey(2, 1), "v21");
858 PutKey(db
.get(), write_options
, TestKey(2, 2), "v22");
859 ASSERT_OK(db
->Flush(FlushOptions()));
861 static_cast_with_check
<DBImpl
>(db
.get())->TEST_WaitForFlushMemTable());
862 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
864 ASSERT_EQ(iter
->value(), v14
);
867 ASSERT_OK(DestroyDB(kDbName
, Options()));
869 WriteOptions write_options
;
870 ReadOptions read_options
;
871 read_options
.iterate_upper_bound
= &upper_bound
;
872 PutKey(db
.get(), write_options
, TestKey(1, 2), "v12");
873 PutKey(db
.get(), write_options
, TestKey(1, 4), "v14");
874 PutKey(db
.get(), write_options
, TestKey(3, 3), "v33");
875 PutKey(db
.get(), write_options
, TestKey(3, 4), "v34");
876 ASSERT_OK(db
->Flush(FlushOptions()));
878 static_cast_with_check
<DBImpl
>(db
.get())->TEST_WaitForFlushMemTable());
879 PutKey(db
.get(), write_options
, TestKey(1, 1), "v11");
880 PutKey(db
.get(), write_options
, TestKey(1, 3), "v13");
881 ASSERT_OK(db
->Flush(FlushOptions()));
883 static_cast_with_check
<DBImpl
>(db
.get())->TEST_WaitForFlushMemTable());
884 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
886 ASSERT_EQ(iter
->value(), v14
);
890 } // namespace ROCKSDB_NAMESPACE
892 int main(int argc
, char** argv
) {
893 ::testing::InitGoogleTest(&argc
, argv
);
894 ParseCommandLineFlags(&argc
, &argv
, true);
895 return RUN_ALL_TESTS();
903 int main(int /*argc*/, char** /*argv*/) {
905 "SKIPPED as HashSkipList and HashLinkList are not supported in "
910 #endif // !ROCKSDB_LITE