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.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 "util/coding.h"
30 #include "util/gflags_compat.h"
31 #include "util/random.h"
32 #include "util/stop_watch.h"
33 #include "util/string_util.h"
34 #include "util/testharness.h"
35 #include "utilities/merge_operators.h"
37 using GFLAGS_NAMESPACE::ParseCommandLineFlags
;
39 DEFINE_bool(trigger_deadlock
, false,
40 "issue delete in range scan to trigger PrefixHashMap deadlock");
41 DEFINE_int32(bucket_count
, 100000, "number of buckets");
42 DEFINE_uint64(num_locks
, 10001, "number of locks");
43 DEFINE_bool(random_prefix
, false, "randomize prefix");
44 DEFINE_uint64(total_prefixes
, 100000, "total number of prefixes");
45 DEFINE_uint64(items_per_prefix
, 1, "total number of values per prefix");
46 DEFINE_int64(write_buffer_size
, 33554432, "");
47 DEFINE_int32(max_write_buffer_number
, 2, "");
48 DEFINE_int32(min_write_buffer_number_to_merge
, 1, "");
49 DEFINE_int32(skiplist_height
, 4, "");
50 DEFINE_double(memtable_prefix_bloom_size_ratio
, 0.1, "");
51 DEFINE_int32(memtable_huge_page_size
, 2 * 1024 * 1024, "");
52 DEFINE_int32(value_size
, 40, "");
53 DEFINE_bool(enable_print
, false, "Print options generated to console.");
55 // Path to the database on file system
56 const std::string kDbName
= rocksdb::test::PerThreadDBPath("prefix_test");
64 TestKey(uint64_t _prefix
, uint64_t _sorted
)
65 : prefix(_prefix
), sorted(_sorted
) {}
68 // return a slice backed by test_key
69 inline Slice
TestKeyToSlice(std::string
&s
, const TestKey
& test_key
) {
71 PutFixed64(&s
, test_key
.prefix
);
72 PutFixed64(&s
, test_key
.sorted
);
73 return Slice(s
.c_str(), s
.size());
76 inline const TestKey
SliceToTestKey(const Slice
& slice
) {
77 return TestKey(DecodeFixed64(slice
.data()),
78 DecodeFixed64(slice
.data() + 8));
81 class TestKeyComparator
: public Comparator
{
84 // Compare needs to be aware of the possibility of a and/or b is
86 virtual int Compare(const Slice
& a
, const Slice
& b
) const override
{
87 const TestKey kkey_a
= SliceToTestKey(a
);
88 const TestKey kkey_b
= SliceToTestKey(b
);
89 const TestKey
*key_a
= &kkey_a
;
90 const TestKey
*key_b
= &kkey_b
;
91 if (key_a
->prefix
!= key_b
->prefix
) {
92 if (key_a
->prefix
< key_b
->prefix
) return -1;
93 if (key_a
->prefix
> key_b
->prefix
) return 1;
95 EXPECT_TRUE(key_a
->prefix
== key_b
->prefix
);
96 // note, both a and b could be prefix only
97 if (a
.size() != b
.size()) {
98 // one of them is prefix
100 (a
.size() == sizeof(uint64_t) && b
.size() == sizeof(TestKey
)) ||
101 (b
.size() == sizeof(uint64_t) && a
.size() == sizeof(TestKey
)));
102 if (a
.size() < b
.size()) return -1;
103 if (a
.size() > b
.size()) return 1;
105 // both a and b are prefix
106 if (a
.size() == sizeof(uint64_t)) {
110 // both a and b are whole key
111 EXPECT_TRUE(a
.size() == sizeof(TestKey
) && b
.size() == sizeof(TestKey
));
112 if (key_a
->sorted
< key_b
->sorted
) return -1;
113 if (key_a
->sorted
> key_b
->sorted
) return 1;
114 if (key_a
->sorted
== key_b
->sorted
) return 0;
120 bool operator()(const TestKey
& a
, const TestKey
& b
) const {
122 return Compare(TestKeyToSlice(sa
, a
), TestKeyToSlice(sb
, b
)) < 0;
125 virtual const char* Name() const override
{
126 return "TestKeyComparator";
129 virtual void FindShortestSeparator(std::string
* /*start*/,
130 const Slice
& /*limit*/) const override
{}
132 virtual 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 virtual const char* Name() const override
{ return name_
.c_str(); }
200 virtual Slice
Transform(const Slice
& src
) const override
{
201 assert(InDomain(src
));
205 virtual bool InDomain(const Slice
& src
) const override
{
206 if (src
.size() >= prefix_
.size()) {
207 return Slice(src
.data(), prefix_
.size()) == prefix_
;
212 virtual bool InRange(const Slice
& dst
) const override
{
213 return dst
== prefix_
;
216 virtual bool FullLengthEnabled(size_t* /*len*/) const override
{
223 class PrefixTest
: public testing::Test
{
225 std::shared_ptr
<DB
> OpenDb() {
228 options
.create_if_missing
= true;
229 options
.write_buffer_size
= FLAGS_write_buffer_size
;
230 options
.max_write_buffer_number
= FLAGS_max_write_buffer_number
;
231 options
.min_write_buffer_number_to_merge
=
232 FLAGS_min_write_buffer_number_to_merge
;
234 options
.memtable_prefix_bloom_size_ratio
=
235 FLAGS_memtable_prefix_bloom_size_ratio
;
236 options
.memtable_huge_page_size
= FLAGS_memtable_huge_page_size
;
238 options
.prefix_extractor
.reset(NewFixedPrefixTransform(8));
239 BlockBasedTableOptions bbto
;
240 bbto
.filter_policy
.reset(NewBloomFilterPolicy(10, false));
241 bbto
.whole_key_filtering
= false;
242 options
.table_factory
.reset(NewBlockBasedTableFactory(bbto
));
243 options
.allow_concurrent_memtable_write
= false;
245 Status s
= DB::Open(options
, kDbName
, &db
);
247 return std::shared_ptr
<DB
>(db
);
251 option_config_
= kBegin
;
254 bool NextOptions(int bucket_count
) {
257 if (option_config_
< kEnd
) {
258 options
.prefix_extractor
.reset(NewFixedPrefixTransform(8));
259 switch(option_config_
) {
261 options
.memtable_factory
.reset(
262 NewHashSkipListRepFactory(bucket_count
, FLAGS_skiplist_height
));
265 options
.memtable_factory
.reset(
266 NewHashLinkListRepFactory(bucket_count
));
268 case kHashLinkListHugePageTlb
:
269 options
.memtable_factory
.reset(
270 NewHashLinkListRepFactory(bucket_count
, 2 * 1024 * 1024));
272 case kHashLinkListTriggerSkipList
:
273 options
.memtable_factory
.reset(
274 NewHashLinkListRepFactory(bucket_count
, 0, 3));
283 PrefixTest() : option_config_(kBegin
) {
284 options
.comparator
= new TestKeyComparator();
287 delete options
.comparator
;
294 kHashLinkListHugePageTlb
,
295 kHashLinkListTriggerSkipList
,
302 TEST(SamePrefixTest
, InDomainTest
) {
305 options
.create_if_missing
= true;
306 options
.prefix_extractor
.reset(new SamePrefixTransform("HHKB"));
307 BlockBasedTableOptions bbto
;
308 bbto
.filter_policy
.reset(NewBloomFilterPolicy(10, false));
309 bbto
.whole_key_filtering
= false;
310 options
.table_factory
.reset(NewBlockBasedTableFactory(bbto
));
311 WriteOptions write_options
;
312 ReadOptions read_options
;
314 ASSERT_OK(DestroyDB(kDbName
, Options()));
315 ASSERT_OK(DB::Open(options
, kDbName
, &db
));
316 ASSERT_OK(db
->Put(write_options
, "HHKB pro2", "Mar 24, 2006"));
317 ASSERT_OK(db
->Put(write_options
, "HHKB pro2 Type-S", "June 29, 2011"));
318 ASSERT_OK(db
->Put(write_options
, "Realforce 87u", "idk"));
319 db
->Flush(FlushOptions());
321 auto db_iter
= db
->NewIterator(ReadOptions());
323 db_iter
->Seek("Realforce 87u");
324 ASSERT_TRUE(db_iter
->Valid());
325 ASSERT_OK(db_iter
->status());
326 ASSERT_EQ(db_iter
->key(), "Realforce 87u");
327 ASSERT_EQ(db_iter
->value(), "idk");
331 ASSERT_OK(DestroyDB(kDbName
, Options()));
335 ASSERT_OK(DB::Open(options
, kDbName
, &db
));
336 ASSERT_OK(db
->Put(write_options
, "pikachu", "1"));
337 ASSERT_OK(db
->Put(write_options
, "Meowth", "1"));
338 ASSERT_OK(db
->Put(write_options
, "Mewtwo", "idk"));
339 db
->Flush(FlushOptions());
341 auto db_iter
= db
->NewIterator(ReadOptions());
343 db_iter
->Seek("Mewtwo");
344 ASSERT_TRUE(db_iter
->Valid());
345 ASSERT_OK(db_iter
->status());
348 ASSERT_OK(DestroyDB(kDbName
, Options()));
352 TEST_F(PrefixTest
, TestResult
) {
353 for (int num_buckets
= 1; num_buckets
<= 2; num_buckets
++) {
355 while (NextOptions(num_buckets
)) {
356 std::cout
<< "*** Mem table: " << options
.memtable_factory
->Name()
357 << " number of buckets: " << num_buckets
359 DestroyDB(kDbName
, Options());
361 WriteOptions write_options
;
362 ReadOptions read_options
;
364 // 1. Insert one row.
366 PutKey(db
.get(), write_options
, 1, 6, v16
);
367 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
368 SeekIterator(iter
.get(), 1, 6);
369 ASSERT_TRUE(iter
->Valid());
370 ASSERT_TRUE(v16
== iter
->value());
371 SeekIterator(iter
.get(), 1, 5);
372 ASSERT_TRUE(iter
->Valid());
373 ASSERT_TRUE(v16
== iter
->value());
374 SeekIterator(iter
.get(), 1, 5);
375 ASSERT_TRUE(iter
->Valid());
376 ASSERT_TRUE(v16
== iter
->value());
378 ASSERT_TRUE(!iter
->Valid());
380 SeekIterator(iter
.get(), 2, 0);
381 ASSERT_TRUE(!iter
->Valid());
383 ASSERT_EQ(v16
.ToString(), Get(db
.get(), read_options
, 1, 6));
384 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 1, 5));
385 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 1, 7));
386 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 0, 6));
387 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 2, 6));
389 // 2. Insert an entry for the same prefix as the last entry in the bucket.
391 PutKey(db
.get(), write_options
, 1, 7, v17
);
392 iter
.reset(db
->NewIterator(read_options
));
393 SeekIterator(iter
.get(), 1, 7);
394 ASSERT_TRUE(iter
->Valid());
395 ASSERT_TRUE(v17
== iter
->value());
397 SeekIterator(iter
.get(), 1, 6);
398 ASSERT_TRUE(iter
->Valid());
399 ASSERT_TRUE(v16
== iter
->value());
401 ASSERT_TRUE(iter
->Valid());
402 ASSERT_TRUE(v17
== iter
->value());
404 ASSERT_TRUE(!iter
->Valid());
406 SeekIterator(iter
.get(), 2, 0);
407 ASSERT_TRUE(!iter
->Valid());
409 // 3. Insert an entry for the same prefix as the head of the bucket.
411 PutKey(db
.get(), write_options
, 1, 5, v15
);
412 iter
.reset(db
->NewIterator(read_options
));
414 SeekIterator(iter
.get(), 1, 7);
415 ASSERT_TRUE(iter
->Valid());
416 ASSERT_TRUE(v17
== iter
->value());
418 SeekIterator(iter
.get(), 1, 5);
419 ASSERT_TRUE(iter
->Valid());
420 ASSERT_TRUE(v15
== iter
->value());
422 ASSERT_TRUE(iter
->Valid());
423 ASSERT_TRUE(v16
== iter
->value());
425 ASSERT_TRUE(iter
->Valid());
426 ASSERT_TRUE(v17
== iter
->value());
428 SeekIterator(iter
.get(), 1, 5);
429 ASSERT_TRUE(iter
->Valid());
430 ASSERT_TRUE(v15
== iter
->value());
432 ASSERT_EQ(v15
.ToString(), Get(db
.get(), read_options
, 1, 5));
433 ASSERT_EQ(v16
.ToString(), Get(db
.get(), read_options
, 1, 6));
434 ASSERT_EQ(v17
.ToString(), Get(db
.get(), read_options
, 1, 7));
436 // 4. Insert an entry with a larger prefix
438 PutKey(db
.get(), write_options
, 2, 2, v22
);
439 iter
.reset(db
->NewIterator(read_options
));
441 SeekIterator(iter
.get(), 2, 2);
442 ASSERT_TRUE(iter
->Valid());
443 ASSERT_TRUE(v22
== iter
->value());
444 SeekIterator(iter
.get(), 2, 0);
445 ASSERT_TRUE(iter
->Valid());
446 ASSERT_TRUE(v22
== iter
->value());
448 SeekIterator(iter
.get(), 1, 5);
449 ASSERT_TRUE(iter
->Valid());
450 ASSERT_TRUE(v15
== iter
->value());
452 SeekIterator(iter
.get(), 1, 7);
453 ASSERT_TRUE(iter
->Valid());
454 ASSERT_TRUE(v17
== iter
->value());
456 // 5. Insert an entry with a smaller prefix
458 PutKey(db
.get(), write_options
, 0, 2, v02
);
459 iter
.reset(db
->NewIterator(read_options
));
461 SeekIterator(iter
.get(), 0, 2);
462 ASSERT_TRUE(iter
->Valid());
463 ASSERT_TRUE(v02
== iter
->value());
464 SeekIterator(iter
.get(), 0, 0);
465 ASSERT_TRUE(iter
->Valid());
466 ASSERT_TRUE(v02
== iter
->value());
468 SeekIterator(iter
.get(), 2, 0);
469 ASSERT_TRUE(iter
->Valid());
470 ASSERT_TRUE(v22
== iter
->value());
472 SeekIterator(iter
.get(), 1, 5);
473 ASSERT_TRUE(iter
->Valid());
474 ASSERT_TRUE(v15
== iter
->value());
476 SeekIterator(iter
.get(), 1, 7);
477 ASSERT_TRUE(iter
->Valid());
478 ASSERT_TRUE(v17
== iter
->value());
480 // 6. Insert to the beginning and the end of the first prefix
483 PutKey(db
.get(), write_options
, 1, 3, v13
);
484 PutKey(db
.get(), write_options
, 1, 8, v18
);
485 iter
.reset(db
->NewIterator(read_options
));
486 SeekIterator(iter
.get(), 1, 7);
487 ASSERT_TRUE(iter
->Valid());
488 ASSERT_TRUE(v17
== iter
->value());
490 SeekIterator(iter
.get(), 1, 3);
491 ASSERT_TRUE(iter
->Valid());
492 ASSERT_TRUE(v13
== iter
->value());
494 ASSERT_TRUE(iter
->Valid());
495 ASSERT_TRUE(v15
== iter
->value());
497 ASSERT_TRUE(iter
->Valid());
498 ASSERT_TRUE(v16
== iter
->value());
500 ASSERT_TRUE(iter
->Valid());
501 ASSERT_TRUE(v17
== iter
->value());
503 ASSERT_TRUE(iter
->Valid());
504 ASSERT_TRUE(v18
== iter
->value());
506 SeekIterator(iter
.get(), 0, 0);
507 ASSERT_TRUE(iter
->Valid());
508 ASSERT_TRUE(v02
== iter
->value());
510 SeekIterator(iter
.get(), 2, 0);
511 ASSERT_TRUE(iter
->Valid());
512 ASSERT_TRUE(v22
== iter
->value());
514 ASSERT_EQ(v22
.ToString(), Get(db
.get(), read_options
, 2, 2));
515 ASSERT_EQ(v02
.ToString(), Get(db
.get(), read_options
, 0, 2));
516 ASSERT_EQ(v13
.ToString(), Get(db
.get(), read_options
, 1, 3));
517 ASSERT_EQ(v15
.ToString(), Get(db
.get(), read_options
, 1, 5));
518 ASSERT_EQ(v16
.ToString(), Get(db
.get(), read_options
, 1, 6));
519 ASSERT_EQ(v17
.ToString(), Get(db
.get(), read_options
, 1, 7));
520 ASSERT_EQ(v18
.ToString(), Get(db
.get(), read_options
, 1, 8));
525 // Show results in prefix
526 TEST_F(PrefixTest
, PrefixValid
) {
527 for (int num_buckets
= 1; num_buckets
<= 2; num_buckets
++) {
529 while (NextOptions(num_buckets
)) {
530 std::cout
<< "*** Mem table: " << options
.memtable_factory
->Name()
531 << " number of buckets: " << num_buckets
<< std::endl
;
532 DestroyDB(kDbName
, Options());
534 WriteOptions write_options
;
535 ReadOptions read_options
;
537 // Insert keys with common prefix and one key with different
542 PutKey(db
.get(), write_options
, 12345, 6, v16
);
543 PutKey(db
.get(), write_options
, 12345, 7, v17
);
544 PutKey(db
.get(), write_options
, 12345, 8, v18
);
545 PutKey(db
.get(), write_options
, 12345, 9, v19
);
546 PutKey(db
.get(), write_options
, 12346, 8, v16
);
547 db
->Flush(FlushOptions());
548 TestKey
test_key(12346, 8);
550 db
->Delete(write_options
, TestKeyToSlice(s
, test_key
));
551 db
->Flush(FlushOptions());
552 read_options
.prefix_same_as_start
= true;
553 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
554 SeekIterator(iter
.get(), 12345, 6);
555 ASSERT_TRUE(iter
->Valid());
556 ASSERT_TRUE(v16
== iter
->value());
559 ASSERT_TRUE(iter
->Valid());
560 ASSERT_TRUE(v17
== iter
->value());
563 ASSERT_TRUE(iter
->Valid());
564 ASSERT_TRUE(v18
== iter
->value());
567 ASSERT_TRUE(iter
->Valid());
568 ASSERT_TRUE(v19
== iter
->value());
570 ASSERT_FALSE(iter
->Valid());
571 ASSERT_EQ(kNotFoundResult
, Get(db
.get(), read_options
, 12346, 8));
573 // Verify seeking past the prefix won't return a result.
574 SeekIterator(iter
.get(), 12345, 10);
575 ASSERT_TRUE(!iter
->Valid());
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 std::random_shuffle(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());
675 std::cout
<< "non-existing Seek key comparison: \n"
676 << hist_no_seek_comparison
.ToString()
677 << "non-existing Seek time: \n"
678 << hist_no_seek_time
.ToString();
682 TEST_F(PrefixTest
, PrefixSeekModePrev
) {
683 // Only for SkipListFactory
684 options
.memtable_factory
.reset(new SkipListFactory
);
685 options
.merge_operator
= MergeOperators::CreatePutOperator();
686 options
.write_buffer_size
= 1024 * 1024;
688 for (size_t m
= 1; m
< 100; m
++) {
689 std::cout
<< "[" + std::to_string(m
) + "]" + "*** Mem table: "
690 << options
.memtable_factory
->Name() << std::endl
;
691 DestroyDB(kDbName
, Options());
693 WriteOptions write_options
;
694 ReadOptions read_options
;
695 std::map
<TestKey
, std::string
, TestKeyComparator
> entry_maps
[3], whole_map
;
696 for (uint64_t i
= 0; i
< 10; i
++) {
698 for (uint64_t j
= 0; j
< 10; j
++) {
699 whole_map
[TestKey(i
, j
)] = entry_maps
[rnd
.Uniform(div
)][TestKey(i
, j
)] =
700 'v' + std::to_string(i
) + std::to_string(j
);
704 std::map
<TestKey
, std::string
, TestKeyComparator
> type_map
;
705 for (size_t i
= 0; i
< 3; i
++) {
706 for (auto& kv
: entry_maps
[i
]) {
708 PutKey(db
.get(), write_options
, kv
.first
, kv
.second
);
709 type_map
[kv
.first
] = "value";
711 MergeKey(db
.get(), write_options
, kv
.first
, kv
.second
);
712 type_map
[kv
.first
] = "merge";
716 db
->Flush(FlushOptions());
720 for (size_t i
= 0; i
< 2; i
++) {
721 for (auto& kv
: entry_maps
[i
]) {
723 whole_map
.erase(kv
.first
);
724 DeleteKey(db
.get(), write_options
, kv
.first
);
725 entry_maps
[2][kv
.first
] = "delete";
730 if (FLAGS_enable_print
) {
731 for (size_t i
= 0; i
< 3; i
++) {
732 for (auto& kv
: entry_maps
[i
]) {
733 std::cout
<< "[" << i
<< "]" << kv
.first
.prefix
<< kv
.first
.sorted
734 << " " << kv
.second
+ " " + type_map
[kv
.first
] << std::endl
;
739 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
740 for (uint64_t prefix
= 0; prefix
< 10; prefix
++) {
741 uint64_t start_suffix
= rnd
.Uniform(9);
742 SeekIterator(iter
.get(), prefix
, start_suffix
);
743 auto it
= whole_map
.find(TestKey(prefix
, start_suffix
));
744 if (it
== whole_map
.end()) {
747 ASSERT_NE(it
, whole_map
.end());
748 ASSERT_TRUE(iter
->Valid());
749 if (FLAGS_enable_print
) {
750 std::cout
<< "round " << prefix
751 << " iter: " << SliceToTestKey(iter
->key()).prefix
752 << SliceToTestKey(iter
->key()).sorted
753 << " | map: " << it
->first
.prefix
<< it
->first
.sorted
<< " | "
754 << iter
->value().ToString() << " " << it
->second
<< std::endl
;
756 ASSERT_EQ(iter
->value(), it
->second
);
757 uint64_t stored_prefix
= prefix
;
758 for (size_t k
= 0; k
< 9; k
++) {
759 if (rnd
.OneIn(2) || it
== whole_map
.begin()) {
762 if (FLAGS_enable_print
) {
763 std::cout
<< "Next >> ";
768 if (FLAGS_enable_print
) {
769 std::cout
<< "Prev >> ";
772 if (!iter
->Valid() ||
773 SliceToTestKey(iter
->key()).prefix
!= stored_prefix
) {
776 stored_prefix
= SliceToTestKey(iter
->key()).prefix
;
777 ASSERT_TRUE(iter
->Valid());
778 ASSERT_NE(it
, whole_map
.end());
779 ASSERT_EQ(iter
->value(), it
->second
);
780 if (FLAGS_enable_print
) {
781 std::cout
<< "iter: " << SliceToTestKey(iter
->key()).prefix
782 << SliceToTestKey(iter
->key()).sorted
783 << " | map: " << it
->first
.prefix
<< it
->first
.sorted
784 << " | " << iter
->value().ToString() << " " << it
->second
792 TEST_F(PrefixTest
, PrefixSeekModePrev2
) {
793 // Only for SkipListFactory
796 // | prefix | suffix | | prefix | suffix |
797 // | 1 | 1 | | 1 | 2 |
798 // | 1 | 3 | | 1 | 4 |
799 // | 2 | 1 | | 3 | 3 |
800 // | 2 | 2 | | 3 | 4 |
801 // after seek(15), iter1 will be at 21 and iter2 will be 33.
802 // Then if call Prev() in prefix mode where SeekForPrev(21) gets called,
803 // iter2 should turn to invalid state because of bloom filter.
804 options
.memtable_factory
.reset(new SkipListFactory
);
805 options
.write_buffer_size
= 1024 * 1024;
806 std::string
v13("v13");
807 DestroyDB(kDbName
, Options());
809 WriteOptions write_options
;
810 ReadOptions read_options
;
811 PutKey(db
.get(), write_options
, TestKey(1, 2), "v12");
812 PutKey(db
.get(), write_options
, TestKey(1, 4), "v14");
813 PutKey(db
.get(), write_options
, TestKey(3, 3), "v33");
814 PutKey(db
.get(), write_options
, TestKey(3, 4), "v34");
815 db
->Flush(FlushOptions());
816 reinterpret_cast<DBImpl
*>(db
.get())->TEST_WaitForFlushMemTable();
817 PutKey(db
.get(), write_options
, TestKey(1, 1), "v11");
818 PutKey(db
.get(), write_options
, TestKey(1, 3), "v13");
819 PutKey(db
.get(), write_options
, TestKey(2, 1), "v21");
820 PutKey(db
.get(), write_options
, TestKey(2, 2), "v22");
821 db
->Flush(FlushOptions());
822 reinterpret_cast<DBImpl
*>(db
.get())->TEST_WaitForFlushMemTable();
823 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
824 SeekIterator(iter
.get(), 1, 5);
826 ASSERT_EQ(iter
->value(), v13
);
829 TEST_F(PrefixTest
, PrefixSeekModePrev3
) {
830 // Only for SkipListFactory
831 // test SeekToLast() with iterate_upper_bound_ in prefix_seek_mode
832 options
.memtable_factory
.reset(new SkipListFactory
);
833 options
.write_buffer_size
= 1024 * 1024;
834 std::string
v14("v14");
835 TestKey upper_bound_key
= TestKey(1, 5);
837 Slice upper_bound
= TestKeyToSlice(s
, upper_bound_key
);
840 DestroyDB(kDbName
, Options());
842 WriteOptions write_options
;
843 ReadOptions read_options
;
844 read_options
.iterate_upper_bound
= &upper_bound
;
845 PutKey(db
.get(), write_options
, TestKey(1, 2), "v12");
846 PutKey(db
.get(), write_options
, TestKey(1, 4), "v14");
847 db
->Flush(FlushOptions());
848 reinterpret_cast<DBImpl
*>(db
.get())->TEST_WaitForFlushMemTable();
849 PutKey(db
.get(), write_options
, TestKey(1, 1), "v11");
850 PutKey(db
.get(), write_options
, TestKey(1, 3), "v13");
851 PutKey(db
.get(), write_options
, TestKey(2, 1), "v21");
852 PutKey(db
.get(), write_options
, TestKey(2, 2), "v22");
853 db
->Flush(FlushOptions());
854 reinterpret_cast<DBImpl
*>(db
.get())->TEST_WaitForFlushMemTable();
855 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
857 ASSERT_EQ(iter
->value(), v14
);
860 DestroyDB(kDbName
, Options());
862 WriteOptions write_options
;
863 ReadOptions read_options
;
864 read_options
.iterate_upper_bound
= &upper_bound
;
865 PutKey(db
.get(), write_options
, TestKey(1, 2), "v12");
866 PutKey(db
.get(), write_options
, TestKey(1, 4), "v14");
867 PutKey(db
.get(), write_options
, TestKey(3, 3), "v33");
868 PutKey(db
.get(), write_options
, TestKey(3, 4), "v34");
869 db
->Flush(FlushOptions());
870 reinterpret_cast<DBImpl
*>(db
.get())->TEST_WaitForFlushMemTable();
871 PutKey(db
.get(), write_options
, TestKey(1, 1), "v11");
872 PutKey(db
.get(), write_options
, TestKey(1, 3), "v13");
873 db
->Flush(FlushOptions());
874 reinterpret_cast<DBImpl
*>(db
.get())->TEST_WaitForFlushMemTable();
875 std::unique_ptr
<Iterator
> iter(db
->NewIterator(read_options
));
877 ASSERT_EQ(iter
->value(), v14
);
881 } // end namespace rocksdb
883 int main(int argc
, char** argv
) {
884 ::testing::InitGoogleTest(&argc
, argv
);
885 ParseCommandLineFlags(&argc
, &argv
, true);
886 return RUN_ALL_TESTS();
894 int main(int /*argc*/, char** /*argv*/) {
896 "SKIPPED as HashSkipList and HashLinkList are not supported in "
901 #endif // !ROCKSDB_LITE