1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under the BSD-style license found in the
3 // LICENSE file in the root directory of this source tree. An additional grant
4 // of patent rights can be found in the PATENTS file in the same directory.
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.
12 #include "db/db_test_util.h"
13 #include "port/port.h"
14 #include "port/stack_trace.h"
15 #include "rocksdb/iostats_context.h"
16 #include "rocksdb/perf_context.h"
20 class DBIteratorTest
: public DBTestBase
{
22 DBIteratorTest() : DBTestBase("/db_iterator_test") {}
25 TEST_F(DBIteratorTest
, IteratorProperty
) {
26 // The test needs to be changed if kPersistedTier is supported in iterator.
27 Options options
= CurrentOptions();
28 CreateAndReopenWithCF({"pikachu"}, options
);
31 ropt
.pin_data
= false;
33 unique_ptr
<Iterator
> iter(db_
->NewIterator(ropt
, handles_
[1]));
35 std::string prop_value
;
36 ASSERT_NOK(iter
->GetProperty("non_existing.value", &prop_value
));
37 ASSERT_OK(iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
38 ASSERT_EQ("0", prop_value
);
40 ASSERT_OK(iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
41 ASSERT_EQ("Iterator is not valid.", prop_value
);
46 TEST_F(DBIteratorTest
, PersistedTierOnIterator
) {
47 // The test needs to be changed if kPersistedTier is supported in iterator.
48 Options options
= CurrentOptions();
49 CreateAndReopenWithCF({"pikachu"}, options
);
51 ropt
.read_tier
= kPersistedTier
;
53 auto* iter
= db_
->NewIterator(ropt
, handles_
[1]);
54 ASSERT_TRUE(iter
->status().IsNotSupported());
57 std::vector
<Iterator
*> iters
;
58 ASSERT_TRUE(db_
->NewIterators(ropt
, {handles_
[1]}, &iters
).IsNotSupported());
62 TEST_F(DBIteratorTest
, NonBlockingIteration
) {
64 ReadOptions non_blocking_opts
, regular_opts
;
65 Options options
= CurrentOptions();
66 options
.statistics
= rocksdb::CreateDBStatistics();
67 non_blocking_opts
.read_tier
= kBlockCacheTier
;
68 CreateAndReopenWithCF({"pikachu"}, options
);
69 // write one kv to the database.
70 ASSERT_OK(Put(1, "a", "b"));
72 // scan using non-blocking iterator. We should find it because
74 Iterator
* iter
= db_
->NewIterator(non_blocking_opts
, handles_
[1]);
76 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
77 ASSERT_OK(iter
->status());
83 // flush memtable to storage. Now, the key should not be in the
84 // memtable neither in the block cache.
87 // verify that a non-blocking iterator does not find any
88 // kvs. Neither does it do any IOs to storage.
89 uint64_t numopen
= TestGetTickerCount(options
, NO_FILE_OPENS
);
90 uint64_t cache_added
= TestGetTickerCount(options
, BLOCK_CACHE_ADD
);
91 iter
= db_
->NewIterator(non_blocking_opts
, handles_
[1]);
93 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
97 ASSERT_TRUE(iter
->status().IsIncomplete());
98 ASSERT_EQ(numopen
, TestGetTickerCount(options
, NO_FILE_OPENS
));
99 ASSERT_EQ(cache_added
, TestGetTickerCount(options
, BLOCK_CACHE_ADD
));
102 // read in the specified block via a regular get
103 ASSERT_EQ(Get(1, "a"), "b");
105 // verify that we can find it via a non-blocking scan
106 numopen
= TestGetTickerCount(options
, NO_FILE_OPENS
);
107 cache_added
= TestGetTickerCount(options
, BLOCK_CACHE_ADD
);
108 iter
= db_
->NewIterator(non_blocking_opts
, handles_
[1]);
110 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
111 ASSERT_OK(iter
->status());
115 ASSERT_EQ(numopen
, TestGetTickerCount(options
, NO_FILE_OPENS
));
116 ASSERT_EQ(cache_added
, TestGetTickerCount(options
, BLOCK_CACHE_ADD
));
119 // This test verifies block cache behaviors, which is not used by plain
121 // Exclude kHashCuckoo as it does not support iteration currently
122 } while (ChangeOptions(kSkipPlainTable
| kSkipNoSeekToLast
| kSkipHashCuckoo
|
127 TEST_F(DBIteratorTest
, ManagedNonBlockingIteration
) {
129 ReadOptions non_blocking_opts
, regular_opts
;
130 Options options
= CurrentOptions();
131 options
.statistics
= rocksdb::CreateDBStatistics();
132 non_blocking_opts
.read_tier
= kBlockCacheTier
;
133 non_blocking_opts
.managed
= true;
134 CreateAndReopenWithCF({"pikachu"}, options
);
135 // write one kv to the database.
136 ASSERT_OK(Put(1, "a", "b"));
138 // scan using non-blocking iterator. We should find it because
139 // it is in memtable.
140 Iterator
* iter
= db_
->NewIterator(non_blocking_opts
, handles_
[1]);
142 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
143 ASSERT_OK(iter
->status());
149 // flush memtable to storage. Now, the key should not be in the
150 // memtable neither in the block cache.
153 // verify that a non-blocking iterator does not find any
154 // kvs. Neither does it do any IOs to storage.
155 int64_t numopen
= TestGetTickerCount(options
, NO_FILE_OPENS
);
156 int64_t cache_added
= TestGetTickerCount(options
, BLOCK_CACHE_ADD
);
157 iter
= db_
->NewIterator(non_blocking_opts
, handles_
[1]);
159 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
163 ASSERT_TRUE(iter
->status().IsIncomplete());
164 ASSERT_EQ(numopen
, TestGetTickerCount(options
, NO_FILE_OPENS
));
165 ASSERT_EQ(cache_added
, TestGetTickerCount(options
, BLOCK_CACHE_ADD
));
168 // read in the specified block via a regular get
169 ASSERT_EQ(Get(1, "a"), "b");
171 // verify that we can find it via a non-blocking scan
172 numopen
= TestGetTickerCount(options
, NO_FILE_OPENS
);
173 cache_added
= TestGetTickerCount(options
, BLOCK_CACHE_ADD
);
174 iter
= db_
->NewIterator(non_blocking_opts
, handles_
[1]);
176 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
177 ASSERT_OK(iter
->status());
181 ASSERT_EQ(numopen
, TestGetTickerCount(options
, NO_FILE_OPENS
));
182 ASSERT_EQ(cache_added
, TestGetTickerCount(options
, BLOCK_CACHE_ADD
));
185 // This test verifies block cache behaviors, which is not used by plain
187 // Exclude kHashCuckoo as it does not support iteration currently
188 } while (ChangeOptions(kSkipPlainTable
| kSkipNoSeekToLast
| kSkipHashCuckoo
|
191 #endif // ROCKSDB_LITE
193 TEST_F(DBIteratorTest
, IterSeekBeforePrev
) {
194 ASSERT_OK(Put("a", "b"));
195 ASSERT_OK(Put("c", "d"));
196 dbfull()->Flush(FlushOptions());
197 ASSERT_OK(Put("0", "f"));
198 ASSERT_OK(Put("1", "h"));
199 dbfull()->Flush(FlushOptions());
200 ASSERT_OK(Put("2", "j"));
201 auto iter
= db_
->NewIterator(ReadOptions());
202 iter
->Seek(Slice("c"));
204 iter
->Seek(Slice("a"));
209 TEST_F(DBIteratorTest
, IterSeekForPrevBeforeNext
) {
210 ASSERT_OK(Put("a", "b"));
211 ASSERT_OK(Put("c", "d"));
212 dbfull()->Flush(FlushOptions());
213 ASSERT_OK(Put("0", "f"));
214 ASSERT_OK(Put("1", "h"));
215 dbfull()->Flush(FlushOptions());
216 ASSERT_OK(Put("2", "j"));
217 auto iter
= db_
->NewIterator(ReadOptions());
218 iter
->SeekForPrev(Slice("0"));
220 iter
->SeekForPrev(Slice("1"));
226 std::string
MakeLongKey(size_t length
, char c
) {
227 return std::string(length
, c
);
231 TEST_F(DBIteratorTest
, IterLongKeys
) {
232 ASSERT_OK(Put(MakeLongKey(20, 0), "0"));
233 ASSERT_OK(Put(MakeLongKey(32, 2), "2"));
234 ASSERT_OK(Put("a", "b"));
235 dbfull()->Flush(FlushOptions());
236 ASSERT_OK(Put(MakeLongKey(50, 1), "1"));
237 ASSERT_OK(Put(MakeLongKey(127, 3), "3"));
238 ASSERT_OK(Put(MakeLongKey(64, 4), "4"));
239 auto iter
= db_
->NewIterator(ReadOptions());
241 // Create a key that needs to be skipped for Seq too new
242 iter
->Seek(MakeLongKey(20, 0));
243 ASSERT_EQ(IterStatus(iter
), MakeLongKey(20, 0) + "->0");
245 ASSERT_EQ(IterStatus(iter
), MakeLongKey(50, 1) + "->1");
247 ASSERT_EQ(IterStatus(iter
), MakeLongKey(32, 2) + "->2");
249 ASSERT_EQ(IterStatus(iter
), MakeLongKey(127, 3) + "->3");
251 ASSERT_EQ(IterStatus(iter
), MakeLongKey(64, 4) + "->4");
253 iter
->SeekForPrev(MakeLongKey(127, 3));
254 ASSERT_EQ(IterStatus(iter
), MakeLongKey(127, 3) + "->3");
256 ASSERT_EQ(IterStatus(iter
), MakeLongKey(32, 2) + "->2");
258 ASSERT_EQ(IterStatus(iter
), MakeLongKey(50, 1) + "->1");
261 iter
= db_
->NewIterator(ReadOptions());
262 iter
->Seek(MakeLongKey(50, 1));
263 ASSERT_EQ(IterStatus(iter
), MakeLongKey(50, 1) + "->1");
265 ASSERT_EQ(IterStatus(iter
), MakeLongKey(32, 2) + "->2");
267 ASSERT_EQ(IterStatus(iter
), MakeLongKey(127, 3) + "->3");
271 TEST_F(DBIteratorTest
, IterNextWithNewerSeq
) {
272 ASSERT_OK(Put("0", "0"));
273 dbfull()->Flush(FlushOptions());
274 ASSERT_OK(Put("a", "b"));
275 ASSERT_OK(Put("c", "d"));
276 ASSERT_OK(Put("d", "e"));
277 auto iter
= db_
->NewIterator(ReadOptions());
279 // Create a key that needs to be skipped for Seq too new
280 for (uint64_t i
= 0; i
< last_options_
.max_sequential_skip_in_iterations
+ 1;
282 ASSERT_OK(Put("b", "f"));
285 iter
->Seek(Slice("a"));
286 ASSERT_EQ(IterStatus(iter
), "a->b");
288 ASSERT_EQ(IterStatus(iter
), "c->d");
289 iter
->SeekForPrev(Slice("b"));
290 ASSERT_EQ(IterStatus(iter
), "a->b");
292 ASSERT_EQ(IterStatus(iter
), "c->d");
297 TEST_F(DBIteratorTest
, IterPrevWithNewerSeq
) {
298 ASSERT_OK(Put("0", "0"));
299 dbfull()->Flush(FlushOptions());
300 ASSERT_OK(Put("a", "b"));
301 ASSERT_OK(Put("c", "d"));
302 ASSERT_OK(Put("d", "e"));
303 auto iter
= db_
->NewIterator(ReadOptions());
305 // Create a key that needs to be skipped for Seq too new
306 for (uint64_t i
= 0; i
< last_options_
.max_sequential_skip_in_iterations
+ 1;
308 ASSERT_OK(Put("b", "f"));
311 iter
->Seek(Slice("d"));
312 ASSERT_EQ(IterStatus(iter
), "d->e");
314 ASSERT_EQ(IterStatus(iter
), "c->d");
316 ASSERT_EQ(IterStatus(iter
), "a->b");
318 iter
->SeekForPrev(Slice("d"));
319 ASSERT_EQ(IterStatus(iter
), "d->e");
321 ASSERT_EQ(IterStatus(iter
), "c->d");
323 ASSERT_EQ(IterStatus(iter
), "a->b");
328 TEST_F(DBIteratorTest
, IterPrevWithNewerSeq2
) {
329 ASSERT_OK(Put("0", "0"));
330 dbfull()->Flush(FlushOptions());
331 ASSERT_OK(Put("a", "b"));
332 ASSERT_OK(Put("c", "d"));
333 ASSERT_OK(Put("e", "f"));
334 auto iter
= db_
->NewIterator(ReadOptions());
335 auto iter2
= db_
->NewIterator(ReadOptions());
336 iter
->Seek(Slice("c"));
337 iter2
->SeekForPrev(Slice("d"));
338 ASSERT_EQ(IterStatus(iter
), "c->d");
339 ASSERT_EQ(IterStatus(iter2
), "c->d");
341 // Create a key that needs to be skipped for Seq too new
342 for (uint64_t i
= 0; i
< last_options_
.max_sequential_skip_in_iterations
+ 1;
344 ASSERT_OK(Put("b", "f"));
348 ASSERT_EQ(IterStatus(iter
), "a->b");
351 ASSERT_EQ(IterStatus(iter2
), "a->b");
357 TEST_F(DBIteratorTest
, IterEmpty
) {
359 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
360 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
363 ASSERT_EQ(IterStatus(iter
), "(invalid)");
366 ASSERT_EQ(IterStatus(iter
), "(invalid)");
369 ASSERT_EQ(IterStatus(iter
), "(invalid)");
371 iter
->SeekForPrev("foo");
372 ASSERT_EQ(IterStatus(iter
), "(invalid)");
375 } while (ChangeCompactOptions());
378 TEST_F(DBIteratorTest
, IterSingle
) {
380 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
381 ASSERT_OK(Put(1, "a", "va"));
382 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
385 ASSERT_EQ(IterStatus(iter
), "a->va");
387 ASSERT_EQ(IterStatus(iter
), "(invalid)");
389 ASSERT_EQ(IterStatus(iter
), "a->va");
391 ASSERT_EQ(IterStatus(iter
), "(invalid)");
394 ASSERT_EQ(IterStatus(iter
), "a->va");
396 ASSERT_EQ(IterStatus(iter
), "(invalid)");
398 ASSERT_EQ(IterStatus(iter
), "a->va");
400 ASSERT_EQ(IterStatus(iter
), "(invalid)");
403 ASSERT_EQ(IterStatus(iter
), "a->va");
405 ASSERT_EQ(IterStatus(iter
), "(invalid)");
406 iter
->SeekForPrev("");
407 ASSERT_EQ(IterStatus(iter
), "(invalid)");
410 ASSERT_EQ(IterStatus(iter
), "a->va");
412 ASSERT_EQ(IterStatus(iter
), "(invalid)");
413 iter
->SeekForPrev("a");
414 ASSERT_EQ(IterStatus(iter
), "a->va");
416 ASSERT_EQ(IterStatus(iter
), "(invalid)");
419 ASSERT_EQ(IterStatus(iter
), "(invalid)");
420 iter
->SeekForPrev("b");
421 ASSERT_EQ(IterStatus(iter
), "a->va");
423 ASSERT_EQ(IterStatus(iter
), "(invalid)");
426 } while (ChangeCompactOptions());
429 TEST_F(DBIteratorTest
, IterMulti
) {
431 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
432 ASSERT_OK(Put(1, "a", "va"));
433 ASSERT_OK(Put(1, "b", "vb"));
434 ASSERT_OK(Put(1, "c", "vc"));
435 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
438 ASSERT_EQ(IterStatus(iter
), "a->va");
440 ASSERT_EQ(IterStatus(iter
), "b->vb");
442 ASSERT_EQ(IterStatus(iter
), "c->vc");
444 ASSERT_EQ(IterStatus(iter
), "(invalid)");
446 ASSERT_EQ(IterStatus(iter
), "a->va");
448 ASSERT_EQ(IterStatus(iter
), "(invalid)");
451 ASSERT_EQ(IterStatus(iter
), "c->vc");
453 ASSERT_EQ(IterStatus(iter
), "b->vb");
455 ASSERT_EQ(IterStatus(iter
), "a->va");
457 ASSERT_EQ(IterStatus(iter
), "(invalid)");
459 ASSERT_EQ(IterStatus(iter
), "c->vc");
461 ASSERT_EQ(IterStatus(iter
), "(invalid)");
464 ASSERT_EQ(IterStatus(iter
), "a->va");
466 ASSERT_EQ(IterStatus(iter
), "a->va");
468 ASSERT_EQ(IterStatus(iter
), "b->vb");
469 iter
->SeekForPrev("d");
470 ASSERT_EQ(IterStatus(iter
), "c->vc");
471 iter
->SeekForPrev("c");
472 ASSERT_EQ(IterStatus(iter
), "c->vc");
473 iter
->SeekForPrev("bx");
474 ASSERT_EQ(IterStatus(iter
), "b->vb");
477 ASSERT_EQ(IterStatus(iter
), "b->vb");
479 ASSERT_EQ(IterStatus(iter
), "(invalid)");
480 iter
->SeekForPrev("b");
481 ASSERT_EQ(IterStatus(iter
), "b->vb");
482 iter
->SeekForPrev("");
483 ASSERT_EQ(IterStatus(iter
), "(invalid)");
485 // Switch from reverse to forward
490 ASSERT_EQ(IterStatus(iter
), "b->vb");
492 // Switch from forward to reverse
497 ASSERT_EQ(IterStatus(iter
), "b->vb");
499 // Make sure iter stays at snapshot
500 ASSERT_OK(Put(1, "a", "va2"));
501 ASSERT_OK(Put(1, "a2", "va3"));
502 ASSERT_OK(Put(1, "b", "vb2"));
503 ASSERT_OK(Put(1, "c", "vc2"));
504 ASSERT_OK(Delete(1, "b"));
506 ASSERT_EQ(IterStatus(iter
), "a->va");
508 ASSERT_EQ(IterStatus(iter
), "b->vb");
510 ASSERT_EQ(IterStatus(iter
), "c->vc");
512 ASSERT_EQ(IterStatus(iter
), "(invalid)");
514 ASSERT_EQ(IterStatus(iter
), "c->vc");
516 ASSERT_EQ(IterStatus(iter
), "b->vb");
518 ASSERT_EQ(IterStatus(iter
), "a->va");
520 ASSERT_EQ(IterStatus(iter
), "(invalid)");
523 } while (ChangeCompactOptions());
526 // Check that we can skip over a run of user keys
527 // by using reseek rather than sequential scan
528 TEST_F(DBIteratorTest
, IterReseek
) {
529 anon::OptionsOverride options_override
;
530 options_override
.skip_policy
= kSkipNoSnapshot
;
531 Options options
= CurrentOptions(options_override
);
532 options
.max_sequential_skip_in_iterations
= 3;
533 options
.create_if_missing
= true;
534 options
.statistics
= rocksdb::CreateDBStatistics();
535 DestroyAndReopen(options
);
536 CreateAndReopenWithCF({"pikachu"}, options
);
538 // insert three keys with same userkey and verify that
539 // reseek is not invoked. For each of these test cases,
540 // verify that we can find the next key "b".
541 ASSERT_OK(Put(1, "a", "zero"));
542 ASSERT_OK(Put(1, "a", "one"));
543 ASSERT_OK(Put(1, "a", "two"));
544 ASSERT_OK(Put(1, "b", "bone"));
545 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
547 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
), 0);
548 ASSERT_EQ(IterStatus(iter
), "a->two");
550 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
), 0);
551 ASSERT_EQ(IterStatus(iter
), "b->bone");
554 // insert a total of three keys with same userkey and verify
555 // that reseek is still not invoked.
556 ASSERT_OK(Put(1, "a", "three"));
557 iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
559 ASSERT_EQ(IterStatus(iter
), "a->three");
561 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
), 0);
562 ASSERT_EQ(IterStatus(iter
), "b->bone");
565 // insert a total of four keys with same userkey and verify
566 // that reseek is invoked.
567 ASSERT_OK(Put(1, "a", "four"));
568 iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
570 ASSERT_EQ(IterStatus(iter
), "a->four");
571 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
), 0);
573 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
), 1);
574 ASSERT_EQ(IterStatus(iter
), "b->bone");
577 // Testing reverse iterator
578 // At this point, we have three versions of "a" and one version of "b".
579 // The reseek statistics is already at 1.
580 int num_reseeks
= static_cast<int>(
581 TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
));
583 // Insert another version of b and assert that reseek is not invoked
584 ASSERT_OK(Put(1, "b", "btwo"));
585 iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
587 ASSERT_EQ(IterStatus(iter
), "b->btwo");
588 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
),
591 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
),
593 ASSERT_EQ(IterStatus(iter
), "a->four");
596 // insert two more versions of b. This makes a total of 4 versions
597 // of b and 4 versions of a.
598 ASSERT_OK(Put(1, "b", "bthree"));
599 ASSERT_OK(Put(1, "b", "bfour"));
600 iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
602 ASSERT_EQ(IterStatus(iter
), "b->bfour");
603 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
),
607 // the previous Prev call should have invoked reseek
608 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_OF_RESEEKS_IN_ITERATION
),
610 ASSERT_EQ(IterStatus(iter
), "a->four");
614 TEST_F(DBIteratorTest
, IterSmallAndLargeMix
) {
616 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
617 ASSERT_OK(Put(1, "a", "va"));
618 ASSERT_OK(Put(1, "b", std::string(100000, 'b')));
619 ASSERT_OK(Put(1, "c", "vc"));
620 ASSERT_OK(Put(1, "d", std::string(100000, 'd')));
621 ASSERT_OK(Put(1, "e", std::string(100000, 'e')));
623 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
626 ASSERT_EQ(IterStatus(iter
), "a->va");
628 ASSERT_EQ(IterStatus(iter
), "b->" + std::string(100000, 'b'));
630 ASSERT_EQ(IterStatus(iter
), "c->vc");
632 ASSERT_EQ(IterStatus(iter
), "d->" + std::string(100000, 'd'));
634 ASSERT_EQ(IterStatus(iter
), "e->" + std::string(100000, 'e'));
636 ASSERT_EQ(IterStatus(iter
), "(invalid)");
639 ASSERT_EQ(IterStatus(iter
), "e->" + std::string(100000, 'e'));
641 ASSERT_EQ(IterStatus(iter
), "d->" + std::string(100000, 'd'));
643 ASSERT_EQ(IterStatus(iter
), "c->vc");
645 ASSERT_EQ(IterStatus(iter
), "b->" + std::string(100000, 'b'));
647 ASSERT_EQ(IterStatus(iter
), "a->va");
649 ASSERT_EQ(IterStatus(iter
), "(invalid)");
652 } while (ChangeCompactOptions());
655 TEST_F(DBIteratorTest
, IterMultiWithDelete
) {
657 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
658 ASSERT_OK(Put(1, "ka", "va"));
659 ASSERT_OK(Put(1, "kb", "vb"));
660 ASSERT_OK(Put(1, "kc", "vc"));
661 ASSERT_OK(Delete(1, "kb"));
662 ASSERT_EQ("NOT_FOUND", Get(1, "kb"));
664 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
666 ASSERT_EQ(IterStatus(iter
), "kc->vc");
667 if (!CurrentOptions().merge_operator
) {
668 // TODO: merge operator does not support backward iteration yet
669 if (kPlainTableAllBytesPrefix
!= option_config_
&&
670 kBlockBasedTableWithWholeKeyHashIndex
!= option_config_
&&
671 kHashLinkList
!= option_config_
&&
672 kHashSkipList
!= option_config_
) { // doesn't support SeekToLast
674 ASSERT_EQ(IterStatus(iter
), "ka->va");
678 } while (ChangeOptions());
681 TEST_F(DBIteratorTest
, IterPrevMaxSkip
) {
683 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
684 for (int i
= 0; i
< 2; i
++) {
685 ASSERT_OK(Put(1, "key1", "v1"));
686 ASSERT_OK(Put(1, "key2", "v2"));
687 ASSERT_OK(Put(1, "key3", "v3"));
688 ASSERT_OK(Put(1, "key4", "v4"));
689 ASSERT_OK(Put(1, "key5", "v5"));
692 VerifyIterLast("key5->v5", 1);
694 ASSERT_OK(Delete(1, "key5"));
695 VerifyIterLast("key4->v4", 1);
697 ASSERT_OK(Delete(1, "key4"));
698 VerifyIterLast("key3->v3", 1);
700 ASSERT_OK(Delete(1, "key3"));
701 VerifyIterLast("key2->v2", 1);
703 ASSERT_OK(Delete(1, "key2"));
704 VerifyIterLast("key1->v1", 1);
706 ASSERT_OK(Delete(1, "key1"));
707 VerifyIterLast("(invalid)", 1);
708 } while (ChangeOptions(kSkipMergePut
| kSkipNoSeekToLast
));
711 TEST_F(DBIteratorTest
, IterWithSnapshot
) {
712 anon::OptionsOverride options_override
;
713 options_override
.skip_policy
= kSkipNoSnapshot
;
715 CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override
));
716 ASSERT_OK(Put(1, "key1", "val1"));
717 ASSERT_OK(Put(1, "key2", "val2"));
718 ASSERT_OK(Put(1, "key3", "val3"));
719 ASSERT_OK(Put(1, "key4", "val4"));
720 ASSERT_OK(Put(1, "key5", "val5"));
722 const Snapshot
* snapshot
= db_
->GetSnapshot();
724 options
.snapshot
= snapshot
;
725 Iterator
* iter
= db_
->NewIterator(options
, handles_
[1]);
727 ASSERT_OK(Put(1, "key0", "val0"));
728 // Put more values after the snapshot
729 ASSERT_OK(Put(1, "key100", "val100"));
730 ASSERT_OK(Put(1, "key101", "val101"));
733 ASSERT_EQ(IterStatus(iter
), "key5->val5");
734 if (!CurrentOptions().merge_operator
) {
735 // TODO: merge operator does not support backward iteration yet
736 if (kPlainTableAllBytesPrefix
!= option_config_
&&
737 kBlockBasedTableWithWholeKeyHashIndex
!= option_config_
&&
738 kHashLinkList
!= option_config_
&& kHashSkipList
!= option_config_
) {
740 ASSERT_EQ(IterStatus(iter
), "key4->val4");
742 ASSERT_EQ(IterStatus(iter
), "key3->val3");
745 ASSERT_EQ(IterStatus(iter
), "key4->val4");
747 ASSERT_EQ(IterStatus(iter
), "key5->val5");
750 ASSERT_TRUE(!iter
->Valid());
753 if (!CurrentOptions().merge_operator
) {
754 // TODO(gzh): merge operator does not support backward iteration yet
755 if (kPlainTableAllBytesPrefix
!= option_config_
&&
756 kBlockBasedTableWithWholeKeyHashIndex
!= option_config_
&&
757 kHashLinkList
!= option_config_
&& kHashSkipList
!= option_config_
) {
758 iter
->SeekForPrev("key1");
759 ASSERT_EQ(IterStatus(iter
), "key1->val1");
761 ASSERT_EQ(IterStatus(iter
), "key2->val2");
763 ASSERT_EQ(IterStatus(iter
), "key3->val3");
765 ASSERT_EQ(IterStatus(iter
), "key2->val2");
767 ASSERT_EQ(IterStatus(iter
), "key1->val1");
769 ASSERT_TRUE(!iter
->Valid());
772 db_
->ReleaseSnapshot(snapshot
);
774 // skip as HashCuckooRep does not support snapshot
775 } while (ChangeOptions(kSkipHashCuckoo
));
778 TEST_F(DBIteratorTest
, IteratorPinsRef
) {
780 CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
781 Put(1, "foo", "hello");
783 // Get iterator that will yield the current contents of the DB.
784 Iterator
* iter
= db_
->NewIterator(ReadOptions(), handles_
[1]);
786 // Write to force compactions
787 Put(1, "foo", "newvalue1");
788 for (int i
= 0; i
< 100; i
++) {
790 ASSERT_OK(Put(1, Key(i
), Key(i
) + std::string(100000, 'v')));
792 Put(1, "foo", "newvalue2");
795 ASSERT_TRUE(iter
->Valid());
796 ASSERT_EQ("foo", iter
->key().ToString());
797 ASSERT_EQ("hello", iter
->value().ToString());
799 ASSERT_TRUE(!iter
->Valid());
801 } while (ChangeCompactOptions());
804 TEST_F(DBIteratorTest
, DBIteratorBoundTest
) {
805 Options options
= CurrentOptions();
807 options
.create_if_missing
= true;
809 options
.prefix_extractor
= nullptr;
810 DestroyAndReopen(options
);
811 ASSERT_OK(Put("a", "0"));
812 ASSERT_OK(Put("foo", "bar"));
813 ASSERT_OK(Put("foo1", "bar1"));
814 ASSERT_OK(Put("g1", "0"));
816 // testing basic case with no iterate_upper_bound and no prefix_extractor
819 ro
.iterate_upper_bound
= nullptr;
821 std::unique_ptr
<Iterator
> iter(db_
->NewIterator(ro
));
825 ASSERT_TRUE(iter
->Valid());
826 ASSERT_EQ(iter
->key().compare(Slice("foo")), 0);
829 ASSERT_TRUE(iter
->Valid());
830 ASSERT_EQ(iter
->key().compare(Slice("foo1")), 0);
833 ASSERT_TRUE(iter
->Valid());
834 ASSERT_EQ(iter
->key().compare(Slice("g1")), 0);
836 iter
->SeekForPrev("g1");
838 ASSERT_TRUE(iter
->Valid());
839 ASSERT_EQ(iter
->key().compare(Slice("g1")), 0);
842 ASSERT_TRUE(iter
->Valid());
843 ASSERT_EQ(iter
->key().compare(Slice("foo1")), 0);
846 ASSERT_TRUE(iter
->Valid());
847 ASSERT_EQ(iter
->key().compare(Slice("foo")), 0);
850 // testing iterate_upper_bound and forward iterator
851 // to make sure it stops at bound
854 // iterate_upper_bound points beyond the last expected entry
855 Slice
prefix("foo2");
856 ro
.iterate_upper_bound
= &prefix
;
858 std::unique_ptr
<Iterator
> iter(db_
->NewIterator(ro
));
862 ASSERT_TRUE(iter
->Valid());
863 ASSERT_EQ(iter
->key().compare(Slice("foo")), 0);
866 ASSERT_TRUE(iter
->Valid());
867 ASSERT_EQ(iter
->key().compare(("foo1")), 0);
870 // should stop here...
871 ASSERT_TRUE(!iter
->Valid());
873 // Testing SeekToLast with iterate_upper_bound set
878 ro
.iterate_upper_bound
= &prefix
;
880 std::unique_ptr
<Iterator
> iter(db_
->NewIterator(ro
));
883 ASSERT_TRUE(iter
->Valid());
884 ASSERT_EQ(iter
->key().compare(Slice("a")), 0);
887 // prefix is the first letter of the key
888 options
.prefix_extractor
.reset(NewFixedPrefixTransform(1));
890 DestroyAndReopen(options
);
891 ASSERT_OK(Put("a", "0"));
892 ASSERT_OK(Put("foo", "bar"));
893 ASSERT_OK(Put("foo1", "bar1"));
894 ASSERT_OK(Put("g1", "0"));
896 // testing with iterate_upper_bound and prefix_extractor
897 // Seek target and iterate_upper_bound are not is same prefix
898 // This should be an error
901 Slice
upper_bound("g");
902 ro
.iterate_upper_bound
= &upper_bound
;
904 std::unique_ptr
<Iterator
> iter(db_
->NewIterator(ro
));
908 ASSERT_TRUE(iter
->Valid());
909 ASSERT_EQ("foo", iter
->key().ToString());
912 ASSERT_TRUE(iter
->Valid());
913 ASSERT_EQ("foo1", iter
->key().ToString());
916 ASSERT_TRUE(!iter
->Valid());
919 // testing that iterate_upper_bound prevents iterating over deleted items
920 // if the bound has already reached
922 options
.prefix_extractor
= nullptr;
923 DestroyAndReopen(options
);
924 ASSERT_OK(Put("a", "0"));
925 ASSERT_OK(Put("b", "0"));
926 ASSERT_OK(Put("b1", "0"));
927 ASSERT_OK(Put("c", "0"));
928 ASSERT_OK(Put("d", "0"));
929 ASSERT_OK(Put("e", "0"));
930 ASSERT_OK(Delete("c"));
931 ASSERT_OK(Delete("d"));
933 // base case with no bound
935 ro
.iterate_upper_bound
= nullptr;
937 std::unique_ptr
<Iterator
> iter(db_
->NewIterator(ro
));
940 ASSERT_TRUE(iter
->Valid());
941 ASSERT_EQ(iter
->key().compare(Slice("b")), 0);
944 ASSERT_TRUE(iter
->Valid());
945 ASSERT_EQ(iter
->key().compare(("b1")), 0);
947 perf_context
.Reset();
950 ASSERT_TRUE(iter
->Valid());
951 ASSERT_EQ(static_cast<int>(perf_context
.internal_delete_skipped_count
), 2);
953 // now testing with iterate_bound
955 ro
.iterate_upper_bound
= &prefix
;
957 iter
.reset(db_
->NewIterator(ro
));
959 perf_context
.Reset();
962 ASSERT_TRUE(iter
->Valid());
963 ASSERT_EQ(iter
->key().compare(Slice("b")), 0);
966 ASSERT_TRUE(iter
->Valid());
967 ASSERT_EQ(iter
->key().compare(("b1")), 0);
970 // the iteration should stop as soon as the bound key is reached
971 // even though the key is deleted
972 // hence internal_delete_skipped_count should be 0
973 ASSERT_TRUE(!iter
->Valid());
974 ASSERT_EQ(static_cast<int>(perf_context
.internal_delete_skipped_count
), 0);
978 // TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
979 // return the biggest key which is smaller than the seek key.
980 TEST_F(DBIteratorTest
, PrevAfterAndNextAfterMerge
) {
982 options
.create_if_missing
= true;
983 options
.merge_operator
= MergeOperators::CreatePutOperator();
985 DestroyAndReopen(options
);
987 // write three entries with different keys using Merge()
989 db_
->Merge(wopts
, "1", "data1");
990 db_
->Merge(wopts
, "2", "data2");
991 db_
->Merge(wopts
, "3", "data3");
993 std::unique_ptr
<Iterator
> it(db_
->NewIterator(ReadOptions()));
996 ASSERT_TRUE(it
->Valid());
997 ASSERT_EQ("2", it
->key().ToString());
1000 ASSERT_TRUE(it
->Valid());
1001 ASSERT_EQ("1", it
->key().ToString());
1003 it
->SeekForPrev("1");
1004 ASSERT_TRUE(it
->Valid());
1005 ASSERT_EQ("1", it
->key().ToString());
1008 ASSERT_TRUE(it
->Valid());
1009 ASSERT_EQ("2", it
->key().ToString());
1012 TEST_F(DBIteratorTest
, PinnedDataIteratorRandomized
) {
1016 COMPACT_BEFORE_READ
,
1021 // Generate Random data
1025 int key_pool
= static_cast<int>(puts
* 0.7);
1027 int val_size
= 1000;
1028 int seeks_percentage
= 20; // 20% of keys will be used to test seek()
1029 int delete_percentage
= 20; // 20% of keys will be deleted
1030 int merge_percentage
= 20; // 20% of keys will be added using Merge()
1032 for (int run_config
= 0; run_config
< TestConfig::MAX
; run_config
++) {
1033 Options options
= CurrentOptions();
1034 BlockBasedTableOptions table_options
;
1035 table_options
.use_delta_encoding
= false;
1036 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
1037 options
.merge_operator
= MergeOperators::CreatePutOperator();
1038 DestroyAndReopen(options
);
1040 std::vector
<std::string
> generated_keys(key_pool
);
1041 for (int i
= 0; i
< key_pool
; i
++) {
1042 generated_keys
[i
] = RandomString(&rnd
, key_size
);
1045 std::map
<std::string
, std::string
> true_data
;
1046 std::vector
<std::string
> random_keys
;
1047 std::vector
<std::string
> deleted_keys
;
1048 for (int i
= 0; i
< puts
; i
++) {
1049 auto& k
= generated_keys
[rnd
.Next() % key_pool
];
1050 auto v
= RandomString(&rnd
, val_size
);
1052 // Insert data to true_data map and to DB
1054 if (rnd
.OneIn(static_cast<int>(100.0 / merge_percentage
))) {
1055 ASSERT_OK(db_
->Merge(WriteOptions(), k
, v
));
1057 ASSERT_OK(Put(k
, v
));
1060 // Pick random keys to be used to test Seek()
1061 if (rnd
.OneIn(static_cast<int>(100.0 / seeks_percentage
))) {
1062 random_keys
.push_back(k
);
1065 // Delete some random keys
1066 if (rnd
.OneIn(static_cast<int>(100.0 / delete_percentage
))) {
1067 deleted_keys
.push_back(k
);
1069 ASSERT_OK(Delete(k
));
1072 if (run_config
== TestConfig::FLUSH_EVERY_1000
) {
1073 if (i
&& i
% 1000 == 0) {
1079 if (run_config
== TestConfig::CLOSE_AND_OPEN
) {
1082 } else if (run_config
== TestConfig::COMPACT_BEFORE_READ
) {
1083 db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr);
1088 auto iter
= db_
->NewIterator(ro
);
1091 // Test Seek to random keys
1092 std::vector
<Slice
> keys_slices
;
1093 std::vector
<std::string
> true_keys
;
1094 for (auto& k
: random_keys
) {
1096 if (!iter
->Valid()) {
1097 ASSERT_EQ(true_data
.lower_bound(k
), true_data
.end());
1100 std::string prop_value
;
1102 iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1103 ASSERT_EQ("1", prop_value
);
1104 keys_slices
.push_back(iter
->key());
1105 true_keys
.push_back(true_data
.lower_bound(k
)->first
);
1108 for (size_t i
= 0; i
< keys_slices
.size(); i
++) {
1109 ASSERT_EQ(keys_slices
[i
].ToString(), true_keys
[i
]);
1114 // Test SeekForPrev to random keys
1115 std::vector
<Slice
> keys_slices
;
1116 std::vector
<std::string
> true_keys
;
1117 for (auto& k
: random_keys
) {
1118 iter
->SeekForPrev(k
);
1119 if (!iter
->Valid()) {
1120 ASSERT_EQ(true_data
.upper_bound(k
), true_data
.begin());
1123 std::string prop_value
;
1125 iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1126 ASSERT_EQ("1", prop_value
);
1127 keys_slices
.push_back(iter
->key());
1128 true_keys
.push_back((--true_data
.upper_bound(k
))->first
);
1131 for (size_t i
= 0; i
< keys_slices
.size(); i
++) {
1132 ASSERT_EQ(keys_slices
[i
].ToString(), true_keys
[i
]);
1137 // Test iterating all data forward
1138 std::vector
<Slice
> all_keys
;
1139 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
1140 std::string prop_value
;
1142 iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1143 ASSERT_EQ("1", prop_value
);
1144 all_keys
.push_back(iter
->key());
1146 ASSERT_EQ(all_keys
.size(), true_data
.size());
1148 // Verify that all keys slices are valid
1149 auto data_iter
= true_data
.begin();
1150 for (size_t i
= 0; i
< all_keys
.size(); i
++) {
1151 ASSERT_EQ(all_keys
[i
].ToString(), data_iter
->first
);
1157 // Test iterating all data backward
1158 std::vector
<Slice
> all_keys
;
1159 for (iter
->SeekToLast(); iter
->Valid(); iter
->Prev()) {
1160 std::string prop_value
;
1162 iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1163 ASSERT_EQ("1", prop_value
);
1164 all_keys
.push_back(iter
->key());
1166 ASSERT_EQ(all_keys
.size(), true_data
.size());
1168 // Verify that all keys slices are valid (backward)
1169 auto data_iter
= true_data
.rbegin();
1170 for (size_t i
= 0; i
< all_keys
.size(); i
++) {
1171 ASSERT_EQ(all_keys
[i
].ToString(), data_iter
->first
);
1180 #ifndef ROCKSDB_LITE
1181 TEST_F(DBIteratorTest
, PinnedDataIteratorMultipleFiles
) {
1182 Options options
= CurrentOptions();
1183 BlockBasedTableOptions table_options
;
1184 table_options
.use_delta_encoding
= false;
1185 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
1186 options
.disable_auto_compactions
= true;
1187 options
.write_buffer_size
= 1024 * 1024 * 10; // 10 Mb
1188 DestroyAndReopen(options
);
1190 std::map
<std::string
, std::string
> true_data
;
1192 // Generate 4 sst files in L2
1194 for (int i
= 1; i
<= 1000; i
++) {
1195 std::string k
= Key(i
* 3);
1196 std::string v
= RandomString(&rnd
, 100);
1197 ASSERT_OK(Put(k
, v
));
1203 ASSERT_EQ(FilesPerLevel(0), "4");
1204 ASSERT_OK(db_
->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1205 ASSERT_EQ(FilesPerLevel(0), "0,4");
1207 // Generate 4 sst files in L0
1208 for (int i
= 1; i
<= 1000; i
++) {
1209 std::string k
= Key(i
* 2);
1210 std::string v
= RandomString(&rnd
, 100);
1211 ASSERT_OK(Put(k
, v
));
1217 ASSERT_EQ(FilesPerLevel(0), "4,4");
1219 // Add some keys/values in memtables
1220 for (int i
= 1; i
<= 1000; i
++) {
1221 std::string k
= Key(i
);
1222 std::string v
= RandomString(&rnd
, 100);
1223 ASSERT_OK(Put(k
, v
));
1226 ASSERT_EQ(FilesPerLevel(0), "4,4");
1230 auto iter
= db_
->NewIterator(ro
);
1232 std::vector
<std::pair
<Slice
, std::string
>> results
;
1233 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
1234 std::string prop_value
;
1235 ASSERT_OK(iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1236 ASSERT_EQ("1", prop_value
);
1237 results
.emplace_back(iter
->key(), iter
->value().ToString());
1240 ASSERT_EQ(results
.size(), true_data
.size());
1241 auto data_iter
= true_data
.begin();
1242 for (size_t i
= 0; i
< results
.size(); i
++, data_iter
++) {
1243 auto& kv
= results
[i
];
1244 ASSERT_EQ(kv
.first
, data_iter
->first
);
1245 ASSERT_EQ(kv
.second
, data_iter
->second
);
1252 TEST_F(DBIteratorTest
, PinnedDataIteratorMergeOperator
) {
1253 Options options
= CurrentOptions();
1254 BlockBasedTableOptions table_options
;
1255 table_options
.use_delta_encoding
= false;
1256 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
1257 options
.merge_operator
= MergeOperators::CreateUInt64AddOperator();
1258 DestroyAndReopen(options
);
1260 std::string numbers
[7];
1261 for (int val
= 0; val
<= 6; val
++) {
1262 PutFixed64(numbers
+ val
, val
);
1265 // +1 all keys in range [ 0 => 999]
1266 for (int i
= 0; i
< 1000; i
++) {
1268 ASSERT_OK(db_
->Merge(wo
, Key(i
), numbers
[1]));
1271 // +2 all keys divisible by 2 in range [ 0 => 999]
1272 for (int i
= 0; i
< 1000; i
+= 2) {
1274 ASSERT_OK(db_
->Merge(wo
, Key(i
), numbers
[2]));
1277 // +3 all keys divisible by 5 in range [ 0 => 999]
1278 for (int i
= 0; i
< 1000; i
+= 5) {
1280 ASSERT_OK(db_
->Merge(wo
, Key(i
), numbers
[3]));
1285 auto iter
= db_
->NewIterator(ro
);
1287 std::vector
<std::pair
<Slice
, std::string
>> results
;
1288 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
1289 std::string prop_value
;
1290 ASSERT_OK(iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1291 ASSERT_EQ("1", prop_value
);
1292 results
.emplace_back(iter
->key(), iter
->value().ToString());
1295 ASSERT_EQ(results
.size(), 1000);
1296 for (size_t i
= 0; i
< results
.size(); i
++) {
1297 auto& kv
= results
[i
];
1298 ASSERT_EQ(kv
.first
, Key(static_cast<int>(i
)));
1299 int expected_val
= 1;
1306 ASSERT_EQ(kv
.second
, numbers
[expected_val
]);
1312 TEST_F(DBIteratorTest
, PinnedDataIteratorReadAfterUpdate
) {
1313 Options options
= CurrentOptions();
1314 BlockBasedTableOptions table_options
;
1315 table_options
.use_delta_encoding
= false;
1316 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
1317 options
.write_buffer_size
= 100000;
1318 DestroyAndReopen(options
);
1322 std::map
<std::string
, std::string
> true_data
;
1323 for (int i
= 0; i
< 1000; i
++) {
1324 std::string k
= RandomString(&rnd
, 10);
1325 std::string v
= RandomString(&rnd
, 1000);
1326 ASSERT_OK(Put(k
, v
));
1332 auto iter
= db_
->NewIterator(ro
);
1334 // Delete 50% of the keys and update the other 50%
1335 for (auto& kv
: true_data
) {
1337 ASSERT_OK(Delete(kv
.first
));
1339 std::string new_val
= RandomString(&rnd
, 1000);
1340 ASSERT_OK(Put(kv
.first
, new_val
));
1344 std::vector
<std::pair
<Slice
, std::string
>> results
;
1345 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
1346 std::string prop_value
;
1347 ASSERT_OK(iter
->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value
));
1348 ASSERT_EQ("1", prop_value
);
1349 results
.emplace_back(iter
->key(), iter
->value().ToString());
1352 auto data_iter
= true_data
.begin();
1353 for (size_t i
= 0; i
< results
.size(); i
++, data_iter
++) {
1354 auto& kv
= results
[i
];
1355 ASSERT_EQ(kv
.first
, data_iter
->first
);
1356 ASSERT_EQ(kv
.second
, data_iter
->second
);
1362 TEST_F(DBIteratorTest
, IterSeekForPrevCrossingFiles
) {
1363 Options options
= CurrentOptions();
1364 options
.prefix_extractor
.reset(NewFixedPrefixTransform(1));
1365 options
.disable_auto_compactions
= true;
1366 // Enable prefix bloom for SST files
1367 BlockBasedTableOptions table_options
;
1368 table_options
.filter_policy
.reset(NewBloomFilterPolicy(10, true));
1369 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
1370 DestroyAndReopen(options
);
1372 ASSERT_OK(Put("a1", "va1"));
1373 ASSERT_OK(Put("a2", "va2"));
1374 ASSERT_OK(Put("a3", "va3"));
1377 ASSERT_OK(Put("b1", "vb1"));
1378 ASSERT_OK(Put("b2", "vb2"));
1379 ASSERT_OK(Put("b3", "vb3"));
1382 ASSERT_OK(Put("b4", "vb4"));
1383 ASSERT_OK(Put("d1", "vd1"));
1384 ASSERT_OK(Put("d2", "vd2"));
1385 ASSERT_OK(Put("d4", "vd4"));
1388 MoveFilesToLevel(1);
1391 Iterator
* iter
= db_
->NewIterator(ro
);
1393 iter
->SeekForPrev("a4");
1394 ASSERT_EQ(iter
->key().ToString(), "a3");
1395 ASSERT_EQ(iter
->value().ToString(), "va3");
1397 iter
->SeekForPrev("c2");
1398 ASSERT_EQ(iter
->key().ToString(), "b3");
1399 iter
->SeekForPrev("d3");
1400 ASSERT_EQ(iter
->key().ToString(), "d2");
1401 iter
->SeekForPrev("b5");
1402 ASSERT_EQ(iter
->key().ToString(), "b4");
1408 ro
.prefix_same_as_start
= true;
1409 Iterator
* iter
= db_
->NewIterator(ro
);
1410 iter
->SeekForPrev("c2");
1411 ASSERT_TRUE(!iter
->Valid());
1416 TEST_F(DBIteratorTest
, IterPrevKeyCrossingBlocks
) {
1417 Options options
= CurrentOptions();
1418 BlockBasedTableOptions table_options
;
1419 table_options
.block_size
= 1; // every block will contain one entry
1420 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
1421 options
.merge_operator
= MergeOperators::CreateStringAppendTESTOperator();
1422 options
.disable_auto_compactions
= true;
1423 options
.max_sequential_skip_in_iterations
= 8;
1425 DestroyAndReopen(options
);
1427 // Putting such deletes will force DBIter::Prev() to fallback to a Seek
1428 for (int file_num
= 0; file_num
< 10; file_num
++) {
1429 ASSERT_OK(Delete("key4"));
1433 // First File containing 5 blocks of puts
1434 ASSERT_OK(Put("key1", "val1.0"));
1435 ASSERT_OK(Put("key2", "val2.0"));
1436 ASSERT_OK(Put("key3", "val3.0"));
1437 ASSERT_OK(Put("key4", "val4.0"));
1438 ASSERT_OK(Put("key5", "val5.0"));
1441 // Second file containing 9 blocks of merge operands
1442 ASSERT_OK(db_
->Merge(WriteOptions(), "key1", "val1.1"));
1443 ASSERT_OK(db_
->Merge(WriteOptions(), "key1", "val1.2"));
1445 ASSERT_OK(db_
->Merge(WriteOptions(), "key2", "val2.1"));
1446 ASSERT_OK(db_
->Merge(WriteOptions(), "key2", "val2.2"));
1447 ASSERT_OK(db_
->Merge(WriteOptions(), "key2", "val2.3"));
1449 ASSERT_OK(db_
->Merge(WriteOptions(), "key3", "val3.1"));
1450 ASSERT_OK(db_
->Merge(WriteOptions(), "key3", "val3.2"));
1451 ASSERT_OK(db_
->Merge(WriteOptions(), "key3", "val3.3"));
1452 ASSERT_OK(db_
->Merge(WriteOptions(), "key3", "val3.4"));
1457 ro
.fill_cache
= false;
1458 Iterator
* iter
= db_
->NewIterator(ro
);
1461 ASSERT_EQ(iter
->key().ToString(), "key5");
1462 ASSERT_EQ(iter
->value().ToString(), "val5.0");
1465 ASSERT_EQ(iter
->key().ToString(), "key4");
1466 ASSERT_EQ(iter
->value().ToString(), "val4.0");
1469 ASSERT_EQ(iter
->key().ToString(), "key3");
1470 ASSERT_EQ(iter
->value().ToString(), "val3.0,val3.1,val3.2,val3.3,val3.4");
1473 ASSERT_EQ(iter
->key().ToString(), "key2");
1474 ASSERT_EQ(iter
->value().ToString(), "val2.0,val2.1,val2.2,val2.3");
1477 ASSERT_EQ(iter
->key().ToString(), "key1");
1478 ASSERT_EQ(iter
->value().ToString(), "val1.0,val1.1,val1.2");
1484 TEST_F(DBIteratorTest
, IterPrevKeyCrossingBlocksRandomized
) {
1485 Options options
= CurrentOptions();
1486 options
.merge_operator
= MergeOperators::CreateStringAppendTESTOperator();
1487 options
.disable_auto_compactions
= true;
1488 options
.level0_slowdown_writes_trigger
= (1 << 30);
1489 options
.level0_stop_writes_trigger
= (1 << 30);
1490 options
.max_sequential_skip_in_iterations
= 8;
1491 DestroyAndReopen(options
);
1493 const int kNumKeys
= 500;
1494 // Small number of merge operands to make sure that DBIter::Prev() dont
1495 // fall back to Seek()
1496 const int kNumMergeOperands
= 3;
1497 // Use value size that will make sure that every block contain 1 key
1498 const int kValSize
=
1499 static_cast<int>(BlockBasedTableOptions().block_size
) * 4;
1500 // Percentage of keys that wont get merge operations
1501 const int kNoMergeOpPercentage
= 20;
1502 // Percentage of keys that will be deleted
1503 const int kDeletePercentage
= 10;
1505 // For half of the key range we will write multiple deletes first to
1506 // force DBIter::Prev() to fall back to Seek()
1507 for (int file_num
= 0; file_num
< 10; file_num
++) {
1508 for (int i
= 0; i
< kNumKeys
; i
+= 2) {
1509 ASSERT_OK(Delete(Key(i
)));
1515 std::map
<std::string
, std::string
> true_data
;
1516 std::string gen_key
;
1517 std::string gen_val
;
1519 for (int i
= 0; i
< kNumKeys
; i
++) {
1521 gen_val
= RandomString(&rnd
, kValSize
);
1523 ASSERT_OK(Put(gen_key
, gen_val
));
1524 true_data
[gen_key
] = gen_val
;
1528 // Separate values and merge operands in different file so that we
1529 // make sure that we dont merge them while flushing but actually
1530 // merge them in the read path
1531 for (int i
= 0; i
< kNumKeys
; i
++) {
1532 if (rnd
.OneIn(static_cast<int>(100.0 / kNoMergeOpPercentage
))) {
1533 // Dont give merge operations for some keys
1537 for (int j
= 0; j
< kNumMergeOperands
; j
++) {
1539 gen_val
= RandomString(&rnd
, kValSize
);
1541 ASSERT_OK(db_
->Merge(WriteOptions(), gen_key
, gen_val
));
1542 true_data
[gen_key
] += "," + gen_val
;
1547 for (int i
= 0; i
< kNumKeys
; i
++) {
1548 if (rnd
.OneIn(static_cast<int>(100.0 / kDeletePercentage
))) {
1551 ASSERT_OK(Delete(gen_key
));
1552 true_data
.erase(gen_key
);
1559 ro
.fill_cache
= false;
1560 Iterator
* iter
= db_
->NewIterator(ro
);
1561 auto data_iter
= true_data
.rbegin();
1563 for (iter
->SeekToLast(); iter
->Valid(); iter
->Prev()) {
1564 ASSERT_EQ(iter
->key().ToString(), data_iter
->first
);
1565 ASSERT_EQ(iter
->value().ToString(), data_iter
->second
);
1568 ASSERT_EQ(data_iter
, true_data
.rend());
1575 ro
.fill_cache
= false;
1576 Iterator
* iter
= db_
->NewIterator(ro
);
1577 auto data_iter
= true_data
.rbegin();
1579 int entries_right
= 0;
1580 std::string seek_key
;
1581 for (iter
->SeekToLast(); iter
->Valid(); iter
->Prev()) {
1582 // Verify key/value of current position
1583 ASSERT_EQ(iter
->key().ToString(), data_iter
->first
);
1584 ASSERT_EQ(iter
->value().ToString(), data_iter
->second
);
1586 bool restore_position_with_seek
= rnd
.Uniform(2);
1587 if (restore_position_with_seek
) {
1588 seek_key
= iter
->key().ToString();
1591 // Do some Next() operations the restore the iterator to orignal position
1593 entries_right
> 0 ? rnd
.Uniform(std::min(entries_right
, 10)) : 0;
1594 for (int i
= 0; i
< next_count
; i
++) {
1598 ASSERT_EQ(iter
->key().ToString(), data_iter
->first
);
1599 ASSERT_EQ(iter
->value().ToString(), data_iter
->second
);
1602 if (restore_position_with_seek
) {
1603 // Restore orignal position using Seek()
1604 iter
->Seek(seek_key
);
1605 for (int i
= 0; i
< next_count
; i
++) {
1609 ASSERT_EQ(iter
->key().ToString(), data_iter
->first
);
1610 ASSERT_EQ(iter
->value().ToString(), data_iter
->second
);
1612 // Restore original position using Prev()
1613 for (int i
= 0; i
< next_count
; i
++) {
1617 ASSERT_EQ(iter
->key().ToString(), data_iter
->first
);
1618 ASSERT_EQ(iter
->value().ToString(), data_iter
->second
);
1625 ASSERT_EQ(data_iter
, true_data
.rend());
1631 TEST_F(DBIteratorTest
, IteratorWithLocalStatistics
) {
1632 Options options
= CurrentOptions();
1633 options
.statistics
= rocksdb::CreateDBStatistics();
1634 DestroyAndReopen(options
);
1637 for (int i
= 0; i
< 1000; i
++) {
1638 // Key 10 bytes / Value 10 bytes
1639 ASSERT_OK(Put(RandomString(&rnd
, 10), RandomString(&rnd
, 10)));
1642 std::atomic
<uint64_t> total_next(0);
1643 std::atomic
<uint64_t> total_next_found(0);
1644 std::atomic
<uint64_t> total_prev(0);
1645 std::atomic
<uint64_t> total_prev_found(0);
1646 std::atomic
<uint64_t> total_bytes(0);
1648 std::vector
<port::Thread
> threads
;
1649 std::function
<void()> reader_func_next
= [&]() {
1650 Iterator
* iter
= db_
->NewIterator(ReadOptions());
1652 iter
->SeekToFirst();
1653 // Seek will bump ITER_BYTES_READ
1654 total_bytes
+= iter
->key().size();
1655 total_bytes
+= iter
->value().size();
1660 if (!iter
->Valid()) {
1664 total_bytes
+= iter
->key().size();
1665 total_bytes
+= iter
->value().size();
1671 std::function
<void()> reader_func_prev
= [&]() {
1672 Iterator
* iter
= db_
->NewIterator(ReadOptions());
1675 // Seek will bump ITER_BYTES_READ
1676 total_bytes
+= iter
->key().size();
1677 total_bytes
+= iter
->value().size();
1682 if (!iter
->Valid()) {
1686 total_bytes
+= iter
->key().size();
1687 total_bytes
+= iter
->value().size();
1693 for (int i
= 0; i
< 10; i
++) {
1694 threads
.emplace_back(reader_func_next
);
1696 for (int i
= 0; i
< 15; i
++) {
1697 threads
.emplace_back(reader_func_prev
);
1700 for (auto& t
: threads
) {
1704 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_DB_NEXT
), (uint64_t)total_next
);
1705 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_DB_NEXT_FOUND
),
1706 (uint64_t)total_next_found
);
1707 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_DB_PREV
), (uint64_t)total_prev
);
1708 ASSERT_EQ(TestGetTickerCount(options
, NUMBER_DB_PREV_FOUND
),
1709 (uint64_t)total_prev_found
);
1710 ASSERT_EQ(TestGetTickerCount(options
, ITER_BYTES_READ
), (uint64_t)total_bytes
);
1714 TEST_F(DBIteratorTest
, ReadAhead
) {
1716 env_
->count_random_reads_
= true;
1718 options
.disable_auto_compactions
= true;
1719 options
.write_buffer_size
= 4 << 20;
1720 options
.statistics
= rocksdb::CreateDBStatistics();
1721 BlockBasedTableOptions table_options
;
1722 table_options
.block_size
= 1024;
1723 table_options
.no_block_cache
= true;
1724 options
.table_factory
.reset(new BlockBasedTableFactory(table_options
));
1727 std::string
value(1024, 'a');
1728 for (int i
= 0; i
< 100; i
++) {
1732 MoveFilesToLevel(2);
1734 for (int i
= 0; i
< 100; i
++) {
1738 MoveFilesToLevel(1);
1740 for (int i
= 0; i
< 100; i
++) {
1744 #ifndef ROCKSDB_LITE
1745 ASSERT_EQ("1,1,1", FilesPerLevel());
1746 #endif // !ROCKSDB_LITE
1748 env_
->random_read_bytes_counter_
= 0;
1749 options
.statistics
->setTickerCount(NO_FILE_OPENS
, 0);
1750 ReadOptions read_options
;
1751 auto* iter
= db_
->NewIterator(read_options
);
1752 iter
->SeekToFirst();
1753 int64_t num_file_opens
= TestGetTickerCount(options
, NO_FILE_OPENS
);
1754 size_t bytes_read
= env_
->random_read_bytes_counter_
;
1757 env_
->random_read_bytes_counter_
= 0;
1758 options
.statistics
->setTickerCount(NO_FILE_OPENS
, 0);
1759 read_options
.readahead_size
= 1024 * 10;
1760 iter
= db_
->NewIterator(read_options
);
1761 iter
->SeekToFirst();
1762 int64_t num_file_opens_readahead
= TestGetTickerCount(options
, NO_FILE_OPENS
);
1763 size_t bytes_read_readahead
= env_
->random_read_bytes_counter_
;
1765 ASSERT_EQ(num_file_opens
+ 3, num_file_opens_readahead
);
1766 ASSERT_GT(bytes_read_readahead
, bytes_read
);
1767 ASSERT_GT(bytes_read_readahead
, read_options
.readahead_size
* 3);
1769 // Verify correctness.
1770 iter
= db_
->NewIterator(read_options
);
1772 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
1773 ASSERT_EQ(value
, iter
->value());
1776 ASSERT_EQ(100, count
);
1777 for (int i
= 0; i
< 100; i
++) {
1779 ASSERT_EQ(value
, iter
->value());
1784 // Insert a key, create a snapshot iterator, overwrite key lots of times,
1785 // seek to a smaller key. Expect DBIter to fall back to a seek instead of
1786 // going through all the overwrites linearly.
1787 TEST_F(DBIteratorTest
, DBIteratorSkipRecentDuplicatesTest
) {
1788 Options options
= CurrentOptions();
1790 options
.create_if_missing
= true;
1791 options
.max_sequential_skip_in_iterations
= 3;
1792 options
.prefix_extractor
= nullptr;
1793 options
.write_buffer_size
= 1 << 27; // big enough to avoid flush
1794 options
.statistics
= rocksdb::CreateDBStatistics();
1795 DestroyAndReopen(options
);
1798 ASSERT_OK(Put("b", "0"));
1802 std::unique_ptr
<Iterator
> iter(db_
->NewIterator(ro
));
1805 for (int i
= 0; i
< 100; ++i
) {
1806 ASSERT_OK(Put("b", std::to_string(i
+ 1).c_str()));
1809 #ifndef ROCKSDB_LITE
1810 // Check that memtable wasn't flushed.
1812 ASSERT_TRUE(db_
->GetProperty("rocksdb.num-files-at-level0", &val
));
1813 EXPECT_EQ("0", val
);
1816 // Seek iterator to a smaller key.
1817 perf_context
.Reset();
1819 ASSERT_TRUE(iter
->Valid());
1820 EXPECT_EQ("b", iter
->key().ToString());
1821 EXPECT_EQ("0", iter
->value().ToString());
1823 // Check that the seek didn't do too much work.
1824 // Checks are not tight, just make sure that everything is well below 100.
1825 EXPECT_LT(perf_context
.internal_key_skipped_count
, 4);
1826 EXPECT_LT(perf_context
.internal_recent_skipped_count
, 8);
1827 EXPECT_LT(perf_context
.seek_on_memtable_count
, 10);
1828 EXPECT_LT(perf_context
.next_on_memtable_count
, 10);
1829 EXPECT_LT(perf_context
.prev_on_memtable_count
, 10);
1831 // Check that iterator did something like what we expect.
1832 EXPECT_EQ(perf_context
.internal_delete_skipped_count
, 0);
1833 EXPECT_EQ(perf_context
.internal_merge_count
, 0);
1834 EXPECT_GE(perf_context
.internal_recent_skipped_count
, 2);
1835 EXPECT_GE(perf_context
.seek_on_memtable_count
, 2);
1836 EXPECT_EQ(1, options
.statistics
->getTickerCount(
1837 NUMBER_OF_RESEEKS_IN_ITERATION
));
1840 } // namespace rocksdb
1842 int main(int argc
, char** argv
) {
1843 rocksdb::port::InstallStackTraceHandler();
1844 ::testing::InitGoogleTest(&argc
, argv
);
1845 return RUN_ALL_TESTS();