| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).//// Copyright (c) 2011 The LevelDB Authors. All rights reserved.// Use of this source code is governed by a BSD-style license that can be// found in the LICENSE file. See the AUTHORS file for names of contributors.#include <functional>#include "db/arena_wrapped_db_iter.h"#include "db/db_iter.h"#include "db/db_test_util.h"#include "port/port.h"#include "port/stack_trace.h"#include "rocksdb/iostats_context.h"#include "rocksdb/perf_context.h"#include "table/block_based/flush_block_policy.h"namespace ROCKSDB_NAMESPACE {// A dumb ReadCallback which saying every key is committed.class DummyReadCallback : public ReadCallback { public:  DummyReadCallback() : ReadCallback(kMaxSequenceNumber) {}  bool IsVisibleFullCheck(SequenceNumber /*seq*/) override { return true; }  void SetSnapshot(SequenceNumber seq) { max_visible_seq_ = seq; }};// Test param://   bool: whether to pass read_callback to NewIterator().class DBIteratorTest : public DBTestBase,                       public testing::WithParamInterface<bool> { public:  DBIteratorTest() : DBTestBase("/db_iterator_test") {}  Iterator* NewIterator(const ReadOptions& read_options,                        ColumnFamilyHandle* column_family = nullptr) {    if (column_family == nullptr) {      column_family = db_->DefaultColumnFamily();    }    auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family)->cfd();    SequenceNumber seq = read_options.snapshot != nullptr                             ? read_options.snapshot->GetSequenceNumber()                             : db_->GetLatestSequenceNumber();    bool use_read_callback = GetParam();    DummyReadCallback* read_callback = nullptr;    if (use_read_callback) {      read_callback = new DummyReadCallback();      read_callback->SetSnapshot(seq);      InstrumentedMutexLock lock(&mutex_);      read_callbacks_.push_back(          std::unique_ptr<DummyReadCallback>(read_callback));    }    return dbfull()->NewIteratorImpl(read_options, cfd, seq, read_callback);  } private:  InstrumentedMutex mutex_;  std::vector<std::unique_ptr<DummyReadCallback>> read_callbacks_;};TEST_P(DBIteratorTest, IteratorProperty) {  // The test needs to be changed if kPersistedTier is supported in iterator.  Options options = CurrentOptions();  CreateAndReopenWithCF({"pikachu"}, options);  Put(1, "1", "2");  Delete(1, "2");  ReadOptions ropt;  ropt.pin_data = false;  {    std::unique_ptr<Iterator> iter(NewIterator(ropt, handles_[1]));    iter->SeekToFirst();    std::string prop_value;    ASSERT_NOK(iter->GetProperty("non_existing.value", &prop_value));    ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));    ASSERT_EQ("0", prop_value);    ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));    ASSERT_EQ("1", prop_value);    iter->Next();    ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));    ASSERT_EQ("Iterator is not valid.", prop_value);    // Get internal key at which the iteration stopped (tombstone in this case).    ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));    ASSERT_EQ("2", prop_value);  }  Close();}TEST_P(DBIteratorTest, PersistedTierOnIterator) {  // The test needs to be changed if kPersistedTier is supported in iterator.  Options options = CurrentOptions();  CreateAndReopenWithCF({"pikachu"}, options);  ReadOptions ropt;  ropt.read_tier = kPersistedTier;  auto* iter = db_->NewIterator(ropt, handles_[1]);  ASSERT_TRUE(iter->status().IsNotSupported());  delete iter;  std::vector<Iterator*> iters;  ASSERT_TRUE(db_->NewIterators(ropt, {handles_[1]}, &iters).IsNotSupported());  Close();}TEST_P(DBIteratorTest, NonBlockingIteration) {  do {    ReadOptions non_blocking_opts, regular_opts;    Options options = CurrentOptions();    options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();    non_blocking_opts.read_tier = kBlockCacheTier;    CreateAndReopenWithCF({"pikachu"}, options);    // write one kv to the database.    ASSERT_OK(Put(1, "a", "b"));    // scan using non-blocking iterator. We should find it because    // it is in memtable.    Iterator* iter = NewIterator(non_blocking_opts, handles_[1]);    int count = 0;    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {      ASSERT_OK(iter->status());      count++;    }    ASSERT_EQ(count, 1);    delete iter;    // flush memtable to storage. Now, the key should not be in the    // memtable neither in the block cache.    ASSERT_OK(Flush(1));    // verify that a non-blocking iterator does not find any    // kvs. Neither does it do any IOs to storage.    uint64_t numopen = TestGetTickerCount(options, NO_FILE_OPENS);    uint64_t cache_added = TestGetTickerCount(options, BLOCK_CACHE_ADD);    iter = NewIterator(non_blocking_opts, handles_[1]);    count = 0;    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {      count++;    }    ASSERT_EQ(count, 0);    ASSERT_TRUE(iter->status().IsIncomplete());    ASSERT_EQ(numopen, TestGetTickerCount(options, NO_FILE_OPENS));    ASSERT_EQ(cache_added, TestGetTickerCount(options, BLOCK_CACHE_ADD));    delete iter;    // read in the specified block via a regular get    ASSERT_EQ(Get(1, "a"), "b");    // verify that we can find it via a non-blocking scan    numopen = TestGetTickerCount(options, NO_FILE_OPENS);    cache_added = TestGetTickerCount(options, BLOCK_CACHE_ADD);    iter = NewIterator(non_blocking_opts, handles_[1]);    count = 0;    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {      ASSERT_OK(iter->status());      count++;    }    ASSERT_EQ(count, 1);    ASSERT_EQ(numopen, TestGetTickerCount(options, NO_FILE_OPENS));    ASSERT_EQ(cache_added, TestGetTickerCount(options, BLOCK_CACHE_ADD));    delete iter;    // This test verifies block cache behaviors, which is not used by plain    // table format.  } while (ChangeOptions(kSkipPlainTable | kSkipNoSeekToLast | kSkipMmapReads));}TEST_P(DBIteratorTest, IterSeekBeforePrev) {  ASSERT_OK(Put("a", "b"));  ASSERT_OK(Put("c", "d"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("0", "f"));  ASSERT_OK(Put("1", "h"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("2", "j"));  auto iter = NewIterator(ReadOptions());  iter->Seek(Slice("c"));  iter->Prev();  iter->Seek(Slice("a"));  iter->Prev();  delete iter;}TEST_P(DBIteratorTest, IterReseekNewUpperBound) {  Random rnd(301);  Options options = CurrentOptions();  BlockBasedTableOptions table_options;  table_options.block_size = 1024;  table_options.block_size_deviation = 50;  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  options.compression = kNoCompression;  Reopen(options);  ASSERT_OK(Put("a", RandomString(&rnd, 400)));  ASSERT_OK(Put("aabb", RandomString(&rnd, 400)));  ASSERT_OK(Put("aaef", RandomString(&rnd, 400)));  ASSERT_OK(Put("b", RandomString(&rnd, 400)));  dbfull()->Flush(FlushOptions());  ReadOptions opts;  Slice ub = Slice("aa");  opts.iterate_upper_bound = &ub;  auto iter = NewIterator(opts);  iter->Seek(Slice("a"));  ub = Slice("b");  iter->Seek(Slice("aabc"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().ToString(), "aaef");  delete iter;}TEST_P(DBIteratorTest, IterSeekForPrevBeforeNext) {  ASSERT_OK(Put("a", "b"));  ASSERT_OK(Put("c", "d"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("0", "f"));  ASSERT_OK(Put("1", "h"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("2", "j"));  auto iter = NewIterator(ReadOptions());  iter->SeekForPrev(Slice("0"));  iter->Next();  iter->SeekForPrev(Slice("1"));  iter->Next();  delete iter;}namespace {std::string MakeLongKey(size_t length, char c) {  return std::string(length, c);}}  // namespaceTEST_P(DBIteratorTest, IterLongKeys) {  ASSERT_OK(Put(MakeLongKey(20, 0), "0"));  ASSERT_OK(Put(MakeLongKey(32, 2), "2"));  ASSERT_OK(Put("a", "b"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put(MakeLongKey(50, 1), "1"));  ASSERT_OK(Put(MakeLongKey(127, 3), "3"));  ASSERT_OK(Put(MakeLongKey(64, 4), "4"));  auto iter = NewIterator(ReadOptions());  // Create a key that needs to be skipped for Seq too new  iter->Seek(MakeLongKey(20, 0));  ASSERT_EQ(IterStatus(iter), MakeLongKey(20, 0) + "->0");  iter->Next();  ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");  iter->Next();  ASSERT_EQ(IterStatus(iter), MakeLongKey(32, 2) + "->2");  iter->Next();  ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");  iter->Next();  ASSERT_EQ(IterStatus(iter), MakeLongKey(64, 4) + "->4");  iter->SeekForPrev(MakeLongKey(127, 3));  ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");  iter->Prev();  ASSERT_EQ(IterStatus(iter), MakeLongKey(32, 2) + "->2");  iter->Prev();  ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");  delete iter;  iter = NewIterator(ReadOptions());  iter->Seek(MakeLongKey(50, 1));  ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");  iter->Next();  ASSERT_EQ(IterStatus(iter), MakeLongKey(32, 2) + "->2");  iter->Next();  ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");  delete iter;}TEST_P(DBIteratorTest, IterNextWithNewerSeq) {  ASSERT_OK(Put("0", "0"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("a", "b"));  ASSERT_OK(Put("c", "d"));  ASSERT_OK(Put("d", "e"));  auto iter = NewIterator(ReadOptions());  // Create a key that needs to be skipped for Seq too new  for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;       i++) {    ASSERT_OK(Put("b", "f"));  }  iter->Seek(Slice("a"));  ASSERT_EQ(IterStatus(iter), "a->b");  iter->Next();  ASSERT_EQ(IterStatus(iter), "c->d");  iter->SeekForPrev(Slice("b"));  ASSERT_EQ(IterStatus(iter), "a->b");  iter->Next();  ASSERT_EQ(IterStatus(iter), "c->d");  delete iter;}TEST_P(DBIteratorTest, IterPrevWithNewerSeq) {  ASSERT_OK(Put("0", "0"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("a", "b"));  ASSERT_OK(Put("c", "d"));  ASSERT_OK(Put("d", "e"));  auto iter = NewIterator(ReadOptions());  // Create a key that needs to be skipped for Seq too new  for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;       i++) {    ASSERT_OK(Put("b", "f"));  }  iter->Seek(Slice("d"));  ASSERT_EQ(IterStatus(iter), "d->e");  iter->Prev();  ASSERT_EQ(IterStatus(iter), "c->d");  iter->Prev();  ASSERT_EQ(IterStatus(iter), "a->b");  iter->Prev();  iter->SeekForPrev(Slice("d"));  ASSERT_EQ(IterStatus(iter), "d->e");  iter->Prev();  ASSERT_EQ(IterStatus(iter), "c->d");  iter->Prev();  ASSERT_EQ(IterStatus(iter), "a->b");  iter->Prev();  delete iter;}TEST_P(DBIteratorTest, IterPrevWithNewerSeq2) {  ASSERT_OK(Put("0", "0"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("a", "b"));  ASSERT_OK(Put("c", "d"));  ASSERT_OK(Put("e", "f"));  auto iter = NewIterator(ReadOptions());  auto iter2 = NewIterator(ReadOptions());  iter->Seek(Slice("c"));  iter2->SeekForPrev(Slice("d"));  ASSERT_EQ(IterStatus(iter), "c->d");  ASSERT_EQ(IterStatus(iter2), "c->d");  // Create a key that needs to be skipped for Seq too new  for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;       i++) {    ASSERT_OK(Put("b", "f"));  }  iter->Prev();  ASSERT_EQ(IterStatus(iter), "a->b");  iter->Prev();  iter2->Prev();  ASSERT_EQ(IterStatus(iter2), "a->b");  iter2->Prev();  delete iter;  delete iter2;}TEST_P(DBIteratorTest, IterEmpty) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    Iterator* iter = NewIterator(ReadOptions(), handles_[1]);    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->Seek("foo");    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekForPrev("foo");    ASSERT_EQ(IterStatus(iter), "(invalid)");    delete iter;  } while (ChangeCompactOptions());}TEST_P(DBIteratorTest, IterSingle) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    ASSERT_OK(Put(1, "a", "va"));    Iterator* iter = NewIterator(ReadOptions(), handles_[1]);    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->Seek("");    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekForPrev("");    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->Seek("a");    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekForPrev("a");    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->Seek("b");    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekForPrev("b");    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    delete iter;  } while (ChangeCompactOptions());}TEST_P(DBIteratorTest, IterMulti) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    ASSERT_OK(Put(1, "a", "va"));    ASSERT_OK(Put(1, "b", "vb"));    ASSERT_OK(Put(1, "c", "vc"));    Iterator* iter = NewIterator(ReadOptions(), handles_[1]);    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->Next();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->Seek("");    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Seek("a");    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Seek("ax");    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->SeekForPrev("d");    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->SeekForPrev("c");    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->SeekForPrev("bx");    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->Seek("b");    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->Seek("z");    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekForPrev("b");    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->SeekForPrev("");    ASSERT_EQ(IterStatus(iter), "(invalid)");    // Switch from reverse to forward    iter->SeekToLast();    iter->Prev();    iter->Prev();    iter->Next();    ASSERT_EQ(IterStatus(iter), "b->vb");    // Switch from forward to reverse    iter->SeekToFirst();    iter->Next();    iter->Next();    iter->Prev();    ASSERT_EQ(IterStatus(iter), "b->vb");    // Make sure iter stays at snapshot    ASSERT_OK(Put(1, "a", "va2"));    ASSERT_OK(Put(1, "a2", "va3"));    ASSERT_OK(Put(1, "b", "vb2"));    ASSERT_OK(Put(1, "c", "vc2"));    ASSERT_OK(Delete(1, "b"));    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->Next();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "b->vb");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    delete iter;  } while (ChangeCompactOptions());}// Check that we can skip over a run of user keys// by using reseek rather than sequential scanTEST_P(DBIteratorTest, IterReseek) {  anon::OptionsOverride options_override;  options_override.skip_policy = kSkipNoSnapshot;  Options options = CurrentOptions(options_override);  options.max_sequential_skip_in_iterations = 3;  options.create_if_missing = true;  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  DestroyAndReopen(options);  CreateAndReopenWithCF({"pikachu"}, options);  // insert three keys with same userkey and verify that  // reseek is not invoked. For each of these test cases,  // verify that we can find the next key "b".  ASSERT_OK(Put(1, "a", "zero"));  ASSERT_OK(Put(1, "a", "one"));  ASSERT_OK(Put(1, "a", "two"));  ASSERT_OK(Put(1, "b", "bone"));  Iterator* iter = NewIterator(ReadOptions(), handles_[1]);  iter->SeekToFirst();  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);  ASSERT_EQ(IterStatus(iter), "a->two");  iter->Next();  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);  ASSERT_EQ(IterStatus(iter), "b->bone");  delete iter;  // insert a total of three keys with same userkey and verify  // that reseek is still not invoked.  ASSERT_OK(Put(1, "a", "three"));  iter = NewIterator(ReadOptions(), handles_[1]);  iter->SeekToFirst();  ASSERT_EQ(IterStatus(iter), "a->three");  iter->Next();  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);  ASSERT_EQ(IterStatus(iter), "b->bone");  delete iter;  // insert a total of four keys with same userkey and verify  // that reseek is invoked.  ASSERT_OK(Put(1, "a", "four"));  iter = NewIterator(ReadOptions(), handles_[1]);  iter->SeekToFirst();  ASSERT_EQ(IterStatus(iter), "a->four");  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);  iter->Next();  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 1);  ASSERT_EQ(IterStatus(iter), "b->bone");  delete iter;  // Testing reverse iterator  // At this point, we have three versions of "a" and one version of "b".  // The reseek statistics is already at 1.  int num_reseeks = static_cast<int>(      TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION));  // Insert another version of b and assert that reseek is not invoked  ASSERT_OK(Put(1, "b", "btwo"));  iter = NewIterator(ReadOptions(), handles_[1]);  iter->SeekToLast();  ASSERT_EQ(IterStatus(iter), "b->btwo");  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION),            num_reseeks);  iter->Prev();  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION),            num_reseeks + 1);  ASSERT_EQ(IterStatus(iter), "a->four");  delete iter;  // insert two more versions of b. This makes a total of 4 versions  // of b and 4 versions of a.  ASSERT_OK(Put(1, "b", "bthree"));  ASSERT_OK(Put(1, "b", "bfour"));  iter = NewIterator(ReadOptions(), handles_[1]);  iter->SeekToLast();  ASSERT_EQ(IterStatus(iter), "b->bfour");  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION),            num_reseeks + 2);  iter->Prev();  // the previous Prev call should have invoked reseek  ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION),            num_reseeks + 3);  ASSERT_EQ(IterStatus(iter), "a->four");  delete iter;}TEST_P(DBIteratorTest, IterSmallAndLargeMix) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    ASSERT_OK(Put(1, "a", "va"));    ASSERT_OK(Put(1, "b", std::string(100000, 'b')));    ASSERT_OK(Put(1, "c", "vc"));    ASSERT_OK(Put(1, "d", std::string(100000, 'd')));    ASSERT_OK(Put(1, "e", std::string(100000, 'e')));    Iterator* iter = NewIterator(ReadOptions(), handles_[1]);    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Next();    ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));    iter->Next();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Next();    ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));    iter->Next();    ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));    iter->Next();    ASSERT_EQ(IterStatus(iter), "(invalid)");    iter->SeekToLast();    ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));    iter->Prev();    ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));    iter->Prev();    ASSERT_EQ(IterStatus(iter), "c->vc");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));    iter->Prev();    ASSERT_EQ(IterStatus(iter), "a->va");    iter->Prev();    ASSERT_EQ(IterStatus(iter), "(invalid)");    delete iter;  } while (ChangeCompactOptions());}TEST_P(DBIteratorTest, IterMultiWithDelete) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    ASSERT_OK(Put(1, "ka", "va"));    ASSERT_OK(Put(1, "kb", "vb"));    ASSERT_OK(Put(1, "kc", "vc"));    ASSERT_OK(Delete(1, "kb"));    ASSERT_EQ("NOT_FOUND", Get(1, "kb"));    Iterator* iter = NewIterator(ReadOptions(), handles_[1]);    iter->Seek("kc");    ASSERT_EQ(IterStatus(iter), "kc->vc");    if (!CurrentOptions().merge_operator) {      // TODO: merge operator does not support backward iteration yet      if (kPlainTableAllBytesPrefix != option_config_ &&          kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&          kHashLinkList != option_config_ &&          kHashSkipList != option_config_) {  // doesn't support SeekToLast        iter->Prev();        ASSERT_EQ(IterStatus(iter), "ka->va");      }    }    delete iter;  } while (ChangeOptions());}TEST_P(DBIteratorTest, IterPrevMaxSkip) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    for (int i = 0; i < 2; i++) {      ASSERT_OK(Put(1, "key1", "v1"));      ASSERT_OK(Put(1, "key2", "v2"));      ASSERT_OK(Put(1, "key3", "v3"));      ASSERT_OK(Put(1, "key4", "v4"));      ASSERT_OK(Put(1, "key5", "v5"));    }    VerifyIterLast("key5->v5", 1);    ASSERT_OK(Delete(1, "key5"));    VerifyIterLast("key4->v4", 1);    ASSERT_OK(Delete(1, "key4"));    VerifyIterLast("key3->v3", 1);    ASSERT_OK(Delete(1, "key3"));    VerifyIterLast("key2->v2", 1);    ASSERT_OK(Delete(1, "key2"));    VerifyIterLast("key1->v1", 1);    ASSERT_OK(Delete(1, "key1"));    VerifyIterLast("(invalid)", 1);  } while (ChangeOptions(kSkipMergePut | kSkipNoSeekToLast));}TEST_P(DBIteratorTest, IterWithSnapshot) {  anon::OptionsOverride options_override;  options_override.skip_policy = kSkipNoSnapshot;  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions(options_override));    ASSERT_OK(Put(1, "key1", "val1"));    ASSERT_OK(Put(1, "key2", "val2"));    ASSERT_OK(Put(1, "key3", "val3"));    ASSERT_OK(Put(1, "key4", "val4"));    ASSERT_OK(Put(1, "key5", "val5"));    const Snapshot* snapshot = db_->GetSnapshot();    ReadOptions options;    options.snapshot = snapshot;    Iterator* iter = NewIterator(options, handles_[1]);    ASSERT_OK(Put(1, "key0", "val0"));    // Put more values after the snapshot    ASSERT_OK(Put(1, "key100", "val100"));    ASSERT_OK(Put(1, "key101", "val101"));    iter->Seek("key5");    ASSERT_EQ(IterStatus(iter), "key5->val5");    if (!CurrentOptions().merge_operator) {      // TODO: merge operator does not support backward iteration yet      if (kPlainTableAllBytesPrefix != option_config_ &&          kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&          kHashLinkList != option_config_ && kHashSkipList != option_config_) {        iter->Prev();        ASSERT_EQ(IterStatus(iter), "key4->val4");        iter->Prev();        ASSERT_EQ(IterStatus(iter), "key3->val3");        iter->Next();        ASSERT_EQ(IterStatus(iter), "key4->val4");        iter->Next();        ASSERT_EQ(IterStatus(iter), "key5->val5");      }      iter->Next();      ASSERT_TRUE(!iter->Valid());    }    if (!CurrentOptions().merge_operator) {      // TODO(gzh): merge operator does not support backward iteration yet      if (kPlainTableAllBytesPrefix != option_config_ &&          kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&          kHashLinkList != option_config_ && kHashSkipList != option_config_) {        iter->SeekForPrev("key1");        ASSERT_EQ(IterStatus(iter), "key1->val1");        iter->Next();        ASSERT_EQ(IterStatus(iter), "key2->val2");        iter->Next();        ASSERT_EQ(IterStatus(iter), "key3->val3");        iter->Prev();        ASSERT_EQ(IterStatus(iter), "key2->val2");        iter->Prev();        ASSERT_EQ(IterStatus(iter), "key1->val1");        iter->Prev();        ASSERT_TRUE(!iter->Valid());      }    }    db_->ReleaseSnapshot(snapshot);    delete iter;  } while (ChangeOptions());}TEST_P(DBIteratorTest, IteratorPinsRef) {  do {    CreateAndReopenWithCF({"pikachu"}, CurrentOptions());    Put(1, "foo", "hello");    // Get iterator that will yield the current contents of the DB.    Iterator* iter = NewIterator(ReadOptions(), handles_[1]);    // Write to force compactions    Put(1, "foo", "newvalue1");    for (int i = 0; i < 100; i++) {      // 100K values      ASSERT_OK(Put(1, Key(i), Key(i) + std::string(100000, 'v')));    }    Put(1, "foo", "newvalue2");    iter->SeekToFirst();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ("foo", iter->key().ToString());    ASSERT_EQ("hello", iter->value().ToString());    iter->Next();    ASSERT_TRUE(!iter->Valid());    delete iter;  } while (ChangeCompactOptions());}TEST_P(DBIteratorTest, IteratorDeleteAfterCfDelete) {  CreateAndReopenWithCF({"pikachu"}, CurrentOptions());  Put(1, "foo", "delete-cf-then-delete-iter");  Put(1, "hello", "value2");  ColumnFamilyHandle* cf = handles_[1];  ReadOptions ro;  auto* iter = db_->NewIterator(ro, cf);  iter->SeekToFirst();  ASSERT_EQ(IterStatus(iter), "foo->delete-cf-then-delete-iter");  // delete CF handle  db_->DestroyColumnFamilyHandle(cf);  handles_.erase(std::begin(handles_) + 1);  // delete Iterator after CF handle is deleted  iter->Next();  ASSERT_EQ(IterStatus(iter), "hello->value2");  delete iter;}TEST_P(DBIteratorTest, IteratorDeleteAfterCfDrop) {  CreateAndReopenWithCF({"pikachu"}, CurrentOptions());  Put(1, "foo", "drop-cf-then-delete-iter");  ReadOptions ro;  ColumnFamilyHandle* cf = handles_[1];  auto* iter = db_->NewIterator(ro, cf);  iter->SeekToFirst();  ASSERT_EQ(IterStatus(iter), "foo->drop-cf-then-delete-iter");  // drop and delete CF  db_->DropColumnFamily(cf);  db_->DestroyColumnFamilyHandle(cf);  handles_.erase(std::begin(handles_) + 1);  // delete Iterator after CF handle is dropped  delete iter;}// SetOptions not defined in ROCKSDB LITE#ifndef ROCKSDB_LITETEST_P(DBIteratorTest, DBIteratorBoundTest) {  Options options = CurrentOptions();  options.env = env_;  options.create_if_missing = true;  options.prefix_extractor = nullptr;  DestroyAndReopen(options);  ASSERT_OK(Put("a", "0"));  ASSERT_OK(Put("foo", "bar"));  ASSERT_OK(Put("foo1", "bar1"));  ASSERT_OK(Put("g1", "0"));  // testing basic case with no iterate_upper_bound and no prefix_extractor  {    ReadOptions ro;    ro.iterate_upper_bound = nullptr;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->Seek("foo");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo")), 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("g1")), 0);    iter->SeekForPrev("g1");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("g1")), 0);    iter->Prev();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);    iter->Prev();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo")), 0);  }  // testing iterate_upper_bound and forward iterator  // to make sure it stops at bound  {    ReadOptions ro;    // iterate_upper_bound points beyond the last expected entry    Slice prefix("foo2");    ro.iterate_upper_bound = &prefix;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->Seek("foo");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo")), 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(("foo1")), 0);    iter->Next();    // should stop here...    ASSERT_TRUE(!iter->Valid());  }  // Testing SeekToLast with iterate_upper_bound set  {    ReadOptions ro;    Slice prefix("foo");    ro.iterate_upper_bound = &prefix;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->SeekToLast();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("a")), 0);  }  // prefix is the first letter of the key  ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:1"}}));  ASSERT_OK(Put("a", "0"));  ASSERT_OK(Put("foo", "bar"));  ASSERT_OK(Put("foo1", "bar1"));  ASSERT_OK(Put("g1", "0"));  // testing with iterate_upper_bound and prefix_extractor  // Seek target and iterate_upper_bound are not is same prefix  // This should be an error  {    ReadOptions ro;    Slice upper_bound("g");    ro.iterate_upper_bound = &upper_bound;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->Seek("foo");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ("foo", iter->key().ToString());    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ("foo1", iter->key().ToString());    iter->Next();    ASSERT_TRUE(!iter->Valid());  }  // testing that iterate_upper_bound prevents iterating over deleted items  // if the bound has already reached  {    options.prefix_extractor = nullptr;    DestroyAndReopen(options);    ASSERT_OK(Put("a", "0"));    ASSERT_OK(Put("b", "0"));    ASSERT_OK(Put("b1", "0"));    ASSERT_OK(Put("c", "0"));    ASSERT_OK(Put("d", "0"));    ASSERT_OK(Put("e", "0"));    ASSERT_OK(Delete("c"));    ASSERT_OK(Delete("d"));    // base case with no bound    ReadOptions ro;    ro.iterate_upper_bound = nullptr;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->Seek("b");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("b")), 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(("b1")), 0);    get_perf_context()->Reset();    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 2);    // now testing with iterate_bound    Slice prefix("c");    ro.iterate_upper_bound = &prefix;    iter.reset(NewIterator(ro));    get_perf_context()->Reset();    iter->Seek("b");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("b")), 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(("b1")), 0);    iter->Next();    // the iteration should stop as soon as the bound key is reached    // even though the key is deleted    // hence internal_delete_skipped_count should be 0    ASSERT_TRUE(!iter->Valid());    ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);  }}TEST_P(DBIteratorTest, DBIteratorBoundMultiSeek) {  Options options = CurrentOptions();  options.env = env_;  options.create_if_missing = true;  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  options.prefix_extractor = nullptr;  DestroyAndReopen(options);  ASSERT_OK(Put("a", "0"));  ASSERT_OK(Put("z", "0"));  ASSERT_OK(Flush());  ASSERT_OK(Put("foo1", "bar1"));  ASSERT_OK(Put("foo2", "bar2"));  ASSERT_OK(Put("foo3", "bar3"));  ASSERT_OK(Put("foo4", "bar4"));  {    std::string up_str = "foo5";    Slice up(up_str);    ReadOptions ro;    ro.iterate_upper_bound = &up;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->Seek("foo1");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);    uint64_t prev_block_cache_hit =        TestGetTickerCount(options, BLOCK_CACHE_HIT);    uint64_t prev_block_cache_miss =        TestGetTickerCount(options, BLOCK_CACHE_MISS);    ASSERT_GT(prev_block_cache_hit + prev_block_cache_miss, 0);    iter->Seek("foo4");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo4")), 0);    ASSERT_EQ(prev_block_cache_hit,              TestGetTickerCount(options, BLOCK_CACHE_HIT));    ASSERT_EQ(prev_block_cache_miss,              TestGetTickerCount(options, BLOCK_CACHE_MISS));    iter->Seek("foo2");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo2")), 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo3")), 0);    ASSERT_EQ(prev_block_cache_hit,              TestGetTickerCount(options, BLOCK_CACHE_HIT));    ASSERT_EQ(prev_block_cache_miss,              TestGetTickerCount(options, BLOCK_CACHE_MISS));  }}#endifTEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {  for (auto format_version : {2, 3, 4}) {    int upper_bound_hits = 0;    Options options = CurrentOptions();    ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(        "BlockBasedTableIterator:out_of_bound",        [&upper_bound_hits](void*) { upper_bound_hits++; });    ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();    options.env = env_;    options.create_if_missing = true;    options.prefix_extractor = nullptr;    BlockBasedTableOptions table_options;    table_options.format_version = format_version;    table_options.flush_block_policy_factory =        std::make_shared<FlushBlockEveryKeyPolicyFactory>();    options.table_factory.reset(NewBlockBasedTableFactory(table_options));    DestroyAndReopen(options);    ASSERT_OK(Put("foo1", "bar1"));    ASSERT_OK(Put("foo2", "bar2"));    ASSERT_OK(Put("foo4", "bar4"));    ASSERT_OK(Flush());    Slice ub("foo3");    ReadOptions ro;    ro.iterate_upper_bound = &ub;    std::unique_ptr<Iterator> iter(NewIterator(ro));    iter->Seek("foo");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);    ASSERT_EQ(upper_bound_hits, 0);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(iter->key().compare(Slice("foo2")), 0);    ASSERT_EQ(upper_bound_hits, 0);    iter->Next();    ASSERT_FALSE(iter->Valid());    ASSERT_EQ(upper_bound_hits, 1);  }}// Enable kBinarySearchWithFirstKey, do some iterator operations and check that// they don't do unnecessary block reads.TEST_P(DBIteratorTest, IndexWithFirstKey) {  for (int tailing = 0; tailing < 2; ++tailing) {    SCOPED_TRACE("tailing = " + std::to_string(tailing));    Options options = CurrentOptions();    options.env = env_;    options.create_if_missing = true;    options.prefix_extractor = nullptr;    options.merge_operator = MergeOperators::CreateStringAppendOperator();    options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();    Statistics* stats = options.statistics.get();    BlockBasedTableOptions table_options;    table_options.index_type =        BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;    table_options.index_shortening =        BlockBasedTableOptions::IndexShorteningMode::kNoShortening;    table_options.flush_block_policy_factory =        std::make_shared<FlushBlockEveryKeyPolicyFactory>();    table_options.block_cache =        NewLRUCache(8000);  // fits all blocks and their cache metadata overhead    options.table_factory.reset(NewBlockBasedTableFactory(table_options));    DestroyAndReopen(options);    ASSERT_OK(Merge("a1", "x1"));    ASSERT_OK(Merge("b1", "y1"));    ASSERT_OK(Merge("c0", "z1"));    ASSERT_OK(Flush());    ASSERT_OK(Merge("a2", "x2"));    ASSERT_OK(Merge("b2", "y2"));    ASSERT_OK(Merge("c0", "z2"));    ASSERT_OK(Flush());    ASSERT_OK(Merge("a3", "x3"));    ASSERT_OK(Merge("b3", "y3"));    ASSERT_OK(Merge("c3", "z3"));    ASSERT_OK(Flush());    // Block cache is not important for this test.    // We use BLOCK_CACHE_DATA_* counters just because they're the most readily    // available way of counting block accesses.    ReadOptions ropt;    ropt.tailing = tailing;    std::unique_ptr<Iterator> iter(NewIterator(ropt));    iter->Seek("b10");    ASSERT_TRUE(iter->Valid());    EXPECT_EQ("b2", iter->key().ToString());    EXPECT_EQ("y2", iter->value().ToString());    EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));    iter->Next();    ASSERT_TRUE(iter->Valid());    EXPECT_EQ("b3", iter->key().ToString());    EXPECT_EQ("y3", iter->value().ToString());    EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));    EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));    iter->Seek("c0");    ASSERT_TRUE(iter->Valid());    EXPECT_EQ("c0", iter->key().ToString());    EXPECT_EQ("z1,z2", iter->value().ToString());    EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));    EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));    iter->Next();    ASSERT_TRUE(iter->Valid());    EXPECT_EQ("c3", iter->key().ToString());    EXPECT_EQ("z3", iter->value().ToString());    EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));    EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));    iter.reset();    // Enable iterate_upper_bound and check that iterator is not trying to read    // blocks that are fully above upper bound.    std::string ub = "b3";    Slice ub_slice(ub);    ropt.iterate_upper_bound = &ub_slice;    iter.reset(NewIterator(ropt));    iter->Seek("b2");    ASSERT_TRUE(iter->Valid());    EXPECT_EQ("b2", iter->key().ToString());    EXPECT_EQ("y2", iter->value().ToString());    EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));    EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));    iter->Next();    ASSERT_FALSE(iter->Valid());    EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));    EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));  }}TEST_P(DBIteratorTest, IndexWithFirstKeyGet) {  Options options = CurrentOptions();  options.env = env_;  options.create_if_missing = true;  options.prefix_extractor = nullptr;  options.merge_operator = MergeOperators::CreateStringAppendOperator();  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  Statistics* stats = options.statistics.get();  BlockBasedTableOptions table_options;  table_options.index_type =      BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;  table_options.index_shortening =      BlockBasedTableOptions::IndexShorteningMode::kNoShortening;  table_options.flush_block_policy_factory =      std::make_shared<FlushBlockEveryKeyPolicyFactory>();  table_options.block_cache = NewLRUCache(1000);  // fits all blocks  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  DestroyAndReopen(options);  ASSERT_OK(Merge("a", "x1"));  ASSERT_OK(Merge("c", "y1"));  ASSERT_OK(Merge("e", "z1"));  ASSERT_OK(Flush());  ASSERT_OK(Merge("c", "y2"));  ASSERT_OK(Merge("e", "z2"));  ASSERT_OK(Flush());  // Get() between blocks shouldn't read any blocks.  ASSERT_EQ("NOT_FOUND", Get("b"));  EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));  EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));  // Get() of an existing key shouldn't read any unnecessary blocks when there's  // only one key per block.  ASSERT_EQ("y1,y2", Get("c"));  EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));  EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));  ASSERT_EQ("x1", Get("a"));  EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));  EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));  EXPECT_EQ(std::vector<std::string>({"NOT_FOUND", "z1,z2"}),            MultiGet({"b", "e"}));}// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary//             return the biggest key which is smaller than the seek key.TEST_P(DBIteratorTest, PrevAfterAndNextAfterMerge) {  Options options;  options.create_if_missing = true;  options.merge_operator = MergeOperators::CreatePutOperator();  options.env = env_;  DestroyAndReopen(options);  // write three entries with different keys using Merge()  WriteOptions wopts;  db_->Merge(wopts, "1", "data1");  db_->Merge(wopts, "2", "data2");  db_->Merge(wopts, "3", "data3");  std::unique_ptr<Iterator> it(NewIterator(ReadOptions()));  it->Seek("2");  ASSERT_TRUE(it->Valid());  ASSERT_EQ("2", it->key().ToString());  it->Prev();  ASSERT_TRUE(it->Valid());  ASSERT_EQ("1", it->key().ToString());  it->SeekForPrev("1");  ASSERT_TRUE(it->Valid());  ASSERT_EQ("1", it->key().ToString());  it->Next();  ASSERT_TRUE(it->Valid());  ASSERT_EQ("2", it->key().ToString());}class DBIteratorTestForPinnedData : public DBIteratorTest { public:  enum TestConfig {    NORMAL,    CLOSE_AND_OPEN,    COMPACT_BEFORE_READ,    FLUSH_EVERY_1000,    MAX  };  DBIteratorTestForPinnedData() : DBIteratorTest() {}  void PinnedDataIteratorRandomized(TestConfig run_config) {    // Generate Random data    Random rnd(301);    int puts = 100000;    int key_pool = static_cast<int>(puts * 0.7);    int key_size = 100;    int val_size = 1000;    int seeks_percentage = 20;   // 20% of keys will be used to test seek()    int delete_percentage = 20;  // 20% of keys will be deleted    int merge_percentage = 20;   // 20% of keys will be added using Merge()    Options options = CurrentOptions();    BlockBasedTableOptions table_options;    table_options.use_delta_encoding = false;    options.table_factory.reset(NewBlockBasedTableFactory(table_options));    options.merge_operator = MergeOperators::CreatePutOperator();    DestroyAndReopen(options);    std::vector<std::string> generated_keys(key_pool);    for (int i = 0; i < key_pool; i++) {      generated_keys[i] = RandomString(&rnd, key_size);    }    std::map<std::string, std::string> true_data;    std::vector<std::string> random_keys;    std::vector<std::string> deleted_keys;    for (int i = 0; i < puts; i++) {      auto& k = generated_keys[rnd.Next() % key_pool];      auto v = RandomString(&rnd, val_size);      // Insert data to true_data map and to DB      true_data[k] = v;      if (rnd.PercentTrue(merge_percentage)) {        ASSERT_OK(db_->Merge(WriteOptions(), k, v));      } else {        ASSERT_OK(Put(k, v));      }      // Pick random keys to be used to test Seek()      if (rnd.PercentTrue(seeks_percentage)) {        random_keys.push_back(k);      }      // Delete some random keys      if (rnd.PercentTrue(delete_percentage)) {        deleted_keys.push_back(k);        true_data.erase(k);        ASSERT_OK(Delete(k));      }      if (run_config == TestConfig::FLUSH_EVERY_1000) {        if (i && i % 1000 == 0) {          Flush();        }      }    }    if (run_config == TestConfig::CLOSE_AND_OPEN) {      Close();      Reopen(options);    } else if (run_config == TestConfig::COMPACT_BEFORE_READ) {      db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);    }    ReadOptions ro;    ro.pin_data = true;    auto iter = NewIterator(ro);    {      // Test Seek to random keys      std::vector<Slice> keys_slices;      std::vector<std::string> true_keys;      for (auto& k : random_keys) {        iter->Seek(k);        if (!iter->Valid()) {          ASSERT_EQ(true_data.lower_bound(k), true_data.end());          continue;        }        std::string prop_value;        ASSERT_OK(            iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));        ASSERT_EQ("1", prop_value);        keys_slices.push_back(iter->key());        true_keys.push_back(true_data.lower_bound(k)->first);      }      for (size_t i = 0; i < keys_slices.size(); i++) {        ASSERT_EQ(keys_slices[i].ToString(), true_keys[i]);      }    }    {      // Test SeekForPrev to random keys      std::vector<Slice> keys_slices;      std::vector<std::string> true_keys;      for (auto& k : random_keys) {        iter->SeekForPrev(k);        if (!iter->Valid()) {          ASSERT_EQ(true_data.upper_bound(k), true_data.begin());          continue;        }        std::string prop_value;        ASSERT_OK(            iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));        ASSERT_EQ("1", prop_value);        keys_slices.push_back(iter->key());        true_keys.push_back((--true_data.upper_bound(k))->first);      }      for (size_t i = 0; i < keys_slices.size(); i++) {        ASSERT_EQ(keys_slices[i].ToString(), true_keys[i]);      }    }    {      // Test iterating all data forward      std::vector<Slice> all_keys;      for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {        std::string prop_value;        ASSERT_OK(            iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));        ASSERT_EQ("1", prop_value);        all_keys.push_back(iter->key());      }      ASSERT_EQ(all_keys.size(), true_data.size());      // Verify that all keys slices are valid      auto data_iter = true_data.begin();      for (size_t i = 0; i < all_keys.size(); i++) {        ASSERT_EQ(all_keys[i].ToString(), data_iter->first);        data_iter++;      }    }    {      // Test iterating all data backward      std::vector<Slice> all_keys;      for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {        std::string prop_value;        ASSERT_OK(            iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));        ASSERT_EQ("1", prop_value);        all_keys.push_back(iter->key());      }      ASSERT_EQ(all_keys.size(), true_data.size());      // Verify that all keys slices are valid (backward)      auto data_iter = true_data.rbegin();      for (size_t i = 0; i < all_keys.size(); i++) {        ASSERT_EQ(all_keys[i].ToString(), data_iter->first);        data_iter++;      }    }    delete iter;}};TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedNormal) {  PinnedDataIteratorRandomized(TestConfig::NORMAL);}TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedCLoseAndOpen) {  PinnedDataIteratorRandomized(TestConfig::CLOSE_AND_OPEN);}TEST_P(DBIteratorTestForPinnedData,       PinnedDataIteratorRandomizedCompactBeforeRead) {  PinnedDataIteratorRandomized(TestConfig::COMPACT_BEFORE_READ);}TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedFlush) {  PinnedDataIteratorRandomized(TestConfig::FLUSH_EVERY_1000);}#ifndef ROCKSDB_LITETEST_P(DBIteratorTest, PinnedDataIteratorMultipleFiles) {  Options options = CurrentOptions();  BlockBasedTableOptions table_options;  table_options.use_delta_encoding = false;  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  options.disable_auto_compactions = true;  options.write_buffer_size = 1024 * 1024 * 10;  // 10 Mb  DestroyAndReopen(options);  std::map<std::string, std::string> true_data;  // Generate 4 sst files in L2  Random rnd(301);  for (int i = 1; i <= 1000; i++) {    std::string k = Key(i * 3);    std::string v = RandomString(&rnd, 100);    ASSERT_OK(Put(k, v));    true_data[k] = v;    if (i % 250 == 0) {      ASSERT_OK(Flush());    }  }  ASSERT_EQ(FilesPerLevel(0), "4");  ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));  ASSERT_EQ(FilesPerLevel(0), "0,4");  // Generate 4 sst files in L0  for (int i = 1; i <= 1000; i++) {    std::string k = Key(i * 2);    std::string v = RandomString(&rnd, 100);    ASSERT_OK(Put(k, v));    true_data[k] = v;    if (i % 250 == 0) {      ASSERT_OK(Flush());    }  }  ASSERT_EQ(FilesPerLevel(0), "4,4");  // Add some keys/values in memtables  for (int i = 1; i <= 1000; i++) {    std::string k = Key(i);    std::string v = RandomString(&rnd, 100);    ASSERT_OK(Put(k, v));    true_data[k] = v;  }  ASSERT_EQ(FilesPerLevel(0), "4,4");  ReadOptions ro;  ro.pin_data = true;  auto iter = NewIterator(ro);  std::vector<std::pair<Slice, std::string>> results;  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {    std::string prop_value;    ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));    ASSERT_EQ("1", prop_value);    results.emplace_back(iter->key(), iter->value().ToString());  }  ASSERT_EQ(results.size(), true_data.size());  auto data_iter = true_data.begin();  for (size_t i = 0; i < results.size(); i++, data_iter++) {    auto& kv = results[i];    ASSERT_EQ(kv.first, data_iter->first);    ASSERT_EQ(kv.second, data_iter->second);  }  delete iter;}#endifTEST_P(DBIteratorTest, PinnedDataIteratorMergeOperator) {  Options options = CurrentOptions();  BlockBasedTableOptions table_options;  table_options.use_delta_encoding = false;  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  options.merge_operator = MergeOperators::CreateUInt64AddOperator();  DestroyAndReopen(options);  std::string numbers[7];  for (int val = 0; val <= 6; val++) {    PutFixed64(numbers + val, val);  }  // +1 all keys in range [ 0 => 999]  for (int i = 0; i < 1000; i++) {    WriteOptions wo;    ASSERT_OK(db_->Merge(wo, Key(i), numbers[1]));  }  // +2 all keys divisible by 2 in range [ 0 => 999]  for (int i = 0; i < 1000; i += 2) {    WriteOptions wo;    ASSERT_OK(db_->Merge(wo, Key(i), numbers[2]));  }  // +3 all keys divisible by 5 in range [ 0 => 999]  for (int i = 0; i < 1000; i += 5) {    WriteOptions wo;    ASSERT_OK(db_->Merge(wo, Key(i), numbers[3]));  }  ReadOptions ro;  ro.pin_data = true;  auto iter = NewIterator(ro);  std::vector<std::pair<Slice, std::string>> results;  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {    std::string prop_value;    ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));    ASSERT_EQ("1", prop_value);    results.emplace_back(iter->key(), iter->value().ToString());  }  ASSERT_EQ(results.size(), 1000);  for (size_t i = 0; i < results.size(); i++) {    auto& kv = results[i];    ASSERT_EQ(kv.first, Key(static_cast<int>(i)));    int expected_val = 1;    if (i % 2 == 0) {      expected_val += 2;    }    if (i % 5 == 0) {      expected_val += 3;    }    ASSERT_EQ(kv.second, numbers[expected_val]);  }  delete iter;}TEST_P(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {  Options options = CurrentOptions();  BlockBasedTableOptions table_options;  table_options.use_delta_encoding = false;  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  options.write_buffer_size = 100000;  DestroyAndReopen(options);  Random rnd(301);  std::map<std::string, std::string> true_data;  for (int i = 0; i < 1000; i++) {    std::string k = RandomString(&rnd, 10);    std::string v = RandomString(&rnd, 1000);    ASSERT_OK(Put(k, v));    true_data[k] = v;  }  ReadOptions ro;  ro.pin_data = true;  auto iter = NewIterator(ro);  // Delete 50% of the keys and update the other 50%  for (auto& kv : true_data) {    if (rnd.OneIn(2)) {      ASSERT_OK(Delete(kv.first));    } else {      std::string new_val = RandomString(&rnd, 1000);      ASSERT_OK(Put(kv.first, new_val));    }  }  std::vector<std::pair<Slice, std::string>> results;  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {    std::string prop_value;    ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));    ASSERT_EQ("1", prop_value);    results.emplace_back(iter->key(), iter->value().ToString());  }  auto data_iter = true_data.begin();  for (size_t i = 0; i < results.size(); i++, data_iter++) {    auto& kv = results[i];    ASSERT_EQ(kv.first, data_iter->first);    ASSERT_EQ(kv.second, data_iter->second);  }  delete iter;}class SliceTransformLimitedDomainGeneric : public SliceTransform {  const char* Name() const override {    return "SliceTransformLimitedDomainGeneric";  }  Slice Transform(const Slice& src) const override {    return Slice(src.data(), 1);  }  bool InDomain(const Slice& src) const override {    // prefix will be x????    return src.size() >= 1;  }  bool InRange(const Slice& dst) const override {    // prefix will be x????    return dst.size() == 1;  }};TEST_P(DBIteratorTest, IterSeekForPrevCrossingFiles) {  Options options = CurrentOptions();  options.prefix_extractor.reset(NewFixedPrefixTransform(1));  options.disable_auto_compactions = true;  // Enable prefix bloom for SST files  BlockBasedTableOptions table_options;  table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  DestroyAndReopen(options);  ASSERT_OK(Put("a1", "va1"));  ASSERT_OK(Put("a2", "va2"));  ASSERT_OK(Put("a3", "va3"));  ASSERT_OK(Flush());  ASSERT_OK(Put("b1", "vb1"));  ASSERT_OK(Put("b2", "vb2"));  ASSERT_OK(Put("b3", "vb3"));  ASSERT_OK(Flush());  ASSERT_OK(Put("b4", "vb4"));  ASSERT_OK(Put("d1", "vd1"));  ASSERT_OK(Put("d2", "vd2"));  ASSERT_OK(Put("d4", "vd4"));  ASSERT_OK(Flush());  MoveFilesToLevel(1);  {    ReadOptions ro;    Iterator* iter = NewIterator(ro);    iter->SeekForPrev("a4");    ASSERT_EQ(iter->key().ToString(), "a3");    ASSERT_EQ(iter->value().ToString(), "va3");    iter->SeekForPrev("c2");    ASSERT_EQ(iter->key().ToString(), "b3");    iter->SeekForPrev("d3");    ASSERT_EQ(iter->key().ToString(), "d2");    iter->SeekForPrev("b5");    ASSERT_EQ(iter->key().ToString(), "b4");    delete iter;  }  {    ReadOptions ro;    ro.prefix_same_as_start = true;    Iterator* iter = NewIterator(ro);    iter->SeekForPrev("c2");    ASSERT_TRUE(!iter->Valid());    delete iter;  }}TEST_P(DBIteratorTest, IterSeekForPrevCrossingFilesCustomPrefixExtractor) {  Options options = CurrentOptions();  options.prefix_extractor =      std::make_shared<SliceTransformLimitedDomainGeneric>();  options.disable_auto_compactions = true;  // Enable prefix bloom for SST files  BlockBasedTableOptions table_options;  table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  DestroyAndReopen(options);  ASSERT_OK(Put("a1", "va1"));  ASSERT_OK(Put("a2", "va2"));  ASSERT_OK(Put("a3", "va3"));  ASSERT_OK(Flush());  ASSERT_OK(Put("b1", "vb1"));  ASSERT_OK(Put("b2", "vb2"));  ASSERT_OK(Put("b3", "vb3"));  ASSERT_OK(Flush());  ASSERT_OK(Put("b4", "vb4"));  ASSERT_OK(Put("d1", "vd1"));  ASSERT_OK(Put("d2", "vd2"));  ASSERT_OK(Put("d4", "vd4"));  ASSERT_OK(Flush());  MoveFilesToLevel(1);  {    ReadOptions ro;    Iterator* iter = NewIterator(ro);    iter->SeekForPrev("a4");    ASSERT_EQ(iter->key().ToString(), "a3");    ASSERT_EQ(iter->value().ToString(), "va3");    iter->SeekForPrev("c2");    ASSERT_EQ(iter->key().ToString(), "b3");    iter->SeekForPrev("d3");    ASSERT_EQ(iter->key().ToString(), "d2");    iter->SeekForPrev("b5");    ASSERT_EQ(iter->key().ToString(), "b4");    delete iter;  }  {    ReadOptions ro;    ro.prefix_same_as_start = true;    Iterator* iter = NewIterator(ro);    iter->SeekForPrev("c2");    ASSERT_TRUE(!iter->Valid());    delete iter;  }}TEST_P(DBIteratorTest, IterPrevKeyCrossingBlocks) {  Options options = CurrentOptions();  BlockBasedTableOptions table_options;  table_options.block_size = 1;  // every block will contain one entry  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();  options.disable_auto_compactions = true;  options.max_sequential_skip_in_iterations = 8;  DestroyAndReopen(options);  // Putting such deletes will force DBIter::Prev() to fallback to a Seek  for (int file_num = 0; file_num < 10; file_num++) {    ASSERT_OK(Delete("key4"));    ASSERT_OK(Flush());  }  // First File containing 5 blocks of puts  ASSERT_OK(Put("key1", "val1.0"));  ASSERT_OK(Put("key2", "val2.0"));  ASSERT_OK(Put("key3", "val3.0"));  ASSERT_OK(Put("key4", "val4.0"));  ASSERT_OK(Put("key5", "val5.0"));  ASSERT_OK(Flush());  // Second file containing 9 blocks of merge operands  ASSERT_OK(db_->Merge(WriteOptions(), "key1", "val1.1"));  ASSERT_OK(db_->Merge(WriteOptions(), "key1", "val1.2"));  ASSERT_OK(db_->Merge(WriteOptions(), "key2", "val2.1"));  ASSERT_OK(db_->Merge(WriteOptions(), "key2", "val2.2"));  ASSERT_OK(db_->Merge(WriteOptions(), "key2", "val2.3"));  ASSERT_OK(db_->Merge(WriteOptions(), "key3", "val3.1"));  ASSERT_OK(db_->Merge(WriteOptions(), "key3", "val3.2"));  ASSERT_OK(db_->Merge(WriteOptions(), "key3", "val3.3"));  ASSERT_OK(db_->Merge(WriteOptions(), "key3", "val3.4"));  ASSERT_OK(Flush());  {    ReadOptions ro;    ro.fill_cache = false;    Iterator* iter = NewIterator(ro);    iter->SeekToLast();    ASSERT_EQ(iter->key().ToString(), "key5");    ASSERT_EQ(iter->value().ToString(), "val5.0");    iter->Prev();    ASSERT_EQ(iter->key().ToString(), "key4");    ASSERT_EQ(iter->value().ToString(), "val4.0");    iter->Prev();    ASSERT_EQ(iter->key().ToString(), "key3");    ASSERT_EQ(iter->value().ToString(), "val3.0,val3.1,val3.2,val3.3,val3.4");    iter->Prev();    ASSERT_EQ(iter->key().ToString(), "key2");    ASSERT_EQ(iter->value().ToString(), "val2.0,val2.1,val2.2,val2.3");    iter->Prev();    ASSERT_EQ(iter->key().ToString(), "key1");    ASSERT_EQ(iter->value().ToString(), "val1.0,val1.1,val1.2");    delete iter;  }}TEST_P(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {  Options options = CurrentOptions();  options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();  options.disable_auto_compactions = true;  options.level0_slowdown_writes_trigger = (1 << 30);  options.level0_stop_writes_trigger = (1 << 30);  options.max_sequential_skip_in_iterations = 8;  DestroyAndReopen(options);  const int kNumKeys = 500;  // Small number of merge operands to make sure that DBIter::Prev() dont  // fall back to Seek()  const int kNumMergeOperands = 3;  // Use value size that will make sure that every block contain 1 key  const int kValSize =      static_cast<int>(BlockBasedTableOptions().block_size) * 4;  // Percentage of keys that wont get merge operations  const int kNoMergeOpPercentage = 20;  // Percentage of keys that will be deleted  const int kDeletePercentage = 10;  // For half of the key range we will write multiple deletes first to  // force DBIter::Prev() to fall back to Seek()  for (int file_num = 0; file_num < 10; file_num++) {    for (int i = 0; i < kNumKeys; i += 2) {      ASSERT_OK(Delete(Key(i)));    }    ASSERT_OK(Flush());  }  Random rnd(301);  std::map<std::string, std::string> true_data;  std::string gen_key;  std::string gen_val;  for (int i = 0; i < kNumKeys; i++) {    gen_key = Key(i);    gen_val = RandomString(&rnd, kValSize);    ASSERT_OK(Put(gen_key, gen_val));    true_data[gen_key] = gen_val;  }  ASSERT_OK(Flush());  // Separate values and merge operands in different file so that we  // make sure that we dont merge them while flushing but actually  // merge them in the read path  for (int i = 0; i < kNumKeys; i++) {    if (rnd.PercentTrue(kNoMergeOpPercentage)) {      // Dont give merge operations for some keys      continue;    }    for (int j = 0; j < kNumMergeOperands; j++) {      gen_key = Key(i);      gen_val = RandomString(&rnd, kValSize);      ASSERT_OK(db_->Merge(WriteOptions(), gen_key, gen_val));      true_data[gen_key] += "," + gen_val;    }  }  ASSERT_OK(Flush());  for (int i = 0; i < kNumKeys; i++) {    if (rnd.PercentTrue(kDeletePercentage)) {      gen_key = Key(i);      ASSERT_OK(Delete(gen_key));      true_data.erase(gen_key);    }  }  ASSERT_OK(Flush());  {    ReadOptions ro;    ro.fill_cache = false;    Iterator* iter = NewIterator(ro);    auto data_iter = true_data.rbegin();    for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {      ASSERT_EQ(iter->key().ToString(), data_iter->first);      ASSERT_EQ(iter->value().ToString(), data_iter->second);      data_iter++;    }    ASSERT_EQ(data_iter, true_data.rend());    delete iter;  }  {    ReadOptions ro;    ro.fill_cache = false;    Iterator* iter = NewIterator(ro);    auto data_iter = true_data.rbegin();    int entries_right = 0;    std::string seek_key;    for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {      // Verify key/value of current position      ASSERT_EQ(iter->key().ToString(), data_iter->first);      ASSERT_EQ(iter->value().ToString(), data_iter->second);      bool restore_position_with_seek = rnd.Uniform(2);      if (restore_position_with_seek) {        seek_key = iter->key().ToString();      }      // Do some Next() operations the restore the iterator to orignal position      int next_count =          entries_right > 0 ? rnd.Uniform(std::min(entries_right, 10)) : 0;      for (int i = 0; i < next_count; i++) {        iter->Next();        data_iter--;        ASSERT_EQ(iter->key().ToString(), data_iter->first);        ASSERT_EQ(iter->value().ToString(), data_iter->second);      }      if (restore_position_with_seek) {        // Restore orignal position using Seek()        iter->Seek(seek_key);        for (int i = 0; i < next_count; i++) {          data_iter++;        }        ASSERT_EQ(iter->key().ToString(), data_iter->first);        ASSERT_EQ(iter->value().ToString(), data_iter->second);      } else {        // Restore original position using Prev()        for (int i = 0; i < next_count; i++) {          iter->Prev();          data_iter++;          ASSERT_EQ(iter->key().ToString(), data_iter->first);          ASSERT_EQ(iter->value().ToString(), data_iter->second);        }      }      entries_right++;      data_iter++;    }    ASSERT_EQ(data_iter, true_data.rend());    delete iter;  }}TEST_P(DBIteratorTest, IteratorWithLocalStatistics) {  Options options = CurrentOptions();  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  DestroyAndReopen(options);  Random rnd(301);  for (int i = 0; i < 1000; i++) {    // Key 10 bytes / Value 10 bytes    ASSERT_OK(Put(RandomString(&rnd, 10), RandomString(&rnd, 10)));  }  std::atomic<uint64_t> total_next(0);  std::atomic<uint64_t> total_next_found(0);  std::atomic<uint64_t> total_prev(0);  std::atomic<uint64_t> total_prev_found(0);  std::atomic<uint64_t> total_bytes(0);  std::vector<port::Thread> threads;  std::function<void()> reader_func_next = [&]() {    SetPerfLevel(kEnableCount);    get_perf_context()->Reset();    Iterator* iter = NewIterator(ReadOptions());    iter->SeekToFirst();    // Seek will bump ITER_BYTES_READ    uint64_t bytes = 0;    bytes += iter->key().size();    bytes += iter->value().size();    while (true) {      iter->Next();      total_next++;      if (!iter->Valid()) {        break;      }      total_next_found++;      bytes += iter->key().size();      bytes += iter->value().size();    }    delete iter;    ASSERT_EQ(bytes, get_perf_context()->iter_read_bytes);    SetPerfLevel(kDisable);    total_bytes += bytes;  };  std::function<void()> reader_func_prev = [&]() {    SetPerfLevel(kEnableCount);    Iterator* iter = NewIterator(ReadOptions());    iter->SeekToLast();    // Seek will bump ITER_BYTES_READ    uint64_t bytes = 0;    bytes += iter->key().size();    bytes += iter->value().size();    while (true) {      iter->Prev();      total_prev++;      if (!iter->Valid()) {        break;      }      total_prev_found++;      bytes += iter->key().size();      bytes += iter->value().size();    }    delete iter;    ASSERT_EQ(bytes, get_perf_context()->iter_read_bytes);    SetPerfLevel(kDisable);    total_bytes += bytes;  };  for (int i = 0; i < 10; i++) {    threads.emplace_back(reader_func_next);  }  for (int i = 0; i < 15; i++) {    threads.emplace_back(reader_func_prev);  }  for (auto& t : threads) {    t.join();  }  ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_NEXT), (uint64_t)total_next);  ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_NEXT_FOUND),            (uint64_t)total_next_found);  ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV), (uint64_t)total_prev);  ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV_FOUND),            (uint64_t)total_prev_found);  ASSERT_EQ(TestGetTickerCount(options, ITER_BYTES_READ), (uint64_t)total_bytes);}TEST_P(DBIteratorTest, ReadAhead) {  Options options;  env_->count_random_reads_ = true;  options.env = env_;  options.disable_auto_compactions = true;  options.write_buffer_size = 4 << 20;  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  BlockBasedTableOptions table_options;  table_options.block_size = 1024;  table_options.no_block_cache = true;  options.table_factory.reset(new BlockBasedTableFactory(table_options));  Reopen(options);  std::string value(1024, 'a');  for (int i = 0; i < 100; i++) {    Put(Key(i), value);  }  ASSERT_OK(Flush());  MoveFilesToLevel(2);  for (int i = 0; i < 100; i++) {    Put(Key(i), value);  }  ASSERT_OK(Flush());  MoveFilesToLevel(1);  for (int i = 0; i < 100; i++) {    Put(Key(i), value);  }  ASSERT_OK(Flush());#ifndef ROCKSDB_LITE  ASSERT_EQ("1,1,1", FilesPerLevel());#endif  // !ROCKSDB_LITE  env_->random_read_bytes_counter_ = 0;  options.statistics->setTickerCount(NO_FILE_OPENS, 0);  ReadOptions read_options;  auto* iter = NewIterator(read_options);  iter->SeekToFirst();  int64_t num_file_opens = TestGetTickerCount(options, NO_FILE_OPENS);  size_t bytes_read = env_->random_read_bytes_counter_;  delete iter;  int64_t num_file_closes = TestGetTickerCount(options, NO_FILE_CLOSES);  env_->random_read_bytes_counter_ = 0;  options.statistics->setTickerCount(NO_FILE_OPENS, 0);  read_options.readahead_size = 1024 * 10;  iter = NewIterator(read_options);  iter->SeekToFirst();  int64_t num_file_opens_readahead = TestGetTickerCount(options, NO_FILE_OPENS);  size_t bytes_read_readahead = env_->random_read_bytes_counter_;  delete iter;  int64_t num_file_closes_readahead =      TestGetTickerCount(options, NO_FILE_CLOSES);  ASSERT_EQ(num_file_opens, num_file_opens_readahead);  ASSERT_EQ(num_file_closes, num_file_closes_readahead);  ASSERT_GT(bytes_read_readahead, bytes_read);  ASSERT_GT(bytes_read_readahead, read_options.readahead_size * 3);  // Verify correctness.  iter = NewIterator(read_options);  int count = 0;  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {    ASSERT_EQ(value, iter->value());    count++;  }  ASSERT_EQ(100, count);  for (int i = 0; i < 100; i++) {    iter->Seek(Key(i));    ASSERT_EQ(value, iter->value());  }  delete iter;}// Insert a key, create a snapshot iterator, overwrite key lots of times,// seek to a smaller key. Expect DBIter to fall back to a seek instead of// going through all the overwrites linearly.TEST_P(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {  Options options = CurrentOptions();  options.env = env_;  options.create_if_missing = true;  options.max_sequential_skip_in_iterations = 3;  options.prefix_extractor = nullptr;  options.write_buffer_size = 1 << 27;  // big enough to avoid flush  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  DestroyAndReopen(options);  // Insert.  ASSERT_OK(Put("b", "0"));  // Create iterator.  ReadOptions ro;  std::unique_ptr<Iterator> iter(NewIterator(ro));  // Insert a lot.  for (int i = 0; i < 100; ++i) {    ASSERT_OK(Put("b", std::to_string(i + 1).c_str()));  }#ifndef ROCKSDB_LITE  // Check that memtable wasn't flushed.  std::string val;  ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level0", &val));  EXPECT_EQ("0", val);#endif  // Seek iterator to a smaller key.  get_perf_context()->Reset();  iter->Seek("a");  ASSERT_TRUE(iter->Valid());  EXPECT_EQ("b", iter->key().ToString());  EXPECT_EQ("0", iter->value().ToString());  // Check that the seek didn't do too much work.  // Checks are not tight, just make sure that everything is well below 100.  EXPECT_LT(get_perf_context()->internal_key_skipped_count, 4);  EXPECT_LT(get_perf_context()->internal_recent_skipped_count, 8);  EXPECT_LT(get_perf_context()->seek_on_memtable_count, 10);  EXPECT_LT(get_perf_context()->next_on_memtable_count, 10);  EXPECT_LT(get_perf_context()->prev_on_memtable_count, 10);  // Check that iterator did something like what we expect.  EXPECT_EQ(get_perf_context()->internal_delete_skipped_count, 0);  EXPECT_EQ(get_perf_context()->internal_merge_count, 0);  EXPECT_GE(get_perf_context()->internal_recent_skipped_count, 2);  EXPECT_GE(get_perf_context()->seek_on_memtable_count, 2);  EXPECT_EQ(1, options.statistics->getTickerCount(                 NUMBER_OF_RESEEKS_IN_ITERATION));}TEST_P(DBIteratorTest, Refresh) {  ASSERT_OK(Put("x", "y"));  std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  ASSERT_OK(Put("c", "d"));  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  iter->Refresh();  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("c")), 0);  iter->Next();  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("m", "n"));  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("c")), 0);  iter->Next();  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  iter->Refresh();  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("c")), 0);  iter->Next();  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("m")), 0);  iter->Next();  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  iter.reset();}TEST_P(DBIteratorTest, RefreshWithSnapshot) {  ASSERT_OK(Put("x", "y"));  const Snapshot* snapshot = db_->GetSnapshot();  ReadOptions options;  options.snapshot = snapshot;  Iterator* iter = NewIterator(options);  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  ASSERT_OK(Put("c", "d"));  iter->Seek(Slice("a"));  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().compare(Slice("x")), 0);  iter->Next();  ASSERT_FALSE(iter->Valid());  Status s;  s = iter->Refresh();  ASSERT_TRUE(s.IsNotSupported());  db_->ReleaseSnapshot(snapshot);  delete iter;}TEST_P(DBIteratorTest, CreationFailure) {  SyncPoint::GetInstance()->SetCallBack(      "DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {        *(reinterpret_cast<Status*>(arg)) = Status::Corruption("test status");      });  SyncPoint::GetInstance()->EnableProcessing();  Iterator* iter = NewIterator(ReadOptions());  ASSERT_FALSE(iter->Valid());  ASSERT_TRUE(iter->status().IsCorruption());  delete iter;}TEST_P(DBIteratorTest, UpperBoundWithChangeDirection) {  Options options = CurrentOptions();  options.max_sequential_skip_in_iterations = 3;  DestroyAndReopen(options);  // write a bunch of kvs to the database.  ASSERT_OK(Put("a", "1"));  ASSERT_OK(Put("y", "1"));  ASSERT_OK(Put("y1", "1"));  ASSERT_OK(Put("y2", "1"));  ASSERT_OK(Put("y3", "1"));  ASSERT_OK(Put("z", "1"));  ASSERT_OK(Flush());  ASSERT_OK(Put("a", "1"));  ASSERT_OK(Put("z", "1"));  ASSERT_OK(Put("bar", "1"));  ASSERT_OK(Put("foo", "1"));  std::string upper_bound = "x";  Slice ub_slice(upper_bound);  ReadOptions ro;  ro.iterate_upper_bound = &ub_slice;  ro.max_skippable_internal_keys = 1000;  Iterator* iter = NewIterator(ro);  iter->Seek("foo");  ASSERT_TRUE(iter->Valid());  ASSERT_EQ("foo", iter->key().ToString());  iter->Prev();  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("bar", iter->key().ToString());  delete iter;}TEST_P(DBIteratorTest, TableFilter) {  ASSERT_OK(Put("a", "1"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("b", "2"));  ASSERT_OK(Put("c", "3"));  dbfull()->Flush(FlushOptions());  ASSERT_OK(Put("d", "4"));  ASSERT_OK(Put("e", "5"));  ASSERT_OK(Put("f", "6"));  dbfull()->Flush(FlushOptions());  // Ensure the table_filter callback is called once for each table.  {    std::set<uint64_t> unseen{1, 2, 3};    ReadOptions opts;    opts.table_filter = [&](const TableProperties& props) {      auto it = unseen.find(props.num_entries);      if (it == unseen.end()) {        ADD_FAILURE() << "saw table properties with an unexpected "                      << props.num_entries << " entries";      } else {        unseen.erase(it);      }      return true;    };    auto iter = NewIterator(opts);    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->1");    iter->Next();    ASSERT_EQ(IterStatus(iter), "b->2");    iter->Next();    ASSERT_EQ(IterStatus(iter), "c->3");    iter->Next();    ASSERT_EQ(IterStatus(iter), "d->4");    iter->Next();    ASSERT_EQ(IterStatus(iter), "e->5");    iter->Next();    ASSERT_EQ(IterStatus(iter), "f->6");    iter->Next();    ASSERT_FALSE(iter->Valid());    ASSERT_TRUE(unseen.empty());    delete iter;  }  // Ensure returning false in the table_filter hides the keys from that table  // during iteration.  {    ReadOptions opts;    opts.table_filter = [](const TableProperties& props) {      return props.num_entries != 2;    };    auto iter = NewIterator(opts);    iter->SeekToFirst();    ASSERT_EQ(IterStatus(iter), "a->1");    iter->Next();    ASSERT_EQ(IterStatus(iter), "d->4");    iter->Next();    ASSERT_EQ(IterStatus(iter), "e->5");    iter->Next();    ASSERT_EQ(IterStatus(iter), "f->6");    iter->Next();    ASSERT_FALSE(iter->Valid());    delete iter;  }}TEST_P(DBIteratorTest, UpperBoundWithPrevReseek) {  Options options = CurrentOptions();  options.max_sequential_skip_in_iterations = 3;  DestroyAndReopen(options);  // write a bunch of kvs to the database.  ASSERT_OK(Put("a", "1"));  ASSERT_OK(Put("y", "1"));  ASSERT_OK(Put("z", "1"));  ASSERT_OK(Flush());  ASSERT_OK(Put("a", "1"));  ASSERT_OK(Put("z", "1"));  ASSERT_OK(Put("bar", "1"));  ASSERT_OK(Put("foo", "1"));  ASSERT_OK(Put("foo", "2"));  ASSERT_OK(Put("foo", "3"));  ASSERT_OK(Put("foo", "4"));  ASSERT_OK(Put("foo", "5"));  const Snapshot* snapshot = db_->GetSnapshot();  ASSERT_OK(Put("foo", "6"));  std::string upper_bound = "x";  Slice ub_slice(upper_bound);  ReadOptions ro;  ro.snapshot = snapshot;  ro.iterate_upper_bound = &ub_slice;  Iterator* iter = NewIterator(ro);  iter->SeekForPrev("goo");  ASSERT_TRUE(iter->Valid());  ASSERT_EQ("foo", iter->key().ToString());  iter->Prev();  ASSERT_TRUE(iter->Valid());  ASSERT_EQ("bar", iter->key().ToString());  delete iter;  db_->ReleaseSnapshot(snapshot);}TEST_P(DBIteratorTest, SkipStatistics) {  Options options = CurrentOptions();  options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();  DestroyAndReopen(options);  int skip_count = 0;  // write a bunch of kvs to the database.  ASSERT_OK(Put("a", "1"));  ASSERT_OK(Put("b", "1"));  ASSERT_OK(Put("c", "1"));  ASSERT_OK(Flush());  ASSERT_OK(Put("d", "1"));  ASSERT_OK(Put("e", "1"));  ASSERT_OK(Put("f", "1"));  ASSERT_OK(Put("a", "2"));  ASSERT_OK(Put("b", "2"));  ASSERT_OK(Flush());  ASSERT_OK(Delete("d"));  ASSERT_OK(Delete("e"));  ASSERT_OK(Delete("f"));  Iterator* iter = NewIterator(ReadOptions());  int count = 0;  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {    ASSERT_OK(iter->status());    count++;  }  ASSERT_EQ(count, 3);  delete iter;  skip_count += 8; // 3 deletes + 3 original keys + 2 lower in sequence  ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));  iter = NewIterator(ReadOptions());  count = 0;  for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {    ASSERT_OK(iter->status());    count++;  }  ASSERT_EQ(count, 3);  delete iter;  skip_count += 8; // Same as above, but in reverse order  ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));  ASSERT_OK(Put("aa", "1"));  ASSERT_OK(Put("ab", "1"));  ASSERT_OK(Put("ac", "1"));  ASSERT_OK(Put("ad", "1"));  ASSERT_OK(Flush());  ASSERT_OK(Delete("ab"));  ASSERT_OK(Delete("ac"));  ASSERT_OK(Delete("ad"));  ReadOptions ro;  Slice prefix("b");  ro.iterate_upper_bound = &prefix;  iter = NewIterator(ro);  count = 0;  for(iter->Seek("aa"); iter->Valid(); iter->Next()) {    ASSERT_OK(iter->status());    count++;  }  ASSERT_EQ(count, 1);  delete iter;  skip_count += 6; // 3 deletes + 3 original keys  ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));  iter = NewIterator(ro);  count = 0;  for(iter->SeekToLast(); iter->Valid(); iter->Prev()) {    ASSERT_OK(iter->status());    count++;  }  ASSERT_EQ(count, 2);  delete iter;  // 3 deletes + 3 original keys + lower sequence of "a"  skip_count += 7;  ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));}TEST_P(DBIteratorTest, SeekAfterHittingManyInternalKeys) {  Options options = CurrentOptions();  DestroyAndReopen(options);  ReadOptions ropts;  ropts.max_skippable_internal_keys = 2;  Put("1", "val_1");  // Add more tombstones than max_skippable_internal_keys so that Next() fails.  Delete("2");  Delete("3");  Delete("4");  Delete("5");  Put("6", "val_6");  std::unique_ptr<Iterator> iter(NewIterator(ropts));  iter->SeekToFirst();  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(iter->key().ToString(), "1");  ASSERT_EQ(iter->value().ToString(), "val_1");  // This should fail as incomplete due to too many non-visible internal keys on  // the way to the next valid user key.  iter->Next();  ASSERT_TRUE(!iter->Valid());  ASSERT_TRUE(iter->status().IsIncomplete());  // Get the internal key at which Next() failed.  std::string prop_value;  ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));  ASSERT_EQ("4", prop_value);  // Create a new iterator to seek to the internal key.  std::unique_ptr<Iterator> iter2(NewIterator(ropts));  iter2->Seek(prop_value);  ASSERT_TRUE(iter2->Valid());  ASSERT_OK(iter2->status());  ASSERT_EQ(iter2->key().ToString(), "6");  ASSERT_EQ(iter2->value().ToString(), "val_6");}// Reproduces a former bug where iterator would skip some records when DBIter// re-seeks subiterator with Incomplete status.TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {  Options options = CurrentOptions();  BlockBasedTableOptions table_options;  // Make sure the sst file has more than one block.  table_options.flush_block_policy_factory =      std::make_shared<FlushBlockEveryKeyPolicyFactory>();  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  DestroyAndReopen(options);  // Two records in sst file, each in its own block.  Put("b", "");  Put("d", "");  Flush();  // Create a nonblocking iterator before writing to memtable.  ReadOptions ropt;  ropt.read_tier = kBlockCacheTier;  std::unique_ptr<Iterator> iter(NewIterator(ropt));  // Overwrite a key in memtable many times to hit  // max_sequential_skip_in_iterations (which is 8 by default).  for (int i = 0; i < 20; ++i) {    Put("c", "");  }  // Load the second block in sst file into the block cache.  {    std::unique_ptr<Iterator> iter2(NewIterator(ReadOptions()));    iter2->Seek("d");  }  // Finally seek the nonblocking iterator.  iter->Seek("a");  // With the bug, the status used to be OK, and the iterator used to point to  // "d".  EXPECT_TRUE(iter->status().IsIncomplete());}TEST_P(DBIteratorTest, SeekBackwardAfterOutOfUpperBound) {  Put("a", "");  Put("b", "");  Flush();  ReadOptions ropt;  Slice ub = "b";  ropt.iterate_upper_bound = &ub;  std::unique_ptr<Iterator> it(dbfull()->NewIterator(ropt));  it->SeekForPrev("a");  ASSERT_TRUE(it->Valid());  ASSERT_OK(it->status());  ASSERT_EQ("a", it->key().ToString());  it->Next();  ASSERT_FALSE(it->Valid());  ASSERT_OK(it->status());  it->SeekForPrev("a");  ASSERT_OK(it->status());  ASSERT_TRUE(it->Valid());  ASSERT_EQ("a", it->key().ToString());}TEST_P(DBIteratorTest, AvoidReseekLevelIterator) {  Options options = CurrentOptions();  options.compression = CompressionType::kNoCompression;  BlockBasedTableOptions table_options;  table_options.block_size = 800;  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  Reopen(options);  Random rnd(301);  std::string random_str = RandomString(&rnd, 180);  ASSERT_OK(Put("1", random_str));  ASSERT_OK(Put("2", random_str));  ASSERT_OK(Put("3", random_str));  ASSERT_OK(Put("4", random_str));  // A new block  ASSERT_OK(Put("5", random_str));  ASSERT_OK(Put("6", random_str));  ASSERT_OK(Put("7", random_str));  ASSERT_OK(Flush());  ASSERT_OK(Put("8", random_str));  ASSERT_OK(Put("9", random_str));  ASSERT_OK(Flush());  ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));  int num_find_file_in_level = 0;  int num_idx_blk_seek = 0;  SyncPoint::GetInstance()->SetCallBack(      "LevelIterator::Seek:BeforeFindFile",      [&](void* /*arg*/) { num_find_file_in_level++; });  SyncPoint::GetInstance()->SetCallBack(      "IndexBlockIter::Seek:0", [&](void* /*arg*/) { num_idx_blk_seek++; });  SyncPoint::GetInstance()->EnableProcessing();  {    std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));    iter->Seek("1");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(1, num_idx_blk_seek);    iter->Seek("2");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(1, num_idx_blk_seek);    iter->Seek("3");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(1, num_idx_blk_seek);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(1, num_idx_blk_seek);    iter->Seek("5");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(2, num_idx_blk_seek);    iter->Seek("6");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(2, num_idx_blk_seek);    iter->Seek("7");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(1, num_find_file_in_level);    ASSERT_EQ(3, num_idx_blk_seek);    iter->Seek("8");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(2, num_find_file_in_level);    // Still re-seek because "8" is the boundary key, which has    // the same user key as the seek key.    ASSERT_EQ(4, num_idx_blk_seek);    iter->Seek("5");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(3, num_find_file_in_level);    ASSERT_EQ(5, num_idx_blk_seek);    iter->Next();    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(3, num_find_file_in_level);    ASSERT_EQ(5, num_idx_blk_seek);    // Seek backward never triggers the index block seek to be skipped    iter->Seek("5");    ASSERT_TRUE(iter->Valid());    ASSERT_EQ(3, num_find_file_in_level);    ASSERT_EQ(6, num_idx_blk_seek);  }  SyncPoint::GetInstance()->DisableProcessing();}// MyRocks may change iterate bounds before seek. Simply test to make sure such// usage doesn't break iterator.TEST_P(DBIteratorTest, IterateBoundChangedBeforeSeek) {  Options options = CurrentOptions();  options.compression = CompressionType::kNoCompression;  BlockBasedTableOptions table_options;  table_options.block_size = 100;  options.table_factory.reset(NewBlockBasedTableFactory(table_options));  std::string value(50, 'v');  Reopen(options);  ASSERT_OK(Put("aaa", value));  ASSERT_OK(Flush());  ASSERT_OK(Put("bbb", "v"));  ASSERT_OK(Put("ccc", "v"));  ASSERT_OK(Put("ddd", "v"));  ASSERT_OK(Flush());  ASSERT_OK(Put("eee", "v"));  ASSERT_OK(Flush());  ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));  std::string ub1 = "e";  std::string ub2 = "c";  Slice ub(ub1);  ReadOptions read_opts1;  read_opts1.iterate_upper_bound = &ub;  Iterator* iter = NewIterator(read_opts1);  // Seek and iterate accross block boundary.  iter->Seek("b");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("bbb", iter->key());  ub = Slice(ub2);  iter->Seek("b");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("bbb", iter->key());  iter->Next();  ASSERT_FALSE(iter->Valid());  ASSERT_OK(iter->status());  delete iter;  std::string lb1 = "a";  std::string lb2 = "c";  Slice lb(lb1);  ReadOptions read_opts2;  read_opts2.iterate_lower_bound = &lb;  iter = NewIterator(read_opts2);  iter->SeekForPrev("d");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("ccc", iter->key());  lb = Slice(lb2);  iter->SeekForPrev("d");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("ccc", iter->key());  iter->Prev();  ASSERT_FALSE(iter->Valid());  ASSERT_OK(iter->status());  delete iter;}TEST_P(DBIteratorTest, IterateWithLowerBoundAcrossFileBoundary) {  ASSERT_OK(Put("aaa", "v"));  ASSERT_OK(Put("bbb", "v"));  ASSERT_OK(Flush());  ASSERT_OK(Put("ccc", "v"));  ASSERT_OK(Put("ddd", "v"));  ASSERT_OK(Flush());  // Move both files to bottom level.  ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));  Slice lower_bound("b");  ReadOptions read_opts;  read_opts.iterate_lower_bound = &lower_bound;  std::unique_ptr<Iterator> iter(NewIterator(read_opts));  iter->SeekForPrev("d");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("ccc", iter->key());  iter->Prev();  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("bbb", iter->key());  iter->Prev();  ASSERT_FALSE(iter->Valid());  ASSERT_OK(iter->status());}INSTANTIATE_TEST_CASE_P(DBIteratorTestInstance, DBIteratorTest,                        testing::Values(true, false));// Tests how DBIter work with ReadCallbackclass DBIteratorWithReadCallbackTest : public DBIteratorTest {};TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {  class TestReadCallback : public ReadCallback {   public:    explicit TestReadCallback(SequenceNumber _max_visible_seq)        : ReadCallback(_max_visible_seq) {}    bool IsVisibleFullCheck(SequenceNumber seq) override {      return seq <= max_visible_seq_;    }  };  ASSERT_OK(Put("foo", "v1"));  ASSERT_OK(Put("foo", "v2"));  ASSERT_OK(Put("foo", "v3"));  ASSERT_OK(Put("a", "va"));  ASSERT_OK(Put("z", "vz"));  SequenceNumber seq1 = db_->GetLatestSequenceNumber();  TestReadCallback callback1(seq1);  ASSERT_OK(Put("foo", "v4"));  ASSERT_OK(Put("foo", "v5"));  ASSERT_OK(Put("bar", "v7"));  SequenceNumber seq2 = db_->GetLatestSequenceNumber();  auto* cfd =      reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())          ->cfd();  // The iterator are suppose to see data before seq1.  Iterator* iter =      dbfull()->NewIteratorImpl(ReadOptions(), cfd, seq2, &callback1);  // Seek  // The latest value of "foo" before seq1 is "v3"  iter->Seek("foo");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("foo", iter->key());  ASSERT_EQ("v3", iter->value());  // "bar" is not visible to the iterator. It will move on to the next key  // "foo".  iter->Seek("bar");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("foo", iter->key());  ASSERT_EQ("v3", iter->value());  // Next  // Seek to "a"  iter->Seek("a");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("va", iter->value());  // "bar" is not visible to the iterator. It will move on to the next key  // "foo".  iter->Next();  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("foo", iter->key());  ASSERT_EQ("v3", iter->value());  // Prev  // Seek to "z"  iter->Seek("z");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("vz", iter->value());  // The previous key is "foo", which is visible to the iterator.  iter->Prev();  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("foo", iter->key());  ASSERT_EQ("v3", iter->value());  // "bar" is not visible to the iterator. It will move on to the next key "a".  iter->Prev();  // skipping "bar"  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("a", iter->key());  ASSERT_EQ("va", iter->value());  // SeekForPrev  // The previous key is "foo", which is visible to the iterator.  iter->SeekForPrev("y");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("foo", iter->key());  ASSERT_EQ("v3", iter->value());  // "bar" is not visible to the iterator. It will move on to the next key "a".  iter->SeekForPrev("bar");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("a", iter->key());  ASSERT_EQ("va", iter->value());  delete iter;  // Prev beyond max_sequential_skip_in_iterations  uint64_t num_versions =      CurrentOptions().max_sequential_skip_in_iterations + 10;  for (uint64_t i = 0; i < num_versions; i++) {    ASSERT_OK(Put("bar", ToString(i)));  }  SequenceNumber seq3 = db_->GetLatestSequenceNumber();  TestReadCallback callback2(seq3);  ASSERT_OK(Put("bar", "v8"));  SequenceNumber seq4 = db_->GetLatestSequenceNumber();  // The iterator is suppose to see data before seq3.  iter = dbfull()->NewIteratorImpl(ReadOptions(), cfd, seq4, &callback2);  // Seek to "z", which is visible.  iter->Seek("z");  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("vz", iter->value());  // Previous key is "foo" and the last value "v5" is visible.  iter->Prev();  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("foo", iter->key());  ASSERT_EQ("v5", iter->value());  // Since the number of values of "bar" is more than  // max_sequential_skip_in_iterations, Prev() will ultimately fallback to  // seek in forward direction. Here we test the fallback seek is correct.  // The last visible value should be (num_versions - 1), as "v8" is not  // visible.  iter->Prev();  ASSERT_TRUE(iter->Valid());  ASSERT_OK(iter->status());  ASSERT_EQ("bar", iter->key());  ASSERT_EQ(ToString(num_versions - 1), iter->value());  delete iter;}}  // namespace ROCKSDB_NAMESPACEint main(int argc, char** argv) {  ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();  ::testing::InitGoogleTest(&argc, argv);  return RUN_ALL_TESTS();}
 |