| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651 |
- // 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 <stdio.h>
- #include <algorithm>
- #include <iostream>
- #include <map>
- #include <memory>
- #include <string>
- #include <vector>
- #include "block_fetcher.h"
- #include "cache/lru_cache.h"
- #include "db/dbformat.h"
- #include "db/memtable.h"
- #include "db/write_batch_internal.h"
- #include "memtable/stl_wrappers.h"
- #include "meta_blocks.h"
- #include "monitoring/statistics.h"
- #include "port/port.h"
- #include "rocksdb/cache.h"
- #include "rocksdb/db.h"
- #include "rocksdb/env.h"
- #include "rocksdb/file_checksum.h"
- #include "rocksdb/file_system.h"
- #include "rocksdb/iterator.h"
- #include "rocksdb/memtablerep.h"
- #include "rocksdb/perf_context.h"
- #include "rocksdb/slice_transform.h"
- #include "rocksdb/statistics.h"
- #include "rocksdb/write_buffer_manager.h"
- #include "table/block_based/block.h"
- #include "table/block_based/block_based_table_builder.h"
- #include "table/block_based/block_based_table_factory.h"
- #include "table/block_based/block_based_table_reader.h"
- #include "table/block_based/block_builder.h"
- #include "table/block_based/flush_block_policy.h"
- #include "table/format.h"
- #include "table/get_context.h"
- #include "table/internal_iterator.h"
- #include "table/plain/plain_table_factory.h"
- #include "table/scoped_arena_iterator.h"
- #include "table/sst_file_writer_collectors.h"
- #include "test_util/sync_point.h"
- #include "test_util/testharness.h"
- #include "test_util/testutil.h"
- #include "util/compression.h"
- #include "util/file_checksum_helper.h"
- #include "util/random.h"
- #include "util/string_util.h"
- #include "utilities/merge_operators.h"
- namespace ROCKSDB_NAMESPACE {
- extern const uint64_t kLegacyBlockBasedTableMagicNumber;
- extern const uint64_t kLegacyPlainTableMagicNumber;
- extern const uint64_t kBlockBasedTableMagicNumber;
- extern const uint64_t kPlainTableMagicNumber;
- namespace {
- const std::string kDummyValue(10000, 'o');
- // DummyPropertiesCollector used to test BlockBasedTableProperties
- class DummyPropertiesCollector : public TablePropertiesCollector {
- public:
- const char* Name() const override { return ""; }
- Status Finish(UserCollectedProperties* /*properties*/) override {
- return Status::OK();
- }
- Status Add(const Slice& /*user_key*/, const Slice& /*value*/) override {
- return Status::OK();
- }
- UserCollectedProperties GetReadableProperties() const override {
- return UserCollectedProperties{};
- }
- };
- class DummyPropertiesCollectorFactory1
- : public TablePropertiesCollectorFactory {
- public:
- TablePropertiesCollector* CreateTablePropertiesCollector(
- TablePropertiesCollectorFactory::Context /*context*/) override {
- return new DummyPropertiesCollector();
- }
- const char* Name() const override { return "DummyPropertiesCollector1"; }
- };
- class DummyPropertiesCollectorFactory2
- : public TablePropertiesCollectorFactory {
- public:
- TablePropertiesCollector* CreateTablePropertiesCollector(
- TablePropertiesCollectorFactory::Context /*context*/) override {
- return new DummyPropertiesCollector();
- }
- const char* Name() const override { return "DummyPropertiesCollector2"; }
- };
- // Return reverse of "key".
- // Used to test non-lexicographic comparators.
- std::string Reverse(const Slice& key) {
- auto rev = key.ToString();
- std::reverse(rev.begin(), rev.end());
- return rev;
- }
- class ReverseKeyComparator : public Comparator {
- public:
- const char* Name() const override {
- return "rocksdb.ReverseBytewiseComparator";
- }
- int Compare(const Slice& a, const Slice& b) const override {
- return BytewiseComparator()->Compare(Reverse(a), Reverse(b));
- }
- void FindShortestSeparator(std::string* start,
- const Slice& limit) const override {
- std::string s = Reverse(*start);
- std::string l = Reverse(limit);
- BytewiseComparator()->FindShortestSeparator(&s, l);
- *start = Reverse(s);
- }
- void FindShortSuccessor(std::string* key) const override {
- std::string s = Reverse(*key);
- BytewiseComparator()->FindShortSuccessor(&s);
- *key = Reverse(s);
- }
- };
- ReverseKeyComparator reverse_key_comparator;
- void Increment(const Comparator* cmp, std::string* key) {
- if (cmp == BytewiseComparator()) {
- key->push_back('\0');
- } else {
- assert(cmp == &reverse_key_comparator);
- std::string rev = Reverse(*key);
- rev.push_back('\0');
- *key = Reverse(rev);
- }
- }
- } // namespace
- // Helper class for tests to unify the interface between
- // BlockBuilder/TableBuilder and Block/Table.
- class Constructor {
- public:
- explicit Constructor(const Comparator* cmp)
- : data_(stl_wrappers::LessOfComparator(cmp)) {}
- virtual ~Constructor() { }
- void Add(const std::string& key, const Slice& value) {
- data_[key] = value.ToString();
- }
- // Finish constructing the data structure with all the keys that have
- // been added so far. Returns the keys in sorted order in "*keys"
- // and stores the key/value pairs in "*kvmap"
- void Finish(const Options& options, const ImmutableCFOptions& ioptions,
- const MutableCFOptions& moptions,
- const BlockBasedTableOptions& table_options,
- const InternalKeyComparator& internal_comparator,
- std::vector<std::string>* keys, stl_wrappers::KVMap* kvmap) {
- last_internal_key_ = &internal_comparator;
- *kvmap = data_;
- keys->clear();
- for (const auto& kv : data_) {
- keys->push_back(kv.first);
- }
- data_.clear();
- Status s = FinishImpl(options, ioptions, moptions, table_options,
- internal_comparator, *kvmap);
- ASSERT_TRUE(s.ok()) << s.ToString();
- }
- // Construct the data structure from the data in "data"
- virtual Status FinishImpl(const Options& options,
- const ImmutableCFOptions& ioptions,
- const MutableCFOptions& moptions,
- const BlockBasedTableOptions& table_options,
- const InternalKeyComparator& internal_comparator,
- const stl_wrappers::KVMap& data) = 0;
- virtual InternalIterator* NewIterator(
- const SliceTransform* prefix_extractor = nullptr) const = 0;
- virtual const stl_wrappers::KVMap& data() { return data_; }
- virtual bool IsArenaMode() const { return false; }
- virtual DB* db() const { return nullptr; } // Overridden in DBConstructor
- virtual bool AnywayDeleteIterator() const { return false; }
- protected:
- const InternalKeyComparator* last_internal_key_;
- private:
- stl_wrappers::KVMap data_;
- };
- class BlockConstructor: public Constructor {
- public:
- explicit BlockConstructor(const Comparator* cmp)
- : Constructor(cmp),
- comparator_(cmp),
- block_(nullptr) { }
- ~BlockConstructor() override { delete block_; }
- Status FinishImpl(const Options& /*options*/,
- const ImmutableCFOptions& /*ioptions*/,
- const MutableCFOptions& /*moptions*/,
- const BlockBasedTableOptions& table_options,
- const InternalKeyComparator& /*internal_comparator*/,
- const stl_wrappers::KVMap& kv_map) override {
- delete block_;
- block_ = nullptr;
- BlockBuilder builder(table_options.block_restart_interval);
- for (const auto kv : kv_map) {
- builder.Add(kv.first, kv.second);
- }
- // Open the block
- data_ = builder.Finish().ToString();
- BlockContents contents;
- contents.data = data_;
- block_ = new Block(std::move(contents), kDisableGlobalSequenceNumber);
- return Status::OK();
- }
- InternalIterator* NewIterator(
- const SliceTransform* /*prefix_extractor*/) const override {
- return block_->NewDataIterator(comparator_, comparator_);
- }
- private:
- const Comparator* comparator_;
- std::string data_;
- Block* block_;
- BlockConstructor();
- };
- // A helper class that converts internal format keys into user keys
- class KeyConvertingIterator : public InternalIterator {
- public:
- explicit KeyConvertingIterator(InternalIterator* iter,
- bool arena_mode = false)
- : iter_(iter), arena_mode_(arena_mode) {}
- ~KeyConvertingIterator() override {
- if (arena_mode_) {
- iter_->~InternalIterator();
- } else {
- delete iter_;
- }
- }
- bool Valid() const override { return iter_->Valid() && status_.ok(); }
- void Seek(const Slice& target) override {
- ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
- std::string encoded;
- AppendInternalKey(&encoded, ikey);
- iter_->Seek(encoded);
- }
- void SeekForPrev(const Slice& target) override {
- ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
- std::string encoded;
- AppendInternalKey(&encoded, ikey);
- iter_->SeekForPrev(encoded);
- }
- void SeekToFirst() override { iter_->SeekToFirst(); }
- void SeekToLast() override { iter_->SeekToLast(); }
- void Next() override { iter_->Next(); }
- void Prev() override { iter_->Prev(); }
- bool IsOutOfBound() override { return iter_->IsOutOfBound(); }
- Slice key() const override {
- assert(Valid());
- ParsedInternalKey parsed_key;
- if (!ParseInternalKey(iter_->key(), &parsed_key)) {
- status_ = Status::Corruption("malformed internal key");
- return Slice("corrupted key");
- }
- return parsed_key.user_key;
- }
- Slice value() const override { return iter_->value(); }
- Status status() const override {
- return status_.ok() ? iter_->status() : status_;
- }
- private:
- mutable Status status_;
- InternalIterator* iter_;
- bool arena_mode_;
- // No copying allowed
- KeyConvertingIterator(const KeyConvertingIterator&);
- void operator=(const KeyConvertingIterator&);
- };
- class TableConstructor: public Constructor {
- public:
- explicit TableConstructor(const Comparator* cmp,
- bool convert_to_internal_key = false,
- int level = -1, SequenceNumber largest_seqno = 0)
- : Constructor(cmp),
- largest_seqno_(largest_seqno),
- convert_to_internal_key_(convert_to_internal_key),
- level_(level) {
- env_ = ROCKSDB_NAMESPACE::Env::Default();
- }
- ~TableConstructor() override { Reset(); }
- Status FinishImpl(const Options& options, const ImmutableCFOptions& ioptions,
- const MutableCFOptions& moptions,
- const BlockBasedTableOptions& /*table_options*/,
- const InternalKeyComparator& internal_comparator,
- const stl_wrappers::KVMap& kv_map) override {
- Reset();
- soptions.use_mmap_reads = ioptions.allow_mmap_reads;
- file_writer_.reset(test::GetWritableFileWriter(new test::StringSink(),
- "" /* don't care */));
- std::unique_ptr<TableBuilder> builder;
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- if (largest_seqno_ != 0) {
- // Pretend that it's an external file written by SstFileWriter.
- int_tbl_prop_collector_factories.emplace_back(
- new SstFileWriterPropertiesCollectorFactory(2 /* version */,
- 0 /* global_seqno*/));
- }
- std::string column_family_name;
- builder.reset(ioptions.table_factory->NewTableBuilder(
- TableBuilderOptions(ioptions, moptions, internal_comparator,
- &int_tbl_prop_collector_factories,
- options.compression, options.sample_for_compression,
- options.compression_opts, false /* skip_filters */,
- column_family_name, level_),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- file_writer_.get()));
- for (const auto kv : kv_map) {
- if (convert_to_internal_key_) {
- ParsedInternalKey ikey(kv.first, kMaxSequenceNumber, kTypeValue);
- std::string encoded;
- AppendInternalKey(&encoded, ikey);
- builder->Add(encoded, kv.second);
- } else {
- builder->Add(kv.first, kv.second);
- }
- EXPECT_TRUE(builder->status().ok());
- }
- Status s = builder->Finish();
- file_writer_->Flush();
- EXPECT_TRUE(s.ok()) << s.ToString();
- EXPECT_EQ(TEST_GetSink()->contents().size(), builder->FileSize());
- // Open the table
- uniq_id_ = cur_uniq_id_++;
- file_reader_.reset(test::GetRandomAccessFileReader(new test::StringSource(
- TEST_GetSink()->contents(), uniq_id_, ioptions.allow_mmap_reads)));
- const bool kSkipFilters = true;
- const bool kImmortal = true;
- return ioptions.table_factory->NewTableReader(
- TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
- internal_comparator, !kSkipFilters, !kImmortal,
- level_, largest_seqno_, &block_cache_tracer_),
- std::move(file_reader_), TEST_GetSink()->contents().size(),
- &table_reader_);
- }
- InternalIterator* NewIterator(
- const SliceTransform* prefix_extractor) const override {
- ReadOptions ro;
- InternalIterator* iter = table_reader_->NewIterator(
- ro, prefix_extractor, /*arena=*/nullptr, /*skip_filters=*/false,
- TableReaderCaller::kUncategorized);
- if (convert_to_internal_key_) {
- return new KeyConvertingIterator(iter);
- } else {
- return iter;
- }
- }
- uint64_t ApproximateOffsetOf(const Slice& key) const {
- if (convert_to_internal_key_) {
- InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
- const Slice skey = ikey.Encode();
- return table_reader_->ApproximateOffsetOf(
- skey, TableReaderCaller::kUncategorized);
- }
- return table_reader_->ApproximateOffsetOf(
- key, TableReaderCaller::kUncategorized);
- }
- virtual Status Reopen(const ImmutableCFOptions& ioptions,
- const MutableCFOptions& moptions) {
- file_reader_.reset(test::GetRandomAccessFileReader(new test::StringSource(
- TEST_GetSink()->contents(), uniq_id_, ioptions.allow_mmap_reads)));
- return ioptions.table_factory->NewTableReader(
- TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
- *last_internal_key_),
- std::move(file_reader_), TEST_GetSink()->contents().size(),
- &table_reader_);
- }
- virtual TableReader* GetTableReader() { return table_reader_.get(); }
- bool AnywayDeleteIterator() const override {
- return convert_to_internal_key_;
- }
- void ResetTableReader() { table_reader_.reset(); }
- bool ConvertToInternalKey() { return convert_to_internal_key_; }
- test::StringSink* TEST_GetSink() {
- return ROCKSDB_NAMESPACE::test::GetStringSinkFromLegacyWriter(
- file_writer_.get());
- }
- BlockCacheTracer block_cache_tracer_;
- private:
- void Reset() {
- uniq_id_ = 0;
- table_reader_.reset();
- file_writer_.reset();
- file_reader_.reset();
- }
- uint64_t uniq_id_;
- std::unique_ptr<WritableFileWriter> file_writer_;
- std::unique_ptr<RandomAccessFileReader> file_reader_;
- std::unique_ptr<TableReader> table_reader_;
- SequenceNumber largest_seqno_;
- bool convert_to_internal_key_;
- int level_;
- TableConstructor();
- static uint64_t cur_uniq_id_;
- EnvOptions soptions;
- Env* env_;
- };
- uint64_t TableConstructor::cur_uniq_id_ = 1;
- class MemTableConstructor: public Constructor {
- public:
- explicit MemTableConstructor(const Comparator* cmp, WriteBufferManager* wb)
- : Constructor(cmp),
- internal_comparator_(cmp),
- write_buffer_manager_(wb),
- table_factory_(new SkipListFactory) {
- options_.memtable_factory = table_factory_;
- ImmutableCFOptions ioptions(options_);
- memtable_ =
- new MemTable(internal_comparator_, ioptions, MutableCFOptions(options_),
- wb, kMaxSequenceNumber, 0 /* column_family_id */);
- memtable_->Ref();
- }
- ~MemTableConstructor() override { delete memtable_->Unref(); }
- Status FinishImpl(const Options&, const ImmutableCFOptions& ioptions,
- const MutableCFOptions& /*moptions*/,
- const BlockBasedTableOptions& /*table_options*/,
- const InternalKeyComparator& /*internal_comparator*/,
- const stl_wrappers::KVMap& kv_map) override {
- delete memtable_->Unref();
- ImmutableCFOptions mem_ioptions(ioptions);
- memtable_ = new MemTable(internal_comparator_, mem_ioptions,
- MutableCFOptions(options_), write_buffer_manager_,
- kMaxSequenceNumber, 0 /* column_family_id */);
- memtable_->Ref();
- int seq = 1;
- for (const auto kv : kv_map) {
- memtable_->Add(seq, kTypeValue, kv.first, kv.second);
- seq++;
- }
- return Status::OK();
- }
- InternalIterator* NewIterator(
- const SliceTransform* /*prefix_extractor*/) const override {
- return new KeyConvertingIterator(
- memtable_->NewIterator(ReadOptions(), &arena_), true);
- }
- bool AnywayDeleteIterator() const override { return true; }
- bool IsArenaMode() const override { return true; }
- private:
- mutable Arena arena_;
- InternalKeyComparator internal_comparator_;
- Options options_;
- WriteBufferManager* write_buffer_manager_;
- MemTable* memtable_;
- std::shared_ptr<SkipListFactory> table_factory_;
- };
- class InternalIteratorFromIterator : public InternalIterator {
- public:
- explicit InternalIteratorFromIterator(Iterator* it) : it_(it) {}
- bool Valid() const override { return it_->Valid(); }
- void Seek(const Slice& target) override { it_->Seek(target); }
- void SeekForPrev(const Slice& target) override { it_->SeekForPrev(target); }
- void SeekToFirst() override { it_->SeekToFirst(); }
- void SeekToLast() override { it_->SeekToLast(); }
- void Next() override { it_->Next(); }
- void Prev() override { it_->Prev(); }
- Slice key() const override { return it_->key(); }
- Slice value() const override { return it_->value(); }
- Status status() const override { return it_->status(); }
- private:
- std::unique_ptr<Iterator> it_;
- };
- class DBConstructor: public Constructor {
- public:
- explicit DBConstructor(const Comparator* cmp)
- : Constructor(cmp),
- comparator_(cmp) {
- db_ = nullptr;
- NewDB();
- }
- ~DBConstructor() override { delete db_; }
- Status FinishImpl(const Options& /*options*/,
- const ImmutableCFOptions& /*ioptions*/,
- const MutableCFOptions& /*moptions*/,
- const BlockBasedTableOptions& /*table_options*/,
- const InternalKeyComparator& /*internal_comparator*/,
- const stl_wrappers::KVMap& kv_map) override {
- delete db_;
- db_ = nullptr;
- NewDB();
- for (const auto kv : kv_map) {
- WriteBatch batch;
- batch.Put(kv.first, kv.second);
- EXPECT_TRUE(db_->Write(WriteOptions(), &batch).ok());
- }
- return Status::OK();
- }
- InternalIterator* NewIterator(
- const SliceTransform* /*prefix_extractor*/) const override {
- return new InternalIteratorFromIterator(db_->NewIterator(ReadOptions()));
- }
- DB* db() const override { return db_; }
- private:
- void NewDB() {
- std::string name = test::PerThreadDBPath("table_testdb");
- Options options;
- options.comparator = comparator_;
- Status status = DestroyDB(name, options);
- ASSERT_TRUE(status.ok()) << status.ToString();
- options.create_if_missing = true;
- options.error_if_exists = true;
- options.write_buffer_size = 10000; // Something small to force merging
- status = DB::Open(options, name, &db_);
- ASSERT_TRUE(status.ok()) << status.ToString();
- }
- const Comparator* comparator_;
- DB* db_;
- };
- enum TestType {
- BLOCK_BASED_TABLE_TEST,
- #ifndef ROCKSDB_LITE
- PLAIN_TABLE_SEMI_FIXED_PREFIX,
- PLAIN_TABLE_FULL_STR_PREFIX,
- PLAIN_TABLE_TOTAL_ORDER,
- #endif // !ROCKSDB_LITE
- BLOCK_TEST,
- MEMTABLE_TEST,
- DB_TEST
- };
- struct TestArgs {
- TestType type;
- bool reverse_compare;
- int restart_interval;
- CompressionType compression;
- uint32_t format_version;
- bool use_mmap;
- };
- static std::vector<TestArgs> GenerateArgList() {
- std::vector<TestArgs> test_args;
- std::vector<TestType> test_types = {
- BLOCK_BASED_TABLE_TEST,
- #ifndef ROCKSDB_LITE
- PLAIN_TABLE_SEMI_FIXED_PREFIX,
- PLAIN_TABLE_FULL_STR_PREFIX,
- PLAIN_TABLE_TOTAL_ORDER,
- #endif // !ROCKSDB_LITE
- BLOCK_TEST,
- MEMTABLE_TEST, DB_TEST};
- std::vector<bool> reverse_compare_types = {false, true};
- std::vector<int> restart_intervals = {16, 1, 1024};
- // Only add compression if it is supported
- std::vector<std::pair<CompressionType, bool>> compression_types;
- compression_types.emplace_back(kNoCompression, false);
- if (Snappy_Supported()) {
- compression_types.emplace_back(kSnappyCompression, false);
- }
- if (Zlib_Supported()) {
- compression_types.emplace_back(kZlibCompression, false);
- compression_types.emplace_back(kZlibCompression, true);
- }
- if (BZip2_Supported()) {
- compression_types.emplace_back(kBZip2Compression, false);
- compression_types.emplace_back(kBZip2Compression, true);
- }
- if (LZ4_Supported()) {
- compression_types.emplace_back(kLZ4Compression, false);
- compression_types.emplace_back(kLZ4Compression, true);
- compression_types.emplace_back(kLZ4HCCompression, false);
- compression_types.emplace_back(kLZ4HCCompression, true);
- }
- if (XPRESS_Supported()) {
- compression_types.emplace_back(kXpressCompression, false);
- compression_types.emplace_back(kXpressCompression, true);
- }
- if (ZSTD_Supported()) {
- compression_types.emplace_back(kZSTD, false);
- compression_types.emplace_back(kZSTD, true);
- }
- for (auto test_type : test_types) {
- for (auto reverse_compare : reverse_compare_types) {
- #ifndef ROCKSDB_LITE
- if (test_type == PLAIN_TABLE_SEMI_FIXED_PREFIX ||
- test_type == PLAIN_TABLE_FULL_STR_PREFIX ||
- test_type == PLAIN_TABLE_TOTAL_ORDER) {
- // Plain table doesn't use restart index or compression.
- TestArgs one_arg;
- one_arg.type = test_type;
- one_arg.reverse_compare = reverse_compare;
- one_arg.restart_interval = restart_intervals[0];
- one_arg.compression = compression_types[0].first;
- one_arg.use_mmap = true;
- test_args.push_back(one_arg);
- one_arg.use_mmap = false;
- test_args.push_back(one_arg);
- continue;
- }
- #endif // !ROCKSDB_LITE
- for (auto restart_interval : restart_intervals) {
- for (auto compression_type : compression_types) {
- TestArgs one_arg;
- one_arg.type = test_type;
- one_arg.reverse_compare = reverse_compare;
- one_arg.restart_interval = restart_interval;
- one_arg.compression = compression_type.first;
- one_arg.format_version = compression_type.second ? 2 : 1;
- one_arg.use_mmap = false;
- test_args.push_back(one_arg);
- }
- }
- }
- }
- return test_args;
- }
- // In order to make all tests run for plain table format, including
- // those operating on empty keys, create a new prefix transformer which
- // return fixed prefix if the slice is not shorter than the prefix length,
- // and the full slice if it is shorter.
- class FixedOrLessPrefixTransform : public SliceTransform {
- private:
- const size_t prefix_len_;
- public:
- explicit FixedOrLessPrefixTransform(size_t prefix_len) :
- prefix_len_(prefix_len) {
- }
- const char* Name() const override { return "rocksdb.FixedPrefix"; }
- Slice Transform(const Slice& src) const override {
- assert(InDomain(src));
- if (src.size() < prefix_len_) {
- return src;
- }
- return Slice(src.data(), prefix_len_);
- }
- bool InDomain(const Slice& /*src*/) const override { return true; }
- bool InRange(const Slice& dst) const override {
- return (dst.size() <= prefix_len_);
- }
- bool FullLengthEnabled(size_t* /*len*/) const override { return false; }
- };
- class HarnessTest : public testing::Test {
- public:
- HarnessTest()
- : ioptions_(options_),
- moptions_(options_),
- constructor_(nullptr),
- write_buffer_(options_.db_write_buffer_size) {}
- void Init(const TestArgs& args) {
- delete constructor_;
- constructor_ = nullptr;
- options_ = Options();
- options_.compression = args.compression;
- // Use shorter block size for tests to exercise block boundary
- // conditions more.
- if (args.reverse_compare) {
- options_.comparator = &reverse_key_comparator;
- }
- internal_comparator_.reset(
- new test::PlainInternalKeyComparator(options_.comparator));
- support_prev_ = true;
- only_support_prefix_seek_ = false;
- options_.allow_mmap_reads = args.use_mmap;
- switch (args.type) {
- case BLOCK_BASED_TABLE_TEST:
- table_options_.flush_block_policy_factory.reset(
- new FlushBlockBySizePolicyFactory());
- table_options_.block_size = 256;
- table_options_.block_restart_interval = args.restart_interval;
- table_options_.index_block_restart_interval = args.restart_interval;
- table_options_.format_version = args.format_version;
- options_.table_factory.reset(
- new BlockBasedTableFactory(table_options_));
- constructor_ = new TableConstructor(
- options_.comparator, true /* convert_to_internal_key_ */);
- internal_comparator_.reset(
- new InternalKeyComparator(options_.comparator));
- break;
- // Plain table is not supported in ROCKSDB_LITE
- #ifndef ROCKSDB_LITE
- case PLAIN_TABLE_SEMI_FIXED_PREFIX:
- support_prev_ = false;
- only_support_prefix_seek_ = true;
- options_.prefix_extractor.reset(new FixedOrLessPrefixTransform(2));
- options_.table_factory.reset(NewPlainTableFactory());
- constructor_ = new TableConstructor(
- options_.comparator, true /* convert_to_internal_key_ */);
- internal_comparator_.reset(
- new InternalKeyComparator(options_.comparator));
- break;
- case PLAIN_TABLE_FULL_STR_PREFIX:
- support_prev_ = false;
- only_support_prefix_seek_ = true;
- options_.prefix_extractor.reset(NewNoopTransform());
- options_.table_factory.reset(NewPlainTableFactory());
- constructor_ = new TableConstructor(
- options_.comparator, true /* convert_to_internal_key_ */);
- internal_comparator_.reset(
- new InternalKeyComparator(options_.comparator));
- break;
- case PLAIN_TABLE_TOTAL_ORDER:
- support_prev_ = false;
- only_support_prefix_seek_ = false;
- options_.prefix_extractor = nullptr;
- {
- PlainTableOptions plain_table_options;
- plain_table_options.user_key_len = kPlainTableVariableLength;
- plain_table_options.bloom_bits_per_key = 0;
- plain_table_options.hash_table_ratio = 0;
- options_.table_factory.reset(
- NewPlainTableFactory(plain_table_options));
- }
- constructor_ = new TableConstructor(
- options_.comparator, true /* convert_to_internal_key_ */);
- internal_comparator_.reset(
- new InternalKeyComparator(options_.comparator));
- break;
- #endif // !ROCKSDB_LITE
- case BLOCK_TEST:
- table_options_.block_size = 256;
- options_.table_factory.reset(
- new BlockBasedTableFactory(table_options_));
- constructor_ = new BlockConstructor(options_.comparator);
- break;
- case MEMTABLE_TEST:
- table_options_.block_size = 256;
- options_.table_factory.reset(
- new BlockBasedTableFactory(table_options_));
- constructor_ = new MemTableConstructor(options_.comparator,
- &write_buffer_);
- break;
- case DB_TEST:
- table_options_.block_size = 256;
- options_.table_factory.reset(
- new BlockBasedTableFactory(table_options_));
- constructor_ = new DBConstructor(options_.comparator);
- break;
- }
- ioptions_ = ImmutableCFOptions(options_);
- moptions_ = MutableCFOptions(options_);
- }
- ~HarnessTest() override { delete constructor_; }
- void Add(const std::string& key, const std::string& value) {
- constructor_->Add(key, value);
- }
- void Test(Random* rnd) {
- std::vector<std::string> keys;
- stl_wrappers::KVMap data;
- constructor_->Finish(options_, ioptions_, moptions_, table_options_,
- *internal_comparator_, &keys, &data);
- TestForwardScan(keys, data);
- if (support_prev_) {
- TestBackwardScan(keys, data);
- }
- TestRandomAccess(rnd, keys, data);
- }
- void TestForwardScan(const std::vector<std::string>& /*keys*/,
- const stl_wrappers::KVMap& data) {
- InternalIterator* iter = constructor_->NewIterator();
- ASSERT_TRUE(!iter->Valid());
- iter->SeekToFirst();
- for (stl_wrappers::KVMap::const_iterator model_iter = data.begin();
- model_iter != data.end(); ++model_iter) {
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- iter->Next();
- }
- ASSERT_TRUE(!iter->Valid());
- if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
- iter->~InternalIterator();
- } else {
- delete iter;
- }
- }
- void TestBackwardScan(const std::vector<std::string>& /*keys*/,
- const stl_wrappers::KVMap& data) {
- InternalIterator* iter = constructor_->NewIterator();
- ASSERT_TRUE(!iter->Valid());
- iter->SeekToLast();
- for (stl_wrappers::KVMap::const_reverse_iterator model_iter = data.rbegin();
- model_iter != data.rend(); ++model_iter) {
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- iter->Prev();
- }
- ASSERT_TRUE(!iter->Valid());
- if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
- iter->~InternalIterator();
- } else {
- delete iter;
- }
- }
- void TestRandomAccess(Random* rnd, const std::vector<std::string>& keys,
- const stl_wrappers::KVMap& data) {
- static const bool kVerbose = false;
- InternalIterator* iter = constructor_->NewIterator();
- ASSERT_TRUE(!iter->Valid());
- stl_wrappers::KVMap::const_iterator model_iter = data.begin();
- if (kVerbose) fprintf(stderr, "---\n");
- for (int i = 0; i < 200; i++) {
- const int toss = rnd->Uniform(support_prev_ ? 5 : 3);
- switch (toss) {
- case 0: {
- if (iter->Valid()) {
- if (kVerbose) fprintf(stderr, "Next\n");
- iter->Next();
- ++model_iter;
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- }
- break;
- }
- case 1: {
- if (kVerbose) fprintf(stderr, "SeekToFirst\n");
- iter->SeekToFirst();
- model_iter = data.begin();
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- break;
- }
- case 2: {
- std::string key = PickRandomKey(rnd, keys);
- model_iter = data.lower_bound(key);
- if (kVerbose) fprintf(stderr, "Seek '%s'\n",
- EscapeString(key).c_str());
- iter->Seek(Slice(key));
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- break;
- }
- case 3: {
- if (iter->Valid()) {
- if (kVerbose) fprintf(stderr, "Prev\n");
- iter->Prev();
- if (model_iter == data.begin()) {
- model_iter = data.end(); // Wrap around to invalid value
- } else {
- --model_iter;
- }
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- }
- break;
- }
- case 4: {
- if (kVerbose) fprintf(stderr, "SeekToLast\n");
- iter->SeekToLast();
- if (keys.empty()) {
- model_iter = data.end();
- } else {
- std::string last = data.rbegin()->first;
- model_iter = data.lower_bound(last);
- }
- ASSERT_EQ(ToString(data, model_iter), ToString(iter));
- break;
- }
- }
- }
- if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
- iter->~InternalIterator();
- } else {
- delete iter;
- }
- }
- std::string ToString(const stl_wrappers::KVMap& data,
- const stl_wrappers::KVMap::const_iterator& it) {
- if (it == data.end()) {
- return "END";
- } else {
- return "'" + it->first + "->" + it->second + "'";
- }
- }
- std::string ToString(const stl_wrappers::KVMap& data,
- const stl_wrappers::KVMap::const_reverse_iterator& it) {
- if (it == data.rend()) {
- return "END";
- } else {
- return "'" + it->first + "->" + it->second + "'";
- }
- }
- std::string ToString(const InternalIterator* it) {
- if (!it->Valid()) {
- return "END";
- } else {
- return "'" + it->key().ToString() + "->" + it->value().ToString() + "'";
- }
- }
- std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) {
- if (keys.empty()) {
- return "foo";
- } else {
- const int index = rnd->Uniform(static_cast<int>(keys.size()));
- std::string result = keys[index];
- switch (rnd->Uniform(support_prev_ ? 3 : 1)) {
- case 0:
- // Return an existing key
- break;
- case 1: {
- // Attempt to return something smaller than an existing key
- if (result.size() > 0 && result[result.size() - 1] > '\0'
- && (!only_support_prefix_seek_
- || options_.prefix_extractor->Transform(result).size()
- < result.size())) {
- result[result.size() - 1]--;
- }
- break;
- }
- case 2: {
- // Return something larger than an existing key
- Increment(options_.comparator, &result);
- break;
- }
- }
- return result;
- }
- }
- // Returns nullptr if not running against a DB
- DB* db() const { return constructor_->db(); }
- void RandomizedHarnessTest(size_t part, size_t total) {
- std::vector<TestArgs> args = GenerateArgList();
- assert(part);
- assert(part <= total);
- for (size_t i = 0; i < args.size(); i++) {
- if ((i % total) + 1 != part) {
- continue;
- }
- Init(args[i]);
- Random rnd(test::RandomSeed() + 5);
- for (int num_entries = 0; num_entries < 2000;
- num_entries += (num_entries < 50 ? 1 : 200)) {
- for (int e = 0; e < num_entries; e++) {
- std::string v;
- Add(test::RandomKey(&rnd, rnd.Skewed(4)),
- test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());
- }
- Test(&rnd);
- }
- }
- }
- private:
- Options options_ = Options();
- ImmutableCFOptions ioptions_;
- MutableCFOptions moptions_;
- BlockBasedTableOptions table_options_ = BlockBasedTableOptions();
- Constructor* constructor_;
- WriteBufferManager write_buffer_;
- bool support_prev_;
- bool only_support_prefix_seek_;
- std::shared_ptr<InternalKeyComparator> internal_comparator_;
- };
- static bool Between(uint64_t val, uint64_t low, uint64_t high) {
- bool result = (val >= low) && (val <= high);
- if (!result) {
- fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
- (unsigned long long)(val),
- (unsigned long long)(low),
- (unsigned long long)(high));
- }
- return result;
- }
- // Tests against all kinds of tables
- class TableTest : public testing::Test {
- public:
- const InternalKeyComparator& GetPlainInternalComparator(
- const Comparator* comp) {
- if (!plain_internal_comparator) {
- plain_internal_comparator.reset(
- new test::PlainInternalKeyComparator(comp));
- }
- return *plain_internal_comparator;
- }
- void IndexTest(BlockBasedTableOptions table_options);
- private:
- std::unique_ptr<InternalKeyComparator> plain_internal_comparator;
- };
- class GeneralTableTest : public TableTest {};
- class BlockBasedTableTest
- : public TableTest,
- virtual public ::testing::WithParamInterface<uint32_t> {
- public:
- BlockBasedTableTest() : format_(GetParam()) {
- env_ = ROCKSDB_NAMESPACE::Env::Default();
- }
- BlockBasedTableOptions GetBlockBasedTableOptions() {
- BlockBasedTableOptions options;
- options.format_version = format_;
- return options;
- }
- void SetupTracingTest(TableConstructor* c) {
- test_path_ = test::PerThreadDBPath("block_based_table_tracing_test");
- EXPECT_OK(env_->CreateDir(test_path_));
- trace_file_path_ = test_path_ + "/block_cache_trace_file";
- TraceOptions trace_opt;
- std::unique_ptr<TraceWriter> trace_writer;
- EXPECT_OK(NewFileTraceWriter(env_, EnvOptions(), trace_file_path_,
- &trace_writer));
- c->block_cache_tracer_.StartTrace(env_, trace_opt, std::move(trace_writer));
- {
- std::string user_key = "k01";
- InternalKey internal_key(user_key, 0, kTypeValue);
- std::string encoded_key = internal_key.Encode().ToString();
- c->Add(encoded_key, kDummyValue);
- }
- {
- std::string user_key = "k02";
- InternalKey internal_key(user_key, 0, kTypeValue);
- std::string encoded_key = internal_key.Encode().ToString();
- c->Add(encoded_key, kDummyValue);
- }
- }
- void VerifyBlockAccessTrace(
- TableConstructor* c,
- const std::vector<BlockCacheTraceRecord>& expected_records) {
- c->block_cache_tracer_.EndTrace();
- std::unique_ptr<TraceReader> trace_reader;
- Status s =
- NewFileTraceReader(env_, EnvOptions(), trace_file_path_, &trace_reader);
- EXPECT_OK(s);
- BlockCacheTraceReader reader(std::move(trace_reader));
- BlockCacheTraceHeader header;
- EXPECT_OK(reader.ReadHeader(&header));
- uint32_t index = 0;
- while (s.ok()) {
- BlockCacheTraceRecord access;
- s = reader.ReadAccess(&access);
- if (!s.ok()) {
- break;
- }
- ASSERT_LT(index, expected_records.size());
- EXPECT_NE("", access.block_key);
- EXPECT_EQ(access.block_type, expected_records[index].block_type);
- EXPECT_GT(access.block_size, 0);
- EXPECT_EQ(access.caller, expected_records[index].caller);
- EXPECT_EQ(access.no_insert, expected_records[index].no_insert);
- EXPECT_EQ(access.is_cache_hit, expected_records[index].is_cache_hit);
- // Get
- if (access.caller == TableReaderCaller::kUserGet) {
- EXPECT_EQ(access.referenced_key,
- expected_records[index].referenced_key);
- EXPECT_EQ(access.get_id, expected_records[index].get_id);
- EXPECT_EQ(access.get_from_user_specified_snapshot,
- expected_records[index].get_from_user_specified_snapshot);
- if (access.block_type == TraceType::kBlockTraceDataBlock) {
- EXPECT_GT(access.referenced_data_size, 0);
- EXPECT_GT(access.num_keys_in_block, 0);
- EXPECT_EQ(access.referenced_key_exist_in_block,
- expected_records[index].referenced_key_exist_in_block);
- }
- } else {
- EXPECT_EQ(access.referenced_key, "");
- EXPECT_EQ(access.get_id, 0);
- EXPECT_TRUE(access.get_from_user_specified_snapshot == Boolean::kFalse);
- EXPECT_EQ(access.referenced_data_size, 0);
- EXPECT_EQ(access.num_keys_in_block, 0);
- EXPECT_TRUE(access.referenced_key_exist_in_block == Boolean::kFalse);
- }
- index++;
- }
- EXPECT_EQ(index, expected_records.size());
- EXPECT_OK(env_->DeleteFile(trace_file_path_));
- EXPECT_OK(env_->DeleteDir(test_path_));
- }
- protected:
- uint64_t IndexUncompressedHelper(bool indexCompress);
- private:
- uint32_t format_;
- Env* env_;
- std::string trace_file_path_;
- std::string test_path_;
- };
- class PlainTableTest : public TableTest {};
- class TablePropertyTest : public testing::Test {};
- class BBTTailPrefetchTest : public TableTest {};
- // The helper class to test the file checksum
- class FileChecksumTestHelper {
- public:
- FileChecksumTestHelper(bool convert_to_internal_key = false)
- : convert_to_internal_key_(convert_to_internal_key) {
- sink_ = new test::StringSink();
- }
- ~FileChecksumTestHelper() {}
- void CreateWriteableFile() {
- file_writer_.reset(test::GetWritableFileWriter(sink_, "" /* don't care */));
- }
- void SetFileChecksumFunc(FileChecksumFunc* checksum_func) {
- if (file_writer_ != nullptr) {
- file_writer_->TEST_SetFileChecksumFunc(checksum_func);
- }
- }
- WritableFileWriter* GetFileWriter() { return file_writer_.get(); }
- Status ResetTableBuilder(std::unique_ptr<TableBuilder>&& builder) {
- assert(builder != nullptr);
- table_builder_ = std::move(builder);
- return Status::OK();
- }
- void AddKVtoKVMap(int num_entries) {
- Random rnd(test::RandomSeed());
- for (int i = 0; i < num_entries; i++) {
- std::string v;
- test::RandomString(&rnd, 100, &v);
- kv_map_[test::RandomKey(&rnd, 20)] = v;
- }
- }
- Status WriteKVAndFlushTable() {
- for (const auto kv : kv_map_) {
- if (convert_to_internal_key_) {
- ParsedInternalKey ikey(kv.first, kMaxSequenceNumber, kTypeValue);
- std::string encoded;
- AppendInternalKey(&encoded, ikey);
- table_builder_->Add(encoded, kv.second);
- } else {
- table_builder_->Add(kv.first, kv.second);
- }
- EXPECT_TRUE(table_builder_->status().ok());
- }
- Status s = table_builder_->Finish();
- file_writer_->Flush();
- EXPECT_TRUE(s.ok());
- EXPECT_EQ(sink_->contents().size(), table_builder_->FileSize());
- return s;
- }
- std::string GetFileChecksum() { return table_builder_->GetFileChecksum(); }
- const char* GetFileChecksumFuncName() {
- return table_builder_->GetFileChecksumFuncName();
- }
- Status CalculateFileChecksum(FileChecksumFunc* file_checksum_func,
- std::string* checksum) {
- assert(file_checksum_func != nullptr);
- cur_uniq_id_ = checksum_uniq_id_++;
- test::StringSink* ss_rw =
- ROCKSDB_NAMESPACE::test::GetStringSinkFromLegacyWriter(
- file_writer_.get());
- file_reader_.reset(test::GetRandomAccessFileReader(
- new test::StringSource(ss_rw->contents())));
- std::unique_ptr<char[]> scratch(new char[2048]);
- Slice result;
- uint64_t offset = 0;
- std::string tmp_checksum;
- bool first_read = true;
- Status s;
- s = file_reader_->Read(offset, 2048, &result, scratch.get(), false);
- if (!s.ok()) {
- return s;
- }
- while (result.size() != 0) {
- if (first_read) {
- first_read = false;
- tmp_checksum = file_checksum_func->Value(scratch.get(), result.size());
- } else {
- tmp_checksum = file_checksum_func->Extend(tmp_checksum, scratch.get(),
- result.size());
- }
- offset += static_cast<uint64_t>(result.size());
- s = file_reader_->Read(offset, 2048, &result, scratch.get(), false);
- if (!s.ok()) {
- return s;
- }
- }
- EXPECT_EQ(offset, static_cast<uint64_t>(table_builder_->FileSize()));
- *checksum = tmp_checksum;
- return Status::OK();
- }
- private:
- bool convert_to_internal_key_;
- uint64_t cur_uniq_id_;
- std::unique_ptr<WritableFileWriter> file_writer_;
- std::unique_ptr<RandomAccessFileReader> file_reader_;
- std::unique_ptr<TableBuilder> table_builder_;
- stl_wrappers::KVMap kv_map_;
- test::StringSink* sink_;
- static uint64_t checksum_uniq_id_;
- };
- uint64_t FileChecksumTestHelper::checksum_uniq_id_ = 1;
- INSTANTIATE_TEST_CASE_P(FormatDef, BlockBasedTableTest,
- testing::Values(test::kDefaultFormatVersion));
- INSTANTIATE_TEST_CASE_P(FormatLatest, BlockBasedTableTest,
- testing::Values(test::kLatestFormatVersion));
- // This test serves as the living tutorial for the prefix scan of user collected
- // properties.
- TEST_F(TablePropertyTest, PrefixScanTest) {
- UserCollectedProperties props{{"num.111.1", "1"},
- {"num.111.2", "2"},
- {"num.111.3", "3"},
- {"num.333.1", "1"},
- {"num.333.2", "2"},
- {"num.333.3", "3"},
- {"num.555.1", "1"},
- {"num.555.2", "2"},
- {"num.555.3", "3"}, };
- // prefixes that exist
- for (const std::string& prefix : {"num.111", "num.333", "num.555"}) {
- int num = 0;
- for (auto pos = props.lower_bound(prefix);
- pos != props.end() &&
- pos->first.compare(0, prefix.size(), prefix) == 0;
- ++pos) {
- ++num;
- auto key = prefix + "." + ToString(num);
- ASSERT_EQ(key, pos->first);
- ASSERT_EQ(ToString(num), pos->second);
- }
- ASSERT_EQ(3, num);
- }
- // prefixes that don't exist
- for (const std::string& prefix :
- {"num.000", "num.222", "num.444", "num.666"}) {
- auto pos = props.lower_bound(prefix);
- ASSERT_TRUE(pos == props.end() ||
- pos->first.compare(0, prefix.size(), prefix) != 0);
- }
- }
- // This test include all the basic checks except those for index size and block
- // size, which will be conducted in separated unit tests.
- TEST_P(BlockBasedTableTest, BasicBlockBasedTableProperties) {
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("a1", "val1");
- c.Add("b2", "val2");
- c.Add("c3", "val3");
- c.Add("d4", "val4");
- c.Add("e5", "val5");
- c.Add("f6", "val6");
- c.Add("g7", "val7");
- c.Add("h8", "val8");
- c.Add("j9", "val9");
- uint64_t diff_internal_user_bytes = 9 * 8; // 8 is seq size, 9 k-v totally
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- options.compression = kNoCompression;
- options.statistics = CreateDBStatistics();
- options.statistics->set_stats_level(StatsLevel::kAll);
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_restart_interval = 1;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- ioptions.statistics = options.statistics.get();
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_NOT_COMPRESSED), 0);
- auto& props = *c.GetTableReader()->GetTableProperties();
- ASSERT_EQ(kvmap.size(), props.num_entries);
- auto raw_key_size = kvmap.size() * 2ul;
- auto raw_value_size = kvmap.size() * 4ul;
- ASSERT_EQ(raw_key_size + diff_internal_user_bytes, props.raw_key_size);
- ASSERT_EQ(raw_value_size, props.raw_value_size);
- ASSERT_EQ(1ul, props.num_data_blocks);
- ASSERT_EQ("", props.filter_policy_name); // no filter policy is used
- // Verify data size.
- BlockBuilder block_builder(1);
- for (const auto& item : kvmap) {
- block_builder.Add(item.first, item.second);
- }
- Slice content = block_builder.Finish();
- ASSERT_EQ(content.size() + kBlockTrailerSize + diff_internal_user_bytes,
- props.data_size);
- c.ResetTableReader();
- }
- #ifdef SNAPPY
- uint64_t BlockBasedTableTest::IndexUncompressedHelper(bool compressed) {
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- constexpr size_t kNumKeys = 10000;
- for (size_t k = 0; k < kNumKeys; ++k) {
- c.Add("key" + ToString(k), "val" + ToString(k));
- }
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- options.compression = kSnappyCompression;
- options.statistics = CreateDBStatistics();
- options.statistics->set_stats_level(StatsLevel::kAll);
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_restart_interval = 1;
- table_options.enable_index_compression = compressed;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- ioptions.statistics = options.statistics.get();
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- c.ResetTableReader();
- return options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
- }
- TEST_P(BlockBasedTableTest, IndexUncompressed) {
- uint64_t tbl1_compressed_cnt = IndexUncompressedHelper(true);
- uint64_t tbl2_compressed_cnt = IndexUncompressedHelper(false);
- // tbl1_compressed_cnt should include 1 index block
- EXPECT_EQ(tbl2_compressed_cnt + 1, tbl1_compressed_cnt);
- }
- #endif // SNAPPY
- TEST_P(BlockBasedTableTest, BlockBasedTableProperties2) {
- TableConstructor c(&reverse_key_comparator);
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- {
- Options options;
- options.compression = CompressionType::kNoCompression;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- auto& props = *c.GetTableReader()->GetTableProperties();
- // Default comparator
- ASSERT_EQ("leveldb.BytewiseComparator", props.comparator_name);
- // No merge operator
- ASSERT_EQ("nullptr", props.merge_operator_name);
- // No prefix extractor
- ASSERT_EQ("nullptr", props.prefix_extractor_name);
- // No property collectors
- ASSERT_EQ("[]", props.property_collectors_names);
- // No filter policy is used
- ASSERT_EQ("", props.filter_policy_name);
- // Compression type == that set:
- ASSERT_EQ("NoCompression", props.compression_name);
- c.ResetTableReader();
- }
- {
- Options options;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.comparator = &reverse_key_comparator;
- options.merge_operator = MergeOperators::CreateUInt64AddOperator();
- options.prefix_extractor.reset(NewNoopTransform());
- options.table_properties_collector_factories.emplace_back(
- new DummyPropertiesCollectorFactory1());
- options.table_properties_collector_factories.emplace_back(
- new DummyPropertiesCollectorFactory2());
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- auto& props = *c.GetTableReader()->GetTableProperties();
- ASSERT_EQ("rocksdb.ReverseBytewiseComparator", props.comparator_name);
- ASSERT_EQ("UInt64AddOperator", props.merge_operator_name);
- ASSERT_EQ("rocksdb.Noop", props.prefix_extractor_name);
- ASSERT_EQ("[DummyPropertiesCollector1,DummyPropertiesCollector2]",
- props.property_collectors_names);
- ASSERT_EQ("", props.filter_policy_name); // no filter policy is used
- c.ResetTableReader();
- }
- }
- TEST_P(BlockBasedTableTest, RangeDelBlock) {
- TableConstructor c(BytewiseComparator());
- std::vector<std::string> keys = {"1pika", "2chu"};
- std::vector<std::string> vals = {"p", "c"};
- std::vector<RangeTombstone> expected_tombstones = {
- {"1pika", "2chu", 0},
- {"2chu", "c", 1},
- {"2chu", "c", 0},
- {"c", "p", 0},
- };
- for (int i = 0; i < 2; i++) {
- RangeTombstone t(keys[i], vals[i], i);
- std::pair<InternalKey, Slice> p = t.Serialize();
- c.Add(p.first.Encode().ToString(), p.second);
- }
- std::vector<std::string> sorted_keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- options.compression = kNoCompression;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_restart_interval = 1;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- std::unique_ptr<InternalKeyComparator> internal_cmp(
- new InternalKeyComparator(options.comparator));
- c.Finish(options, ioptions, moptions, table_options, *internal_cmp,
- &sorted_keys, &kvmap);
- for (int j = 0; j < 2; ++j) {
- std::unique_ptr<InternalIterator> iter(
- c.GetTableReader()->NewRangeTombstoneIterator(ReadOptions()));
- if (j > 0) {
- // For second iteration, delete the table reader object and verify the
- // iterator can still access its metablock's range tombstones.
- c.ResetTableReader();
- }
- ASSERT_FALSE(iter->Valid());
- iter->SeekToFirst();
- ASSERT_TRUE(iter->Valid());
- for (size_t i = 0; i < expected_tombstones.size(); i++) {
- ASSERT_TRUE(iter->Valid());
- ParsedInternalKey parsed_key;
- ASSERT_TRUE(ParseInternalKey(iter->key(), &parsed_key));
- RangeTombstone t(parsed_key, iter->value());
- const auto& expected_t = expected_tombstones[i];
- ASSERT_EQ(t.start_key_, expected_t.start_key_);
- ASSERT_EQ(t.end_key_, expected_t.end_key_);
- ASSERT_EQ(t.seq_, expected_t.seq_);
- iter->Next();
- }
- ASSERT_TRUE(!iter->Valid());
- }
- }
- TEST_P(BlockBasedTableTest, FilterPolicyNameProperties) {
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("a1", "val1");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.filter_policy.reset(NewBloomFilterPolicy(10));
- Options options;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- auto& props = *c.GetTableReader()->GetTableProperties();
- ASSERT_EQ("rocksdb.BuiltinBloomFilter", props.filter_policy_name);
- c.ResetTableReader();
- }
- //
- // BlockBasedTableTest::PrefetchTest
- //
- void AssertKeysInCache(BlockBasedTable* table_reader,
- const std::vector<std::string>& keys_in_cache,
- const std::vector<std::string>& keys_not_in_cache,
- bool convert = false) {
- if (convert) {
- for (auto key : keys_in_cache) {
- InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
- ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
- }
- for (auto key : keys_not_in_cache) {
- InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
- ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
- }
- } else {
- for (auto key : keys_in_cache) {
- ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), key));
- }
- for (auto key : keys_not_in_cache) {
- ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), key));
- }
- }
- }
- void PrefetchRange(TableConstructor* c, Options* opt,
- BlockBasedTableOptions* table_options, const char* key_begin,
- const char* key_end,
- const std::vector<std::string>& keys_in_cache,
- const std::vector<std::string>& keys_not_in_cache,
- const Status expected_status = Status::OK()) {
- // reset the cache and reopen the table
- table_options->block_cache = NewLRUCache(16 * 1024 * 1024, 4);
- opt->table_factory.reset(NewBlockBasedTableFactory(*table_options));
- const ImmutableCFOptions ioptions2(*opt);
- const MutableCFOptions moptions(*opt);
- ASSERT_OK(c->Reopen(ioptions2, moptions));
- // prefetch
- auto* table_reader = dynamic_cast<BlockBasedTable*>(c->GetTableReader());
- Status s;
- std::unique_ptr<Slice> begin, end;
- std::unique_ptr<InternalKey> i_begin, i_end;
- if (key_begin != nullptr) {
- if (c->ConvertToInternalKey()) {
- i_begin.reset(new InternalKey(key_begin, kMaxSequenceNumber, kTypeValue));
- begin.reset(new Slice(i_begin->Encode()));
- } else {
- begin.reset(new Slice(key_begin));
- }
- }
- if (key_end != nullptr) {
- if (c->ConvertToInternalKey()) {
- i_end.reset(new InternalKey(key_end, kMaxSequenceNumber, kTypeValue));
- end.reset(new Slice(i_end->Encode()));
- } else {
- end.reset(new Slice(key_end));
- }
- }
- s = table_reader->Prefetch(begin.get(), end.get());
- ASSERT_TRUE(s.code() == expected_status.code());
- // assert our expectation in cache warmup
- AssertKeysInCache(table_reader, keys_in_cache, keys_not_in_cache,
- c->ConvertToInternalKey());
- c->ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, PrefetchTest) {
- // The purpose of this test is to test the prefetching operation built into
- // BlockBasedTable.
- Options opt;
- std::unique_ptr<InternalKeyComparator> ikc;
- ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
- opt.compression = kNoCompression;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_size = 1024;
- // big enough so we don't ever lose cached values.
- table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
- opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("k01", "hello");
- c.Add("k02", "hello2");
- c.Add("k03", std::string(10000, 'x'));
- c.Add("k04", std::string(200000, 'x'));
- c.Add("k05", std::string(300000, 'x'));
- c.Add("k06", "hello3");
- c.Add("k07", std::string(100000, 'x'));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(opt);
- const MutableCFOptions moptions(opt);
- c.Finish(opt, ioptions, moptions, table_options, *ikc, &keys, &kvmap);
- c.ResetTableReader();
- // We get the following data spread :
- //
- // Data block Index
- // ========================
- // [ k01 k02 k03 ] k03
- // [ k04 ] k04
- // [ k05 ] k05
- // [ k06 k07 ] k07
- // Simple
- PrefetchRange(&c, &opt, &table_options,
- /*key_range=*/"k01", "k05",
- /*keys_in_cache=*/{"k01", "k02", "k03", "k04", "k05"},
- /*keys_not_in_cache=*/{"k06", "k07"});
- PrefetchRange(&c, &opt, &table_options, "k01", "k01", {"k01", "k02", "k03"},
- {"k04", "k05", "k06", "k07"});
- // odd
- PrefetchRange(&c, &opt, &table_options, "a", "z",
- {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
- PrefetchRange(&c, &opt, &table_options, "k00", "k00", {"k01", "k02", "k03"},
- {"k04", "k05", "k06", "k07"});
- // Edge cases
- PrefetchRange(&c, &opt, &table_options, "k00", "k06",
- {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
- PrefetchRange(&c, &opt, &table_options, "k00", "zzz",
- {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
- // null keys
- PrefetchRange(&c, &opt, &table_options, nullptr, nullptr,
- {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
- PrefetchRange(&c, &opt, &table_options, "k04", nullptr,
- {"k04", "k05", "k06", "k07"}, {"k01", "k02", "k03"});
- PrefetchRange(&c, &opt, &table_options, nullptr, "k05",
- {"k01", "k02", "k03", "k04", "k05"}, {"k06", "k07"});
- // invalid
- PrefetchRange(&c, &opt, &table_options, "k06", "k00", {}, {},
- Status::InvalidArgument(Slice("k06 "), Slice("k07")));
- c.ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, TotalOrderSeekOnHashIndex) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- for (int i = 0; i <= 5; ++i) {
- Options options;
- // Make each key/value an individual block
- table_options.block_size = 64;
- switch (i) {
- case 0:
- // Binary search index
- table_options.index_type = BlockBasedTableOptions::kBinarySearch;
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- break;
- case 1:
- // Hash search index
- table_options.index_type = BlockBasedTableOptions::kHashSearch;
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- options.prefix_extractor.reset(NewFixedPrefixTransform(4));
- break;
- case 2:
- // Hash search index with hash_index_allow_collision
- table_options.index_type = BlockBasedTableOptions::kHashSearch;
- table_options.hash_index_allow_collision = true;
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- options.prefix_extractor.reset(NewFixedPrefixTransform(4));
- break;
- case 3:
- // Hash search index with filter policy
- table_options.index_type = BlockBasedTableOptions::kHashSearch;
- table_options.filter_policy.reset(NewBloomFilterPolicy(10));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- options.prefix_extractor.reset(NewFixedPrefixTransform(4));
- break;
- case 4:
- // Two-level index
- table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- break;
- case 5:
- // Binary search with first key
- table_options.index_type =
- BlockBasedTableOptions::kBinarySearchWithFirstKey;
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- break;
- }
- TableConstructor c(BytewiseComparator(),
- true /* convert_to_internal_key_ */);
- c.Add("aaaa1", std::string('a', 56));
- c.Add("bbaa1", std::string('a', 56));
- c.Add("cccc1", std::string('a', 56));
- c.Add("bbbb1", std::string('a', 56));
- c.Add("baaa1", std::string('a', 56));
- c.Add("abbb1", std::string('a', 56));
- c.Add("cccc2", std::string('a', 56));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- auto props = c.GetTableReader()->GetTableProperties();
- ASSERT_EQ(7u, props->num_data_blocks);
- auto* reader = c.GetTableReader();
- ReadOptions ro;
- ro.total_order_seek = true;
- std::unique_ptr<InternalIterator> iter(reader->NewIterator(
- ro, moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- iter->Seek(InternalKey("b", 0, kTypeValue).Encode());
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("baaa1", ExtractUserKey(iter->key()).ToString());
- iter->Next();
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
- iter->Seek(InternalKey("bb", 0, kTypeValue).Encode());
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
- iter->Next();
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
- iter->Seek(InternalKey("bbb", 0, kTypeValue).Encode());
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
- iter->Next();
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("cccc1", ExtractUserKey(iter->key()).ToString());
- }
- }
- TEST_P(BlockBasedTableTest, NoopTransformSeek) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.filter_policy.reset(NewBloomFilterPolicy(10));
- Options options;
- options.comparator = BytewiseComparator();
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- options.prefix_extractor.reset(NewNoopTransform());
- TableConstructor c(options.comparator);
- // To tickle the PrefixMayMatch bug it is important that the
- // user-key is a single byte so that the index key exactly matches
- // the user-key.
- InternalKey key("a", 1, kTypeValue);
- c.Add(key.Encode().ToString(), "b");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- const InternalKeyComparator internal_comparator(options.comparator);
- c.Finish(options, ioptions, moptions, table_options, internal_comparator,
- &keys, &kvmap);
- auto* reader = c.GetTableReader();
- for (int i = 0; i < 2; ++i) {
- ReadOptions ro;
- ro.total_order_seek = (i == 0);
- std::unique_ptr<InternalIterator> iter(reader->NewIterator(
- ro, moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- iter->Seek(key.Encode());
- ASSERT_OK(iter->status());
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("a", ExtractUserKey(iter->key()).ToString());
- }
- }
- TEST_P(BlockBasedTableTest, SkipPrefixBloomFilter) {
- // if DB is opened with a prefix extractor of a different name,
- // prefix bloom is skipped when read the file
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.filter_policy.reset(NewBloomFilterPolicy(2));
- table_options.whole_key_filtering = false;
- Options options;
- options.comparator = BytewiseComparator();
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- options.prefix_extractor.reset(NewFixedPrefixTransform(1));
- TableConstructor c(options.comparator);
- InternalKey key("abcdefghijk", 1, kTypeValue);
- c.Add(key.Encode().ToString(), "test");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- const InternalKeyComparator internal_comparator(options.comparator);
- c.Finish(options, ioptions, moptions, table_options, internal_comparator,
- &keys, &kvmap);
- // TODO(Zhongyi): update test to use MutableCFOptions
- options.prefix_extractor.reset(NewFixedPrefixTransform(9));
- const ImmutableCFOptions new_ioptions(options);
- const MutableCFOptions new_moptions(options);
- c.Reopen(new_ioptions, new_moptions);
- auto reader = c.GetTableReader();
- std::unique_ptr<InternalIterator> db_iter(reader->NewIterator(
- ReadOptions(), new_moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- // Test point lookup
- // only one kv
- for (auto& kv : kvmap) {
- db_iter->Seek(kv.first);
- ASSERT_TRUE(db_iter->Valid());
- ASSERT_OK(db_iter->status());
- ASSERT_EQ(db_iter->key(), kv.first);
- ASSERT_EQ(db_iter->value(), kv.second);
- }
- }
- static std::string RandomString(Random* rnd, int len) {
- std::string r;
- test::RandomString(rnd, len, &r);
- return r;
- }
- void AddInternalKey(TableConstructor* c, const std::string& prefix,
- std::string value = "v", int /*suffix_len*/ = 800) {
- static Random rnd(1023);
- InternalKey k(prefix + RandomString(&rnd, 800), 0, kTypeValue);
- c->Add(k.Encode().ToString(), value);
- }
- void TableTest::IndexTest(BlockBasedTableOptions table_options) {
- TableConstructor c(BytewiseComparator());
- // keys with prefix length 3, make sure the key/value is big enough to fill
- // one block
- AddInternalKey(&c, "0015");
- AddInternalKey(&c, "0035");
- AddInternalKey(&c, "0054");
- AddInternalKey(&c, "0055");
- AddInternalKey(&c, "0056");
- AddInternalKey(&c, "0057");
- AddInternalKey(&c, "0058");
- AddInternalKey(&c, "0075");
- AddInternalKey(&c, "0076");
- AddInternalKey(&c, "0095");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- options.prefix_extractor.reset(NewFixedPrefixTransform(3));
- table_options.block_size = 1700;
- table_options.block_cache = NewLRUCache(1024, 4);
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
- &kvmap);
- auto reader = c.GetTableReader();
- auto props = reader->GetTableProperties();
- ASSERT_EQ(5u, props->num_data_blocks);
- // TODO(Zhongyi): update test to use MutableCFOptions
- std::unique_ptr<InternalIterator> index_iter(reader->NewIterator(
- ReadOptions(), moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- // -- Find keys do not exist, but have common prefix.
- std::vector<std::string> prefixes = {"001", "003", "005", "007", "009"};
- std::vector<std::string> lower_bound = {keys[0], keys[1], keys[2],
- keys[7], keys[9], };
- // find the lower bound of the prefix
- for (size_t i = 0; i < prefixes.size(); ++i) {
- index_iter->Seek(InternalKey(prefixes[i], 0, kTypeValue).Encode());
- ASSERT_OK(index_iter->status());
- ASSERT_TRUE(index_iter->Valid());
- // seek the first element in the block
- ASSERT_EQ(lower_bound[i], index_iter->key().ToString());
- ASSERT_EQ("v", index_iter->value().ToString());
- }
- // find the upper bound of prefixes
- std::vector<std::string> upper_bound = {keys[1], keys[2], keys[7], keys[9], };
- // find existing keys
- for (const auto& item : kvmap) {
- auto ukey = ExtractUserKey(item.first).ToString();
- index_iter->Seek(ukey);
- // ASSERT_OK(regular_iter->status());
- ASSERT_OK(index_iter->status());
- // ASSERT_TRUE(regular_iter->Valid());
- ASSERT_TRUE(index_iter->Valid());
- ASSERT_EQ(item.first, index_iter->key().ToString());
- ASSERT_EQ(item.second, index_iter->value().ToString());
- }
- for (size_t i = 0; i < prefixes.size(); ++i) {
- // the key is greater than any existing keys.
- auto key = prefixes[i] + "9";
- index_iter->Seek(InternalKey(key, 0, kTypeValue).Encode());
- ASSERT_TRUE(index_iter->status().ok() || index_iter->status().IsNotFound());
- ASSERT_TRUE(!index_iter->status().IsNotFound() || !index_iter->Valid());
- if (i == prefixes.size() - 1) {
- // last key
- ASSERT_TRUE(!index_iter->Valid());
- } else {
- ASSERT_TRUE(index_iter->Valid());
- // seek the first element in the block
- ASSERT_EQ(upper_bound[i], index_iter->key().ToString());
- ASSERT_EQ("v", index_iter->value().ToString());
- }
- }
- // find keys with prefix that don't match any of the existing prefixes.
- std::vector<std::string> non_exist_prefixes = {"002", "004", "006", "008"};
- for (const auto& prefix : non_exist_prefixes) {
- index_iter->Seek(InternalKey(prefix, 0, kTypeValue).Encode());
- // regular_iter->Seek(prefix);
- ASSERT_OK(index_iter->status());
- // Seek to non-existing prefixes should yield either invalid, or a
- // key with prefix greater than the target.
- if (index_iter->Valid()) {
- Slice ukey = ExtractUserKey(index_iter->key());
- Slice ukey_prefix = options.prefix_extractor->Transform(ukey);
- ASSERT_TRUE(BytewiseComparator()->Compare(prefix, ukey_prefix) < 0);
- }
- }
- for (const auto& prefix : non_exist_prefixes) {
- index_iter->SeekForPrev(InternalKey(prefix, 0, kTypeValue).Encode());
- // regular_iter->Seek(prefix);
- ASSERT_OK(index_iter->status());
- // Seek to non-existing prefixes should yield either invalid, or a
- // key with prefix greater than the target.
- if (index_iter->Valid()) {
- Slice ukey = ExtractUserKey(index_iter->key());
- Slice ukey_prefix = options.prefix_extractor->Transform(ukey);
- ASSERT_TRUE(BytewiseComparator()->Compare(prefix, ukey_prefix) > 0);
- }
- }
- c.ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, BinaryIndexTest) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.index_type = BlockBasedTableOptions::kBinarySearch;
- IndexTest(table_options);
- }
- TEST_P(BlockBasedTableTest, HashIndexTest) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.index_type = BlockBasedTableOptions::kHashSearch;
- IndexTest(table_options);
- }
- TEST_P(BlockBasedTableTest, PartitionIndexTest) {
- const int max_index_keys = 5;
- const int est_max_index_key_value_size = 32;
- const int est_max_index_size = max_index_keys * est_max_index_key_value_size;
- for (int i = 1; i <= est_max_index_size + 1; i++) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
- table_options.metadata_block_size = i;
- IndexTest(table_options);
- }
- }
- TEST_P(BlockBasedTableTest, IndexSeekOptimizationIncomplete) {
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- Options options;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- TableConstructor c(BytewiseComparator());
- AddInternalKey(&c, "pika");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
- &kvmap);
- ASSERT_EQ(1, keys.size());
- auto reader = c.GetTableReader();
- ReadOptions ropt;
- ropt.read_tier = ReadTier::kBlockCacheTier;
- std::unique_ptr<InternalIterator> iter(reader->NewIterator(
- ropt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- auto ikey = [](Slice user_key) {
- return InternalKey(user_key, 0, kTypeValue).Encode().ToString();
- };
- iter->Seek(ikey("pika"));
- ASSERT_FALSE(iter->Valid());
- ASSERT_TRUE(iter->status().IsIncomplete());
- // This used to crash at some point.
- iter->Seek(ikey("pika"));
- ASSERT_FALSE(iter->Valid());
- ASSERT_TRUE(iter->status().IsIncomplete());
- }
- TEST_P(BlockBasedTableTest, BinaryIndexWithFirstKey1) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.index_type = BlockBasedTableOptions::kBinarySearchWithFirstKey;
- IndexTest(table_options);
- }
- class CustomFlushBlockPolicy : public FlushBlockPolicyFactory,
- public FlushBlockPolicy {
- public:
- explicit CustomFlushBlockPolicy(std::vector<int> keys_per_block)
- : keys_per_block_(keys_per_block) {}
- const char* Name() const override { return "table_test"; }
- FlushBlockPolicy* NewFlushBlockPolicy(const BlockBasedTableOptions&,
- const BlockBuilder&) const override {
- return new CustomFlushBlockPolicy(keys_per_block_);
- }
- bool Update(const Slice&, const Slice&) override {
- if (keys_in_current_block_ >= keys_per_block_.at(current_block_idx_)) {
- ++current_block_idx_;
- keys_in_current_block_ = 1;
- return true;
- }
- ++keys_in_current_block_;
- return false;
- }
- std::vector<int> keys_per_block_;
- int current_block_idx_ = 0;
- int keys_in_current_block_ = 0;
- };
- TEST_P(BlockBasedTableTest, BinaryIndexWithFirstKey2) {
- for (int use_first_key = 0; use_first_key < 2; ++use_first_key) {
- SCOPED_TRACE("use_first_key = " + std::to_string(use_first_key));
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.index_type =
- use_first_key ? BlockBasedTableOptions::kBinarySearchWithFirstKey
- : BlockBasedTableOptions::kBinarySearch;
- table_options.block_cache = NewLRUCache(10000); // fits all blocks
- table_options.index_shortening =
- BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
- table_options.flush_block_policy_factory =
- std::make_shared<CustomFlushBlockPolicy>(std::vector<int>{2, 1, 3, 2});
- Options options;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.statistics = CreateDBStatistics();
- Statistics* stats = options.statistics.get();
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- TableConstructor c(BytewiseComparator());
- // Block 0.
- AddInternalKey(&c, "aaaa", "v0");
- AddInternalKey(&c, "aaac", "v1");
- // Block 1.
- AddInternalKey(&c, "aaca", "v2");
- // Block 2.
- AddInternalKey(&c, "caaa", "v3");
- AddInternalKey(&c, "caac", "v4");
- AddInternalKey(&c, "caae", "v5");
- // Block 3.
- AddInternalKey(&c, "ccaa", "v6");
- AddInternalKey(&c, "ccac", "v7");
- // Write the file.
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
- &kvmap);
- ASSERT_EQ(8, keys.size());
- auto reader = c.GetTableReader();
- auto props = reader->GetTableProperties();
- ASSERT_EQ(4u, props->num_data_blocks);
- std::unique_ptr<InternalIterator> iter(reader->NewIterator(
- ReadOptions(), /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- // Shouldn't have read data blocks before iterator is seeked.
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- auto ikey = [](Slice user_key) {
- return InternalKey(user_key, 0, kTypeValue).Encode().ToString();
- };
- // Seek to a key between blocks. If index contains first key, we shouldn't
- // read any data blocks until value is requested.
- iter->Seek(ikey("aaba"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[2], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 0 : 1,
- stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ("v2", iter->value().ToString());
- EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Seek to the middle of a block. The block should be read right away.
- iter->Seek(ikey("caab"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[4], iter->key().ToString());
- EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ("v4", iter->value().ToString());
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Seek to just before the same block and don't access value.
- // The iterator should keep pinning the block contents.
- iter->Seek(ikey("baaa"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[3], iter->key().ToString());
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Seek to the same block again to check that the block is still pinned.
- iter->Seek(ikey("caae"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[5], iter->key().ToString());
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ("v5", iter->value().ToString());
- EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Step forward and fall through to the next block. Don't access value.
- iter->Next();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[6], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 2 : 3,
- stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Step forward again. Block should be read.
- iter->Next();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[7], iter->key().ToString());
- EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ("v7", iter->value().ToString());
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Step forward and reach the end.
- iter->Next();
- EXPECT_FALSE(iter->Valid());
- EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Seek to a single-key block and step forward without accessing value.
- iter->Seek(ikey("aaca"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[2], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 0 : 1,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- iter->Next();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[3], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 1 : 2,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ("v3", iter->value().ToString());
- EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- // Seek between blocks and step back without accessing value.
- iter->Seek(ikey("aaca"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[2], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 2 : 3,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- iter->Prev();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[1], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 2 : 3,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // All blocks are in cache now, there'll be no more misses ever.
- EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ("v1", iter->value().ToString());
- // Next into the next block again.
- iter->Next();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[2], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 2 : 4,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Seek to first and step back without accessing value.
- iter->SeekToFirst();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[0], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 2 : 5,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- iter->Prev();
- EXPECT_FALSE(iter->Valid());
- EXPECT_EQ(use_first_key ? 2 : 5,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- // Do some SeekForPrev() and SeekToLast() just to cover all methods.
- iter->SeekForPrev(ikey("caad"));
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[4], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 3 : 6,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ("v4", iter->value().ToString());
- EXPECT_EQ(use_first_key ? 3 : 6,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- iter->SeekToLast();
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(keys[7], iter->key().ToString());
- EXPECT_EQ(use_first_key ? 4 : 7,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ("v7", iter->value().ToString());
- EXPECT_EQ(use_first_key ? 4 : 7,
- stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- c.ResetTableReader();
- }
- }
- TEST_P(BlockBasedTableTest, BinaryIndexWithFirstKeyGlobalSeqno) {
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.index_type = BlockBasedTableOptions::kBinarySearchWithFirstKey;
- table_options.block_cache = NewLRUCache(10000);
- Options options;
- options.statistics = CreateDBStatistics();
- Statistics* stats = options.statistics.get();
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- TableConstructor c(BytewiseComparator(), /* convert_to_internal_key */ false,
- /* level */ -1, /* largest_seqno */ 42);
- c.Add(InternalKey("b", 0, kTypeValue).Encode().ToString(), "x");
- c.Add(InternalKey("c", 0, kTypeValue).Encode().ToString(), "y");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
- &kvmap);
- ASSERT_EQ(2, keys.size());
- auto reader = c.GetTableReader();
- auto props = reader->GetTableProperties();
- ASSERT_EQ(1u, props->num_data_blocks);
- std::unique_ptr<InternalIterator> iter(reader->NewIterator(
- ReadOptions(), /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- iter->Seek(InternalKey("a", 0, kTypeValue).Encode().ToString());
- ASSERT_TRUE(iter->Valid());
- EXPECT_EQ(InternalKey("b", 42, kTypeValue).Encode().ToString(),
- iter->key().ToString());
- EXPECT_NE(keys[0], iter->key().ToString());
- // Key should have been served from index, without reading data blocks.
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ("x", iter->value().ToString());
- EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
- EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
- EXPECT_EQ(InternalKey("b", 42, kTypeValue).Encode().ToString(),
- iter->key().ToString());
- c.ResetTableReader();
- }
- // It's very hard to figure out the index block size of a block accurately.
- // To make sure we get the index size, we just make sure as key number
- // grows, the filter block size also grows.
- TEST_P(BlockBasedTableTest, IndexSizeStat) {
- uint64_t last_index_size = 0;
- // we need to use random keys since the pure human readable texts
- // may be well compressed, resulting insignifcant change of index
- // block size.
- Random rnd(test::RandomSeed());
- std::vector<std::string> keys;
- for (int i = 0; i < 100; ++i) {
- keys.push_back(RandomString(&rnd, 10000));
- }
- // Each time we load one more key to the table. the table index block
- // size is expected to be larger than last time's.
- for (size_t i = 1; i < keys.size(); ++i) {
- TableConstructor c(BytewiseComparator(),
- true /* convert_to_internal_key_ */);
- for (size_t j = 0; j < i; ++j) {
- c.Add(keys[j], "val");
- }
- std::vector<std::string> ks;
- stl_wrappers::KVMap kvmap;
- Options options;
- options.compression = kNoCompression;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_restart_interval = 1;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &ks, &kvmap);
- auto index_size = c.GetTableReader()->GetTableProperties()->index_size;
- ASSERT_GT(index_size, last_index_size);
- last_index_size = index_size;
- c.ResetTableReader();
- }
- }
- TEST_P(BlockBasedTableTest, NumBlockStat) {
- Random rnd(test::RandomSeed());
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- Options options;
- options.compression = kNoCompression;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_restart_interval = 1;
- table_options.block_size = 1000;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- for (int i = 0; i < 10; ++i) {
- // the key/val are slightly smaller than block size, so that each block
- // holds roughly one key/value pair.
- c.Add(RandomString(&rnd, 900), "val");
- }
- std::vector<std::string> ks;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &ks, &kvmap);
- ASSERT_EQ(kvmap.size(),
- c.GetTableReader()->GetTableProperties()->num_data_blocks);
- c.ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, TracingGetTest) {
- TableConstructor c(BytewiseComparator());
- Options options;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- options.create_if_missing = true;
- table_options.block_cache = NewLRUCache(1024 * 1024, 0);
- table_options.cache_index_and_filter_blocks = true;
- table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- SetupTracingTest(&c);
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- std::string user_key = "k01";
- InternalKey internal_key(user_key, 0, kTypeValue);
- std::string encoded_key = internal_key.Encode().ToString();
- for (uint32_t i = 1; i <= 2; i++) {
- PinnableSlice value;
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, user_key, &value, nullptr,
- nullptr, true, nullptr, nullptr, nullptr, nullptr,
- nullptr, nullptr, /*tracing_get_id=*/i);
- get_perf_context()->Reset();
- ASSERT_OK(c.GetTableReader()->Get(ReadOptions(), encoded_key, &get_context,
- moptions.prefix_extractor.get()));
- ASSERT_EQ(get_context.State(), GetContext::kFound);
- ASSERT_EQ(value.ToString(), kDummyValue);
- }
- // Verify traces.
- std::vector<BlockCacheTraceRecord> expected_records;
- // The first two records should be prefetching index and filter blocks.
- BlockCacheTraceRecord record;
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kPrefetch;
- record.is_cache_hit = Boolean::kFalse;
- record.no_insert = Boolean::kFalse;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceFilterBlock;
- expected_records.push_back(record);
- // Then we should have three records for one index, one filter, and one data
- // block access.
- record.get_id = 1;
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kUserGet;
- record.get_from_user_specified_snapshot = Boolean::kFalse;
- record.referenced_key = encoded_key;
- record.referenced_key_exist_in_block = Boolean::kTrue;
- record.is_cache_hit = Boolean::kTrue;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceFilterBlock;
- expected_records.push_back(record);
- record.is_cache_hit = Boolean::kFalse;
- record.block_type = TraceType::kBlockTraceDataBlock;
- expected_records.push_back(record);
- // The second get should all observe cache hits.
- record.is_cache_hit = Boolean::kTrue;
- record.get_id = 2;
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kUserGet;
- record.get_from_user_specified_snapshot = Boolean::kFalse;
- record.referenced_key = encoded_key;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceFilterBlock;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceDataBlock;
- expected_records.push_back(record);
- VerifyBlockAccessTrace(&c, expected_records);
- c.ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, TracingApproximateOffsetOfTest) {
- TableConstructor c(BytewiseComparator());
- Options options;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- options.create_if_missing = true;
- table_options.block_cache = NewLRUCache(1024 * 1024, 0);
- table_options.cache_index_and_filter_blocks = true;
- table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- SetupTracingTest(&c);
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- for (uint32_t i = 1; i <= 2; i++) {
- std::string user_key = "k01";
- InternalKey internal_key(user_key, 0, kTypeValue);
- std::string encoded_key = internal_key.Encode().ToString();
- c.GetTableReader()->ApproximateOffsetOf(
- encoded_key, TableReaderCaller::kUserApproximateSize);
- }
- // Verify traces.
- std::vector<BlockCacheTraceRecord> expected_records;
- // The first two records should be prefetching index and filter blocks.
- BlockCacheTraceRecord record;
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kPrefetch;
- record.is_cache_hit = Boolean::kFalse;
- record.no_insert = Boolean::kFalse;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceFilterBlock;
- expected_records.push_back(record);
- // Then we should have two records for only index blocks.
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kUserApproximateSize;
- record.is_cache_hit = Boolean::kTrue;
- expected_records.push_back(record);
- expected_records.push_back(record);
- VerifyBlockAccessTrace(&c, expected_records);
- c.ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, TracingIterator) {
- TableConstructor c(BytewiseComparator());
- Options options;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- options.create_if_missing = true;
- table_options.block_cache = NewLRUCache(1024 * 1024, 0);
- table_options.cache_index_and_filter_blocks = true;
- table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- SetupTracingTest(&c);
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- for (uint32_t i = 1; i <= 2; i++) {
- std::unique_ptr<InternalIterator> iter(c.GetTableReader()->NewIterator(
- ReadOptions(), moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUserIterator));
- iter->SeekToFirst();
- while (iter->Valid()) {
- iter->key();
- iter->value();
- iter->Next();
- }
- ASSERT_OK(iter->status());
- iter.reset();
- }
- // Verify traces.
- std::vector<BlockCacheTraceRecord> expected_records;
- // The first two records should be prefetching index and filter blocks.
- BlockCacheTraceRecord record;
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kPrefetch;
- record.is_cache_hit = Boolean::kFalse;
- record.no_insert = Boolean::kFalse;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceFilterBlock;
- expected_records.push_back(record);
- // Then we should have three records for index and two data block access.
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.caller = TableReaderCaller::kUserIterator;
- record.is_cache_hit = Boolean::kTrue;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceDataBlock;
- record.is_cache_hit = Boolean::kFalse;
- expected_records.push_back(record);
- expected_records.push_back(record);
- // When we iterate this file for the second time, we should observe all cache
- // hits.
- record.block_type = TraceType::kBlockTraceIndexBlock;
- record.is_cache_hit = Boolean::kTrue;
- expected_records.push_back(record);
- record.block_type = TraceType::kBlockTraceDataBlock;
- expected_records.push_back(record);
- expected_records.push_back(record);
- VerifyBlockAccessTrace(&c, expected_records);
- c.ResetTableReader();
- }
- // A simple tool that takes the snapshot of block cache statistics.
- class BlockCachePropertiesSnapshot {
- public:
- explicit BlockCachePropertiesSnapshot(Statistics* statistics) {
- block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_MISS);
- block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_HIT);
- index_block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_INDEX_MISS);
- index_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_INDEX_HIT);
- data_block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_DATA_MISS);
- data_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_DATA_HIT);
- filter_block_cache_miss =
- statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS);
- filter_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT);
- block_cache_bytes_read = statistics->getTickerCount(BLOCK_CACHE_BYTES_READ);
- block_cache_bytes_write =
- statistics->getTickerCount(BLOCK_CACHE_BYTES_WRITE);
- }
- void AssertIndexBlockStat(int64_t expected_index_block_cache_miss,
- int64_t expected_index_block_cache_hit) {
- ASSERT_EQ(expected_index_block_cache_miss, index_block_cache_miss);
- ASSERT_EQ(expected_index_block_cache_hit, index_block_cache_hit);
- }
- void AssertFilterBlockStat(int64_t expected_filter_block_cache_miss,
- int64_t expected_filter_block_cache_hit) {
- ASSERT_EQ(expected_filter_block_cache_miss, filter_block_cache_miss);
- ASSERT_EQ(expected_filter_block_cache_hit, filter_block_cache_hit);
- }
- // Check if the fetched props matches the expected ones.
- // TODO(kailiu) Use this only when you disabled filter policy!
- void AssertEqual(int64_t expected_index_block_cache_miss,
- int64_t expected_index_block_cache_hit,
- int64_t expected_data_block_cache_miss,
- int64_t expected_data_block_cache_hit) const {
- ASSERT_EQ(expected_index_block_cache_miss, index_block_cache_miss);
- ASSERT_EQ(expected_index_block_cache_hit, index_block_cache_hit);
- ASSERT_EQ(expected_data_block_cache_miss, data_block_cache_miss);
- ASSERT_EQ(expected_data_block_cache_hit, data_block_cache_hit);
- ASSERT_EQ(expected_index_block_cache_miss + expected_data_block_cache_miss,
- block_cache_miss);
- ASSERT_EQ(expected_index_block_cache_hit + expected_data_block_cache_hit,
- block_cache_hit);
- }
- int64_t GetCacheBytesRead() { return block_cache_bytes_read; }
- int64_t GetCacheBytesWrite() { return block_cache_bytes_write; }
- private:
- int64_t block_cache_miss = 0;
- int64_t block_cache_hit = 0;
- int64_t index_block_cache_miss = 0;
- int64_t index_block_cache_hit = 0;
- int64_t data_block_cache_miss = 0;
- int64_t data_block_cache_hit = 0;
- int64_t filter_block_cache_miss = 0;
- int64_t filter_block_cache_hit = 0;
- int64_t block_cache_bytes_read = 0;
- int64_t block_cache_bytes_write = 0;
- };
- // Make sure, by default, index/filter blocks were pre-loaded (meaning we won't
- // use block cache to store them).
- TEST_P(BlockBasedTableTest, BlockCacheDisabledTest) {
- Options options;
- options.create_if_missing = true;
- options.statistics = CreateDBStatistics();
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_cache = NewLRUCache(1024, 4);
- table_options.filter_policy.reset(NewBloomFilterPolicy(10));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("key", "value");
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- // preloading filter/index blocks is enabled.
- auto reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
- ASSERT_FALSE(reader->TEST_FilterBlockInCache());
- ASSERT_FALSE(reader->TEST_IndexBlockInCache());
- {
- // nothing happens in the beginning
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertIndexBlockStat(0, 0);
- props.AssertFilterBlockStat(0, 0);
- }
- {
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, Slice(), nullptr, nullptr,
- nullptr, true, nullptr, nullptr);
- // a hack that just to trigger BlockBasedTable::GetFilter.
- reader->Get(ReadOptions(), "non-exist-key", &get_context,
- moptions.prefix_extractor.get());
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertIndexBlockStat(0, 0);
- props.AssertFilterBlockStat(0, 0);
- }
- }
- // Due to the difficulities of the intersaction between statistics, this test
- // only tests the case when "index block is put to block cache"
- TEST_P(BlockBasedTableTest, FilterBlockInBlockCache) {
- // -- Table construction
- Options options;
- options.create_if_missing = true;
- options.statistics = CreateDBStatistics();
- // Enable the cache for index/filter blocks
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- LRUCacheOptions co;
- co.capacity = 2048;
- co.num_shard_bits = 2;
- co.metadata_charge_policy = kDontChargeCacheMetadata;
- table_options.block_cache = NewLRUCache(co);
- table_options.cache_index_and_filter_blocks = true;
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("key", "value");
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- // preloading filter/index blocks is prohibited.
- auto* reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
- ASSERT_FALSE(reader->TEST_FilterBlockInCache());
- ASSERT_TRUE(reader->TEST_IndexBlockInCache());
- // -- PART 1: Open with regular block cache.
- // Since block_cache is disabled, no cache activities will be involved.
- std::unique_ptr<InternalIterator> iter;
- int64_t last_cache_bytes_read = 0;
- // At first, no block will be accessed.
- {
- BlockCachePropertiesSnapshot props(options.statistics.get());
- // index will be added to block cache.
- props.AssertEqual(1, // index block miss
- 0, 0, 0);
- ASSERT_EQ(props.GetCacheBytesRead(), 0);
- ASSERT_EQ(props.GetCacheBytesWrite(),
- static_cast<int64_t>(table_options.block_cache->GetUsage()));
- last_cache_bytes_read = props.GetCacheBytesRead();
- }
- // Only index block will be accessed
- {
- iter.reset(c.NewIterator(moptions.prefix_extractor.get()));
- BlockCachePropertiesSnapshot props(options.statistics.get());
- // NOTE: to help better highlight the "detla" of each ticker, I use
- // <last_value> + <added_value> to indicate the increment of changed
- // value; other numbers remain the same.
- props.AssertEqual(1, 0 + 1, // index block hit
- 0, 0);
- // Cache hit, bytes read from cache should increase
- ASSERT_GT(props.GetCacheBytesRead(), last_cache_bytes_read);
- ASSERT_EQ(props.GetCacheBytesWrite(),
- static_cast<int64_t>(table_options.block_cache->GetUsage()));
- last_cache_bytes_read = props.GetCacheBytesRead();
- }
- // Only data block will be accessed
- {
- iter->SeekToFirst();
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertEqual(1, 1, 0 + 1, // data block miss
- 0);
- // Cache miss, Bytes read from cache should not change
- ASSERT_EQ(props.GetCacheBytesRead(), last_cache_bytes_read);
- ASSERT_EQ(props.GetCacheBytesWrite(),
- static_cast<int64_t>(table_options.block_cache->GetUsage()));
- last_cache_bytes_read = props.GetCacheBytesRead();
- }
- // Data block will be in cache
- {
- iter.reset(c.NewIterator(moptions.prefix_extractor.get()));
- iter->SeekToFirst();
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertEqual(1, 1 + 1, /* index block hit */
- 1, 0 + 1 /* data block hit */);
- // Cache hit, bytes read from cache should increase
- ASSERT_GT(props.GetCacheBytesRead(), last_cache_bytes_read);
- ASSERT_EQ(props.GetCacheBytesWrite(),
- static_cast<int64_t>(table_options.block_cache->GetUsage()));
- }
- // release the iterator so that the block cache can reset correctly.
- iter.reset();
- c.ResetTableReader();
- // -- PART 2: Open with very small block cache
- // In this test, no block will ever get hit since the block cache is
- // too small to fit even one entry.
- table_options.block_cache = NewLRUCache(1, 4);
- options.statistics = CreateDBStatistics();
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions2(options);
- const MutableCFOptions moptions2(options);
- c.Reopen(ioptions2, moptions2);
- {
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertEqual(1, // index block miss
- 0, 0, 0);
- // Cache miss, Bytes read from cache should not change
- ASSERT_EQ(props.GetCacheBytesRead(), 0);
- }
- {
- // Both index and data block get accessed.
- // It first cache index block then data block. But since the cache size
- // is only 1, index block will be purged after data block is inserted.
- iter.reset(c.NewIterator(moptions2.prefix_extractor.get()));
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertEqual(1 + 1, // index block miss
- 0, 0, // data block miss
- 0);
- // Cache hit, bytes read from cache should increase
- ASSERT_EQ(props.GetCacheBytesRead(), 0);
- }
- {
- // SeekToFirst() accesses data block. With similar reason, we expect data
- // block's cache miss.
- iter->SeekToFirst();
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertEqual(2, 0, 0 + 1, // data block miss
- 0);
- // Cache miss, Bytes read from cache should not change
- ASSERT_EQ(props.GetCacheBytesRead(), 0);
- }
- iter.reset();
- c.ResetTableReader();
- // -- PART 3: Open table with bloom filter enabled but not in SST file
- table_options.block_cache = NewLRUCache(4096, 4);
- table_options.cache_index_and_filter_blocks = false;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- TableConstructor c3(BytewiseComparator());
- std::string user_key = "k01";
- InternalKey internal_key(user_key, 0, kTypeValue);
- c3.Add(internal_key.Encode().ToString(), "hello");
- ImmutableCFOptions ioptions3(options);
- MutableCFOptions moptions3(options);
- // Generate table without filter policy
- c3.Finish(options, ioptions3, moptions3, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- c3.ResetTableReader();
- // Open table with filter policy
- table_options.filter_policy.reset(NewBloomFilterPolicy(1));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- options.statistics = CreateDBStatistics();
- ImmutableCFOptions ioptions4(options);
- MutableCFOptions moptions4(options);
- ASSERT_OK(c3.Reopen(ioptions4, moptions4));
- reader = dynamic_cast<BlockBasedTable*>(c3.GetTableReader());
- ASSERT_FALSE(reader->TEST_FilterBlockInCache());
- PinnableSlice value;
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, user_key, &value, nullptr,
- nullptr, true, nullptr, nullptr);
- ASSERT_OK(reader->Get(ReadOptions(), internal_key.Encode(), &get_context,
- moptions4.prefix_extractor.get()));
- ASSERT_STREQ(value.data(), "hello");
- BlockCachePropertiesSnapshot props(options.statistics.get());
- props.AssertFilterBlockStat(0, 0);
- c3.ResetTableReader();
- }
- void ValidateBlockSizeDeviation(int value, int expected) {
- BlockBasedTableOptions table_options;
- table_options.block_size_deviation = value;
- BlockBasedTableFactory* factory = new BlockBasedTableFactory(table_options);
- const BlockBasedTableOptions* normalized_table_options =
- (const BlockBasedTableOptions*)factory->GetOptions();
- ASSERT_EQ(normalized_table_options->block_size_deviation, expected);
- delete factory;
- }
- void ValidateBlockRestartInterval(int value, int expected) {
- BlockBasedTableOptions table_options;
- table_options.block_restart_interval = value;
- BlockBasedTableFactory* factory = new BlockBasedTableFactory(table_options);
- const BlockBasedTableOptions* normalized_table_options =
- (const BlockBasedTableOptions*)factory->GetOptions();
- ASSERT_EQ(normalized_table_options->block_restart_interval, expected);
- delete factory;
- }
- TEST_P(BlockBasedTableTest, InvalidOptions) {
- // invalid values for block_size_deviation (<0 or >100) are silently set to 0
- ValidateBlockSizeDeviation(-10, 0);
- ValidateBlockSizeDeviation(-1, 0);
- ValidateBlockSizeDeviation(0, 0);
- ValidateBlockSizeDeviation(1, 1);
- ValidateBlockSizeDeviation(99, 99);
- ValidateBlockSizeDeviation(100, 100);
- ValidateBlockSizeDeviation(101, 0);
- ValidateBlockSizeDeviation(1000, 0);
- // invalid values for block_restart_interval (<1) are silently set to 1
- ValidateBlockRestartInterval(-10, 1);
- ValidateBlockRestartInterval(-1, 1);
- ValidateBlockRestartInterval(0, 1);
- ValidateBlockRestartInterval(1, 1);
- ValidateBlockRestartInterval(2, 2);
- ValidateBlockRestartInterval(1000, 1000);
- }
- TEST_P(BlockBasedTableTest, BlockReadCountTest) {
- // bloom_filter_type = 0 -- block-based filter
- // bloom_filter_type = 0 -- full filter
- for (int bloom_filter_type = 0; bloom_filter_type < 2; ++bloom_filter_type) {
- for (int index_and_filter_in_cache = 0; index_and_filter_in_cache < 2;
- ++index_and_filter_in_cache) {
- Options options;
- options.create_if_missing = true;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_cache = NewLRUCache(1, 0);
- table_options.cache_index_and_filter_blocks = index_and_filter_in_cache;
- table_options.filter_policy.reset(
- NewBloomFilterPolicy(10, bloom_filter_type == 0));
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- TableConstructor c(BytewiseComparator());
- std::string user_key = "k04";
- InternalKey internal_key(user_key, 0, kTypeValue);
- std::string encoded_key = internal_key.Encode().ToString();
- c.Add(encoded_key, "hello");
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- // Generate table with filter policy
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- auto reader = c.GetTableReader();
- PinnableSlice value;
- {
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, user_key, &value, nullptr,
- nullptr, true, nullptr, nullptr);
- get_perf_context()->Reset();
- ASSERT_OK(reader->Get(ReadOptions(), encoded_key, &get_context,
- moptions.prefix_extractor.get()));
- if (index_and_filter_in_cache) {
- // data, index and filter block
- ASSERT_EQ(get_perf_context()->block_read_count, 3);
- ASSERT_EQ(get_perf_context()->index_block_read_count, 1);
- ASSERT_EQ(get_perf_context()->filter_block_read_count, 1);
- } else {
- // just the data block
- ASSERT_EQ(get_perf_context()->block_read_count, 1);
- }
- ASSERT_EQ(get_context.State(), GetContext::kFound);
- ASSERT_STREQ(value.data(), "hello");
- }
- // Get non-existing key
- user_key = "does-not-exist";
- internal_key = InternalKey(user_key, 0, kTypeValue);
- encoded_key = internal_key.Encode().ToString();
- value.Reset();
- {
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, user_key, &value, nullptr,
- nullptr, true, nullptr, nullptr);
- get_perf_context()->Reset();
- ASSERT_OK(reader->Get(ReadOptions(), encoded_key, &get_context,
- moptions.prefix_extractor.get()));
- ASSERT_EQ(get_context.State(), GetContext::kNotFound);
- }
- if (index_and_filter_in_cache) {
- if (bloom_filter_type == 0) {
- // with block-based, we read index and then the filter
- ASSERT_EQ(get_perf_context()->block_read_count, 2);
- ASSERT_EQ(get_perf_context()->index_block_read_count, 1);
- ASSERT_EQ(get_perf_context()->filter_block_read_count, 1);
- } else {
- // with full-filter, we read filter first and then we stop
- ASSERT_EQ(get_perf_context()->block_read_count, 1);
- ASSERT_EQ(get_perf_context()->filter_block_read_count, 1);
- }
- } else {
- // filter is already in memory and it figures out that the key doesn't
- // exist
- ASSERT_EQ(get_perf_context()->block_read_count, 0);
- }
- }
- }
- }
- TEST_P(BlockBasedTableTest, BlockCacheLeak) {
- // Check that when we reopen a table we don't lose access to blocks already
- // in the cache. This test checks whether the Table actually makes use of the
- // unique ID from the file.
- Options opt;
- std::unique_ptr<InternalKeyComparator> ikc;
- ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
- opt.compression = kNoCompression;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.block_size = 1024;
- // big enough so we don't ever lose cached values.
- table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
- opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("k01", "hello");
- c.Add("k02", "hello2");
- c.Add("k03", std::string(10000, 'x'));
- c.Add("k04", std::string(200000, 'x'));
- c.Add("k05", std::string(300000, 'x'));
- c.Add("k06", "hello3");
- c.Add("k07", std::string(100000, 'x'));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(opt);
- const MutableCFOptions moptions(opt);
- c.Finish(opt, ioptions, moptions, table_options, *ikc, &keys, &kvmap);
- std::unique_ptr<InternalIterator> iter(
- c.NewIterator(moptions.prefix_extractor.get()));
- iter->SeekToFirst();
- while (iter->Valid()) {
- iter->key();
- iter->value();
- iter->Next();
- }
- ASSERT_OK(iter->status());
- iter.reset();
- const ImmutableCFOptions ioptions1(opt);
- const MutableCFOptions moptions1(opt);
- ASSERT_OK(c.Reopen(ioptions1, moptions1));
- auto table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
- for (const std::string& key : keys) {
- InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
- ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
- }
- c.ResetTableReader();
- // rerun with different block cache
- table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
- opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
- const ImmutableCFOptions ioptions2(opt);
- const MutableCFOptions moptions2(opt);
- ASSERT_OK(c.Reopen(ioptions2, moptions2));
- table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
- for (const std::string& key : keys) {
- InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
- ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
- }
- c.ResetTableReader();
- }
- namespace {
- class CustomMemoryAllocator : public MemoryAllocator {
- public:
- const char* Name() const override { return "CustomMemoryAllocator"; }
- void* Allocate(size_t size) override {
- ++numAllocations;
- auto ptr = new char[size + 16];
- memcpy(ptr, "memory_allocator_", 16); // mangle first 16 bytes
- return reinterpret_cast<void*>(ptr + 16);
- }
- void Deallocate(void* p) override {
- ++numDeallocations;
- char* ptr = reinterpret_cast<char*>(p) - 16;
- delete[] ptr;
- }
- std::atomic<int> numAllocations;
- std::atomic<int> numDeallocations;
- };
- } // namespace
- TEST_P(BlockBasedTableTest, MemoryAllocator) {
- auto custom_memory_allocator = std::make_shared<CustomMemoryAllocator>();
- {
- Options opt;
- std::unique_ptr<InternalKeyComparator> ikc;
- ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
- opt.compression = kNoCompression;
- BlockBasedTableOptions table_options;
- table_options.block_size = 1024;
- LRUCacheOptions lruOptions;
- lruOptions.memory_allocator = custom_memory_allocator;
- lruOptions.capacity = 16 * 1024 * 1024;
- lruOptions.num_shard_bits = 4;
- table_options.block_cache = NewLRUCache(std::move(lruOptions));
- opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
- TableConstructor c(BytewiseComparator(),
- true /* convert_to_internal_key_ */);
- c.Add("k01", "hello");
- c.Add("k02", "hello2");
- c.Add("k03", std::string(10000, 'x'));
- c.Add("k04", std::string(200000, 'x'));
- c.Add("k05", std::string(300000, 'x'));
- c.Add("k06", "hello3");
- c.Add("k07", std::string(100000, 'x'));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(opt);
- const MutableCFOptions moptions(opt);
- c.Finish(opt, ioptions, moptions, table_options, *ikc, &keys, &kvmap);
- std::unique_ptr<InternalIterator> iter(
- c.NewIterator(moptions.prefix_extractor.get()));
- iter->SeekToFirst();
- while (iter->Valid()) {
- iter->key();
- iter->value();
- iter->Next();
- }
- ASSERT_OK(iter->status());
- }
- // out of scope, block cache should have been deleted, all allocations
- // deallocated
- EXPECT_EQ(custom_memory_allocator->numAllocations.load(),
- custom_memory_allocator->numDeallocations.load());
- // make sure that allocations actually happened through the cache allocator
- EXPECT_GT(custom_memory_allocator->numAllocations.load(), 0);
- }
- // Test the file checksum of block based table
- TEST_P(BlockBasedTableTest, NoFileChecksum) {
- Options options;
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- SequenceNumber largest_seqno = 0;
- int level = 0;
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- if (largest_seqno != 0) {
- // Pretend that it's an external file written by SstFileWriter.
- int_tbl_prop_collector_factories.emplace_back(
- new SstFileWriterPropertiesCollectorFactory(2 /* version */,
- 0 /* global_seqno*/));
- }
- std::string column_family_name;
- FileChecksumTestHelper f(true);
- f.CreateWriteableFile();
- std::unique_ptr<TableBuilder> builder;
- builder.reset(ioptions.table_factory->NewTableBuilder(
- TableBuilderOptions(ioptions, moptions, *comparator,
- &int_tbl_prop_collector_factories,
- options.compression, options.sample_for_compression,
- options.compression_opts, false /* skip_filters */,
- column_family_name, level),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- f.GetFileWriter()));
- f.ResetTableBuilder(std::move(builder));
- f.AddKVtoKVMap(1000);
- f.WriteKVAndFlushTable();
- ASSERT_STREQ(f.GetFileChecksumFuncName(),
- kUnknownFileChecksumFuncName.c_str());
- ASSERT_STREQ(f.GetFileChecksum().c_str(), kUnknownFileChecksum.c_str());
- }
- TEST_P(BlockBasedTableTest, Crc32FileChecksum) {
- Options options;
- options.sst_file_checksum_func =
- std::shared_ptr<FileChecksumFunc>(CreateFileChecksumFuncCrc32c());
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- SequenceNumber largest_seqno = 0;
- int level = 0;
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- if (largest_seqno != 0) {
- // Pretend that it's an external file written by SstFileWriter.
- int_tbl_prop_collector_factories.emplace_back(
- new SstFileWriterPropertiesCollectorFactory(2 /* version */,
- 0 /* global_seqno*/));
- }
- std::string column_family_name;
- FileChecksumTestHelper f(true);
- f.CreateWriteableFile();
- f.SetFileChecksumFunc(options.sst_file_checksum_func.get());
- std::unique_ptr<TableBuilder> builder;
- builder.reset(ioptions.table_factory->NewTableBuilder(
- TableBuilderOptions(ioptions, moptions, *comparator,
- &int_tbl_prop_collector_factories,
- options.compression, options.sample_for_compression,
- options.compression_opts, false /* skip_filters */,
- column_family_name, level),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- f.GetFileWriter()));
- f.ResetTableBuilder(std::move(builder));
- f.AddKVtoKVMap(1000);
- f.WriteKVAndFlushTable();
- ASSERT_STREQ(f.GetFileChecksumFuncName(), "FileChecksumCrc32c");
- std::string checksum;
- ASSERT_OK(
- f.CalculateFileChecksum(options.sst_file_checksum_func.get(), &checksum));
- ASSERT_STREQ(f.GetFileChecksum().c_str(), checksum.c_str());
- }
- // Plain table is not supported in ROCKSDB_LITE
- #ifndef ROCKSDB_LITE
- TEST_F(PlainTableTest, BasicPlainTableProperties) {
- PlainTableOptions plain_table_options;
- plain_table_options.user_key_len = 8;
- plain_table_options.bloom_bits_per_key = 8;
- plain_table_options.hash_table_ratio = 0;
- PlainTableFactory factory(plain_table_options);
- test::StringSink sink;
- std::unique_ptr<WritableFileWriter> file_writer(
- test::GetWritableFileWriter(new test::StringSink(), "" /* don't care */));
- Options options;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- InternalKeyComparator ikc(options.comparator);
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- std::string column_family_name;
- int unknown_level = -1;
- std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
- TableBuilderOptions(
- ioptions, moptions, ikc, &int_tbl_prop_collector_factories,
- kNoCompression, 0 /* sample_for_compression */, CompressionOptions(),
- false /* skip_filters */, column_family_name, unknown_level),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- file_writer.get()));
- for (char c = 'a'; c <= 'z'; ++c) {
- std::string key(8, c);
- key.append("\1 "); // PlainTable expects internal key structure
- std::string value(28, c + 42);
- builder->Add(key, value);
- }
- ASSERT_OK(builder->Finish());
- file_writer->Flush();
- test::StringSink* ss =
- ROCKSDB_NAMESPACE::test::GetStringSinkFromLegacyWriter(file_writer.get());
- std::unique_ptr<RandomAccessFileReader> file_reader(
- test::GetRandomAccessFileReader(
- new test::StringSource(ss->contents(), 72242, true)));
- TableProperties* props = nullptr;
- auto s = ReadTableProperties(file_reader.get(), ss->contents().size(),
- kPlainTableMagicNumber, ioptions,
- &props, true /* compression_type_missing */);
- std::unique_ptr<TableProperties> props_guard(props);
- ASSERT_OK(s);
- ASSERT_EQ(0ul, props->index_size);
- ASSERT_EQ(0ul, props->filter_size);
- ASSERT_EQ(16ul * 26, props->raw_key_size);
- ASSERT_EQ(28ul * 26, props->raw_value_size);
- ASSERT_EQ(26ul, props->num_entries);
- ASSERT_EQ(1ul, props->num_data_blocks);
- }
- TEST_F(PlainTableTest, NoFileChecksum) {
- PlainTableOptions plain_table_options;
- plain_table_options.user_key_len = 20;
- plain_table_options.bloom_bits_per_key = 8;
- plain_table_options.hash_table_ratio = 0;
- PlainTableFactory factory(plain_table_options);
- Options options;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- InternalKeyComparator ikc(options.comparator);
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- std::string column_family_name;
- int unknown_level = -1;
- FileChecksumTestHelper f(true);
- f.CreateWriteableFile();
- std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
- TableBuilderOptions(
- ioptions, moptions, ikc, &int_tbl_prop_collector_factories,
- kNoCompression, 0 /* sample_for_compression */, CompressionOptions(),
- false /* skip_filters */, column_family_name, unknown_level),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- f.GetFileWriter()));
- f.ResetTableBuilder(std::move(builder));
- f.AddKVtoKVMap(1000);
- f.WriteKVAndFlushTable();
- ASSERT_STREQ(f.GetFileChecksumFuncName(),
- kUnknownFileChecksumFuncName.c_str());
- EXPECT_EQ(f.GetFileChecksum(), kUnknownFileChecksum.c_str());
- }
- TEST_F(PlainTableTest, Crc32FileChecksum) {
- PlainTableOptions plain_table_options;
- plain_table_options.user_key_len = 20;
- plain_table_options.bloom_bits_per_key = 8;
- plain_table_options.hash_table_ratio = 0;
- PlainTableFactory factory(plain_table_options);
- Options options;
- options.sst_file_checksum_func =
- std::shared_ptr<FileChecksumFunc>(CreateFileChecksumFuncCrc32c());
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- InternalKeyComparator ikc(options.comparator);
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- std::string column_family_name;
- int unknown_level = -1;
- FileChecksumTestHelper f(true);
- f.CreateWriteableFile();
- f.SetFileChecksumFunc(options.sst_file_checksum_func.get());
- std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
- TableBuilderOptions(
- ioptions, moptions, ikc, &int_tbl_prop_collector_factories,
- kNoCompression, 0 /* sample_for_compression */, CompressionOptions(),
- false /* skip_filters */, column_family_name, unknown_level),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- f.GetFileWriter()));
- f.ResetTableBuilder(std::move(builder));
- f.AddKVtoKVMap(1000);
- f.WriteKVAndFlushTable();
- ASSERT_STREQ(f.GetFileChecksumFuncName(), "FileChecksumCrc32c");
- std::string checksum;
- ASSERT_OK(
- f.CalculateFileChecksum(options.sst_file_checksum_func.get(), &checksum));
- EXPECT_STREQ(f.GetFileChecksum().c_str(), checksum.c_str());
- }
- #endif // !ROCKSDB_LITE
- TEST_F(GeneralTableTest, ApproximateOffsetOfPlain) {
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("k01", "hello");
- c.Add("k02", "hello2");
- c.Add("k03", std::string(10000, 'x'));
- c.Add("k04", std::string(200000, 'x'));
- c.Add("k05", std::string(300000, 'x'));
- c.Add("k06", "hello3");
- c.Add("k07", std::string(100000, 'x'));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- test::PlainInternalKeyComparator internal_comparator(options.comparator);
- options.compression = kNoCompression;
- BlockBasedTableOptions table_options;
- table_options.block_size = 1024;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options, internal_comparator,
- &keys, &kvmap);
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000));
- // k04 and k05 will be in two consecutive blocks, the index is
- // an arbitrary slice between k04 and k05, either before or after k04a
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 10000, 211000));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000));
- c.ResetTableReader();
- }
- static void DoCompressionTest(CompressionType comp) {
- Random rnd(301);
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- std::string tmp;
- c.Add("k01", "hello");
- c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
- c.Add("k03", "hello3");
- c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- test::PlainInternalKeyComparator ikc(options.comparator);
- options.compression = comp;
- BlockBasedTableOptions table_options;
- table_options.block_size = 1024;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options, ikc, &keys, &kvmap);
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3500));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3500));
- ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6500));
- c.ResetTableReader();
- }
- TEST_F(GeneralTableTest, ApproximateOffsetOfCompressed) {
- std::vector<CompressionType> compression_state;
- if (!Snappy_Supported()) {
- fprintf(stderr, "skipping snappy compression tests\n");
- } else {
- compression_state.push_back(kSnappyCompression);
- }
- if (!Zlib_Supported()) {
- fprintf(stderr, "skipping zlib compression tests\n");
- } else {
- compression_state.push_back(kZlibCompression);
- }
- // TODO(kailiu) DoCompressionTest() doesn't work with BZip2.
- /*
- if (!BZip2_Supported()) {
- fprintf(stderr, "skipping bzip2 compression tests\n");
- } else {
- compression_state.push_back(kBZip2Compression);
- }
- */
- if (!LZ4_Supported()) {
- fprintf(stderr, "skipping lz4 and lz4hc compression tests\n");
- } else {
- compression_state.push_back(kLZ4Compression);
- compression_state.push_back(kLZ4HCCompression);
- }
- if (!XPRESS_Supported()) {
- fprintf(stderr, "skipping xpress and xpress compression tests\n");
- }
- else {
- compression_state.push_back(kXpressCompression);
- }
- for (auto state : compression_state) {
- DoCompressionTest(state);
- }
- }
- #ifndef ROCKSDB_VALGRIND_RUN
- // RandomizedHarnessTest is very slow for certain combination of arguments
- // Split into 8 pieces to reduce the time individual tests take.
- TEST_F(HarnessTest, Randomized1) {
- // part 1 out of 8
- const size_t part = 1;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized2) {
- // part 2 out of 8
- const size_t part = 2;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized3) {
- // part 3 out of 8
- const size_t part = 3;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized4) {
- // part 4 out of 8
- const size_t part = 4;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized5) {
- // part 5 out of 8
- const size_t part = 5;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized6) {
- // part 6 out of 8
- const size_t part = 6;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized7) {
- // part 7 out of 8
- const size_t part = 7;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- TEST_F(HarnessTest, Randomized8) {
- // part 8 out of 8
- const size_t part = 8;
- const size_t total = 8;
- RandomizedHarnessTest(part, total);
- }
- #ifndef ROCKSDB_LITE
- TEST_F(HarnessTest, RandomizedLongDB) {
- Random rnd(test::RandomSeed());
- TestArgs args = {DB_TEST, false, 16, kNoCompression, 0, false};
- Init(args);
- int num_entries = 100000;
- for (int e = 0; e < num_entries; e++) {
- std::string v;
- Add(test::RandomKey(&rnd, rnd.Skewed(4)),
- test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());
- }
- Test(&rnd);
- // We must have created enough data to force merging
- int files = 0;
- for (int level = 0; level < db()->NumberLevels(); level++) {
- std::string value;
- char name[100];
- snprintf(name, sizeof(name), "rocksdb.num-files-at-level%d", level);
- ASSERT_TRUE(db()->GetProperty(name, &value));
- files += atoi(value.c_str());
- }
- ASSERT_GT(files, 0);
- }
- #endif // ROCKSDB_LITE
- #endif // ROCKSDB_VALGRIND_RUN
- class MemTableTest : public testing::Test {};
- TEST_F(MemTableTest, Simple) {
- InternalKeyComparator cmp(BytewiseComparator());
- auto table_factory = std::make_shared<SkipListFactory>();
- Options options;
- options.memtable_factory = table_factory;
- ImmutableCFOptions ioptions(options);
- WriteBufferManager wb(options.db_write_buffer_size);
- MemTable* memtable =
- new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
- kMaxSequenceNumber, 0 /* column_family_id */);
- memtable->Ref();
- WriteBatch batch;
- WriteBatchInternal::SetSequence(&batch, 100);
- batch.Put(std::string("k1"), std::string("v1"));
- batch.Put(std::string("k2"), std::string("v2"));
- batch.Put(std::string("k3"), std::string("v3"));
- batch.Put(std::string("largekey"), std::string("vlarge"));
- batch.DeleteRange(std::string("chi"), std::string("xigua"));
- batch.DeleteRange(std::string("begin"), std::string("end"));
- ColumnFamilyMemTablesDefault cf_mems_default(memtable);
- ASSERT_TRUE(
- WriteBatchInternal::InsertInto(&batch, &cf_mems_default, nullptr, nullptr)
- .ok());
- for (int i = 0; i < 2; ++i) {
- Arena arena;
- ScopedArenaIterator arena_iter_guard;
- std::unique_ptr<InternalIterator> iter_guard;
- InternalIterator* iter;
- if (i == 0) {
- iter = memtable->NewIterator(ReadOptions(), &arena);
- arena_iter_guard.set(iter);
- } else {
- iter = memtable->NewRangeTombstoneIterator(
- ReadOptions(), kMaxSequenceNumber /* read_seq */);
- iter_guard.reset(iter);
- }
- if (iter == nullptr) {
- continue;
- }
- iter->SeekToFirst();
- while (iter->Valid()) {
- fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(),
- iter->value().ToString().c_str());
- iter->Next();
- }
- }
- delete memtable->Unref();
- }
- // Test the empty key
- TEST_F(HarnessTest, SimpleEmptyKey) {
- auto args = GenerateArgList();
- for (const auto& arg : args) {
- Init(arg);
- Random rnd(test::RandomSeed() + 1);
- Add("", "v");
- Test(&rnd);
- }
- }
- TEST_F(HarnessTest, SimpleSingle) {
- auto args = GenerateArgList();
- for (const auto& arg : args) {
- Init(arg);
- Random rnd(test::RandomSeed() + 2);
- Add("abc", "v");
- Test(&rnd);
- }
- }
- TEST_F(HarnessTest, SimpleMulti) {
- auto args = GenerateArgList();
- for (const auto& arg : args) {
- Init(arg);
- Random rnd(test::RandomSeed() + 3);
- Add("abc", "v");
- Add("abcd", "v");
- Add("ac", "v2");
- Test(&rnd);
- }
- }
- TEST_F(HarnessTest, SimpleSpecialKey) {
- auto args = GenerateArgList();
- for (const auto& arg : args) {
- Init(arg);
- Random rnd(test::RandomSeed() + 4);
- Add("\xff\xff", "v3");
- Test(&rnd);
- }
- }
- TEST_F(HarnessTest, FooterTests) {
- {
- // upconvert legacy block based
- std::string encoded;
- Footer footer(kLegacyBlockBasedTableMagicNumber, 0);
- BlockHandle meta_index(10, 5), index(20, 15);
- footer.set_metaindex_handle(meta_index);
- footer.set_index_handle(index);
- footer.EncodeTo(&encoded);
- Footer decoded_footer;
- Slice encoded_slice(encoded);
- decoded_footer.DecodeFrom(&encoded_slice);
- ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
- ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
- ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
- ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
- ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
- ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
- ASSERT_EQ(decoded_footer.version(), 0U);
- }
- {
- // xxhash block based
- std::string encoded;
- Footer footer(kBlockBasedTableMagicNumber, 1);
- BlockHandle meta_index(10, 5), index(20, 15);
- footer.set_metaindex_handle(meta_index);
- footer.set_index_handle(index);
- footer.set_checksum(kxxHash);
- footer.EncodeTo(&encoded);
- Footer decoded_footer;
- Slice encoded_slice(encoded);
- decoded_footer.DecodeFrom(&encoded_slice);
- ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
- ASSERT_EQ(decoded_footer.checksum(), kxxHash);
- ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
- ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
- ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
- ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
- ASSERT_EQ(decoded_footer.version(), 1U);
- }
- {
- // xxhash64 block based
- std::string encoded;
- Footer footer(kBlockBasedTableMagicNumber, 1);
- BlockHandle meta_index(10, 5), index(20, 15);
- footer.set_metaindex_handle(meta_index);
- footer.set_index_handle(index);
- footer.set_checksum(kxxHash64);
- footer.EncodeTo(&encoded);
- Footer decoded_footer;
- Slice encoded_slice(encoded);
- decoded_footer.DecodeFrom(&encoded_slice);
- ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
- ASSERT_EQ(decoded_footer.checksum(), kxxHash64);
- ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
- ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
- ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
- ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
- ASSERT_EQ(decoded_footer.version(), 1U);
- }
- // Plain table is not supported in ROCKSDB_LITE
- #ifndef ROCKSDB_LITE
- {
- // upconvert legacy plain table
- std::string encoded;
- Footer footer(kLegacyPlainTableMagicNumber, 0);
- BlockHandle meta_index(10, 5), index(20, 15);
- footer.set_metaindex_handle(meta_index);
- footer.set_index_handle(index);
- footer.EncodeTo(&encoded);
- Footer decoded_footer;
- Slice encoded_slice(encoded);
- decoded_footer.DecodeFrom(&encoded_slice);
- ASSERT_EQ(decoded_footer.table_magic_number(), kPlainTableMagicNumber);
- ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
- ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
- ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
- ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
- ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
- ASSERT_EQ(decoded_footer.version(), 0U);
- }
- {
- // xxhash block based
- std::string encoded;
- Footer footer(kPlainTableMagicNumber, 1);
- BlockHandle meta_index(10, 5), index(20, 15);
- footer.set_metaindex_handle(meta_index);
- footer.set_index_handle(index);
- footer.set_checksum(kxxHash);
- footer.EncodeTo(&encoded);
- Footer decoded_footer;
- Slice encoded_slice(encoded);
- decoded_footer.DecodeFrom(&encoded_slice);
- ASSERT_EQ(decoded_footer.table_magic_number(), kPlainTableMagicNumber);
- ASSERT_EQ(decoded_footer.checksum(), kxxHash);
- ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
- ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
- ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
- ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
- ASSERT_EQ(decoded_footer.version(), 1U);
- }
- #endif // !ROCKSDB_LITE
- {
- // version == 2
- std::string encoded;
- Footer footer(kBlockBasedTableMagicNumber, 2);
- BlockHandle meta_index(10, 5), index(20, 15);
- footer.set_metaindex_handle(meta_index);
- footer.set_index_handle(index);
- footer.EncodeTo(&encoded);
- Footer decoded_footer;
- Slice encoded_slice(encoded);
- decoded_footer.DecodeFrom(&encoded_slice);
- ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
- ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
- ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
- ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
- ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
- ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
- ASSERT_EQ(decoded_footer.version(), 2U);
- }
- }
- class IndexBlockRestartIntervalTest
- : public TableTest,
- public ::testing::WithParamInterface<std::pair<int, bool>> {
- public:
- static std::vector<std::pair<int, bool>> GetRestartValues() {
- return {{-1, false}, {0, false}, {1, false}, {8, false},
- {16, false}, {32, false}, {-1, true}, {0, true},
- {1, true}, {8, true}, {16, true}, {32, true}};
- }
- };
- INSTANTIATE_TEST_CASE_P(
- IndexBlockRestartIntervalTest, IndexBlockRestartIntervalTest,
- ::testing::ValuesIn(IndexBlockRestartIntervalTest::GetRestartValues()));
- TEST_P(IndexBlockRestartIntervalTest, IndexBlockRestartInterval) {
- const int kKeysInTable = 10000;
- const int kKeySize = 100;
- const int kValSize = 500;
- const int index_block_restart_interval = std::get<0>(GetParam());
- const bool value_delta_encoding = std::get<1>(GetParam());
- Options options;
- BlockBasedTableOptions table_options;
- table_options.block_size = 64; // small block size to get big index block
- table_options.index_block_restart_interval = index_block_restart_interval;
- if (value_delta_encoding) {
- table_options.format_version = 4;
- }
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- TableConstructor c(BytewiseComparator());
- static Random rnd(301);
- for (int i = 0; i < kKeysInTable; i++) {
- InternalKey k(RandomString(&rnd, kKeySize), 0, kTypeValue);
- c.Add(k.Encode().ToString(), RandomString(&rnd, kValSize));
- }
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- std::unique_ptr<InternalKeyComparator> comparator(
- new InternalKeyComparator(BytewiseComparator()));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
- &kvmap);
- auto reader = c.GetTableReader();
- std::unique_ptr<InternalIterator> db_iter(reader->NewIterator(
- ReadOptions(), moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- // Test point lookup
- for (auto& kv : kvmap) {
- db_iter->Seek(kv.first);
- ASSERT_TRUE(db_iter->Valid());
- ASSERT_OK(db_iter->status());
- ASSERT_EQ(db_iter->key(), kv.first);
- ASSERT_EQ(db_iter->value(), kv.second);
- }
- // Test iterating
- auto kv_iter = kvmap.begin();
- for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
- ASSERT_EQ(db_iter->key(), kv_iter->first);
- ASSERT_EQ(db_iter->value(), kv_iter->second);
- kv_iter++;
- }
- ASSERT_EQ(kv_iter, kvmap.end());
- c.ResetTableReader();
- }
- class PrefixTest : public testing::Test {
- public:
- PrefixTest() : testing::Test() {}
- ~PrefixTest() override {}
- };
- namespace {
- // A simple PrefixExtractor that only works for test PrefixAndWholeKeyTest
- class TestPrefixExtractor : public ROCKSDB_NAMESPACE::SliceTransform {
- public:
- ~TestPrefixExtractor() override{};
- const char* Name() const override { return "TestPrefixExtractor"; }
- ROCKSDB_NAMESPACE::Slice Transform(
- const ROCKSDB_NAMESPACE::Slice& src) const override {
- assert(IsValid(src));
- return ROCKSDB_NAMESPACE::Slice(src.data(), 3);
- }
- bool InDomain(const ROCKSDB_NAMESPACE::Slice& src) const override {
- assert(IsValid(src));
- return true;
- }
- bool InRange(const ROCKSDB_NAMESPACE::Slice& /*dst*/) const override {
- return true;
- }
- bool IsValid(const ROCKSDB_NAMESPACE::Slice& src) const {
- if (src.size() != 4) {
- return false;
- }
- if (src[0] != '[') {
- return false;
- }
- if (src[1] < '0' || src[1] > '9') {
- return false;
- }
- if (src[2] != ']') {
- return false;
- }
- if (src[3] < '0' || src[3] > '9') {
- return false;
- }
- return true;
- }
- };
- } // namespace
- TEST_F(PrefixTest, PrefixAndWholeKeyTest) {
- ROCKSDB_NAMESPACE::Options options;
- options.compaction_style = ROCKSDB_NAMESPACE::kCompactionStyleUniversal;
- options.num_levels = 20;
- options.create_if_missing = true;
- options.optimize_filters_for_hits = false;
- options.target_file_size_base = 268435456;
- options.prefix_extractor = std::make_shared<TestPrefixExtractor>();
- ROCKSDB_NAMESPACE::BlockBasedTableOptions bbto;
- bbto.filter_policy.reset(ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10));
- bbto.block_size = 262144;
- bbto.whole_key_filtering = true;
- const std::string kDBPath = test::PerThreadDBPath("table_prefix_test");
- options.table_factory.reset(NewBlockBasedTableFactory(bbto));
- DestroyDB(kDBPath, options);
- ROCKSDB_NAMESPACE::DB* db;
- ASSERT_OK(ROCKSDB_NAMESPACE::DB::Open(options, kDBPath, &db));
- // Create a bunch of keys with 10 filters.
- for (int i = 0; i < 10; i++) {
- std::string prefix = "[" + std::to_string(i) + "]";
- for (int j = 0; j < 10; j++) {
- std::string key = prefix + std::to_string(j);
- db->Put(ROCKSDB_NAMESPACE::WriteOptions(), key, "1");
- }
- }
- // Trigger compaction.
- db->CompactRange(CompactRangeOptions(), nullptr, nullptr);
- delete db;
- // In the second round, turn whole_key_filtering off and expect
- // rocksdb still works.
- }
- /*
- * Disable TableWithGlobalSeqno since RocksDB does not store global_seqno in
- * the SST file any more. Instead, RocksDB deduces global_seqno from the
- * MANIFEST while reading from an SST. Therefore, it's not possible to test the
- * functionality of global_seqno in a single, isolated unit test without the
- * involvement of Version, VersionSet, etc.
- */
- TEST_P(BlockBasedTableTest, DISABLED_TableWithGlobalSeqno) {
- BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
- test::StringSink* sink = new test::StringSink();
- std::unique_ptr<WritableFileWriter> file_writer(
- test::GetWritableFileWriter(sink, "" /* don't care */));
- Options options;
- options.table_factory.reset(NewBlockBasedTableFactory(bbto));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- InternalKeyComparator ikc(options.comparator);
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- int_tbl_prop_collector_factories.emplace_back(
- new SstFileWriterPropertiesCollectorFactory(2 /* version */,
- 0 /* global_seqno*/));
- std::string column_family_name;
- std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
- TableBuilderOptions(ioptions, moptions, ikc,
- &int_tbl_prop_collector_factories, kNoCompression,
- 0 /* sample_for_compression */, CompressionOptions(),
- false /* skip_filters */, column_family_name, -1),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- file_writer.get()));
- for (char c = 'a'; c <= 'z'; ++c) {
- std::string key(8, c);
- std::string value = key;
- InternalKey ik(key, 0, kTypeValue);
- builder->Add(ik.Encode(), value);
- }
- ASSERT_OK(builder->Finish());
- file_writer->Flush();
- test::RandomRWStringSink ss_rw(sink);
- uint32_t version;
- uint64_t global_seqno;
- uint64_t global_seqno_offset;
- // Helper function to get version, global_seqno, global_seqno_offset
- std::function<void()> GetVersionAndGlobalSeqno = [&]() {
- std::unique_ptr<RandomAccessFileReader> file_reader(
- test::GetRandomAccessFileReader(
- new test::StringSource(ss_rw.contents(), 73342, true)));
- TableProperties* props = nullptr;
- ASSERT_OK(ReadTableProperties(file_reader.get(), ss_rw.contents().size(),
- kBlockBasedTableMagicNumber, ioptions,
- &props, true /* compression_type_missing */));
- UserCollectedProperties user_props = props->user_collected_properties;
- version = DecodeFixed32(
- user_props[ExternalSstFilePropertyNames::kVersion].c_str());
- global_seqno = DecodeFixed64(
- user_props[ExternalSstFilePropertyNames::kGlobalSeqno].c_str());
- global_seqno_offset =
- props->properties_offsets[ExternalSstFilePropertyNames::kGlobalSeqno];
- delete props;
- };
- // Helper function to update the value of the global seqno in the file
- std::function<void(uint64_t)> SetGlobalSeqno = [&](uint64_t val) {
- std::string new_global_seqno;
- PutFixed64(&new_global_seqno, val);
- ASSERT_OK(ss_rw.Write(global_seqno_offset, new_global_seqno));
- };
- // Helper function to get the contents of the table InternalIterator
- std::unique_ptr<TableReader> table_reader;
- std::function<InternalIterator*()> GetTableInternalIter = [&]() {
- std::unique_ptr<RandomAccessFileReader> file_reader(
- test::GetRandomAccessFileReader(
- new test::StringSource(ss_rw.contents(), 73342, true)));
- options.table_factory->NewTableReader(
- TableReaderOptions(ioptions, moptions.prefix_extractor.get(),
- EnvOptions(), ikc),
- std::move(file_reader), ss_rw.contents().size(), &table_reader);
- return table_reader->NewIterator(
- ReadOptions(), moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized);
- };
- GetVersionAndGlobalSeqno();
- ASSERT_EQ(2u, version);
- ASSERT_EQ(0u, global_seqno);
- InternalIterator* iter = GetTableInternalIter();
- char current_c = 'a';
- for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
- ParsedInternalKey pik;
- ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));
- ASSERT_EQ(pik.type, ValueType::kTypeValue);
- ASSERT_EQ(pik.sequence, 0);
- ASSERT_EQ(pik.user_key, iter->value());
- ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
- current_c++;
- }
- ASSERT_EQ(current_c, 'z' + 1);
- delete iter;
- // Update global sequence number to 10
- SetGlobalSeqno(10);
- GetVersionAndGlobalSeqno();
- ASSERT_EQ(2u, version);
- ASSERT_EQ(10u, global_seqno);
- iter = GetTableInternalIter();
- current_c = 'a';
- for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
- ParsedInternalKey pik;
- ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));
- ASSERT_EQ(pik.type, ValueType::kTypeValue);
- ASSERT_EQ(pik.sequence, 10);
- ASSERT_EQ(pik.user_key, iter->value());
- ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
- current_c++;
- }
- ASSERT_EQ(current_c, 'z' + 1);
- // Verify Seek
- for (char c = 'a'; c <= 'z'; c++) {
- std::string k = std::string(8, c);
- InternalKey ik(k, 10, kValueTypeForSeek);
- iter->Seek(ik.Encode());
- ASSERT_TRUE(iter->Valid());
- ParsedInternalKey pik;
- ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));
- ASSERT_EQ(pik.type, ValueType::kTypeValue);
- ASSERT_EQ(pik.sequence, 10);
- ASSERT_EQ(pik.user_key.ToString(), k);
- ASSERT_EQ(iter->value().ToString(), k);
- }
- delete iter;
- // Update global sequence number to 3
- SetGlobalSeqno(3);
- GetVersionAndGlobalSeqno();
- ASSERT_EQ(2u, version);
- ASSERT_EQ(3u, global_seqno);
- iter = GetTableInternalIter();
- current_c = 'a';
- for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
- ParsedInternalKey pik;
- ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));
- ASSERT_EQ(pik.type, ValueType::kTypeValue);
- ASSERT_EQ(pik.sequence, 3);
- ASSERT_EQ(pik.user_key, iter->value());
- ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
- current_c++;
- }
- ASSERT_EQ(current_c, 'z' + 1);
- // Verify Seek
- for (char c = 'a'; c <= 'z'; c++) {
- std::string k = std::string(8, c);
- // seqno=4 is less than 3 so we still should get our key
- InternalKey ik(k, 4, kValueTypeForSeek);
- iter->Seek(ik.Encode());
- ASSERT_TRUE(iter->Valid());
- ParsedInternalKey pik;
- ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));
- ASSERT_EQ(pik.type, ValueType::kTypeValue);
- ASSERT_EQ(pik.sequence, 3);
- ASSERT_EQ(pik.user_key.ToString(), k);
- ASSERT_EQ(iter->value().ToString(), k);
- }
- delete iter;
- }
- TEST_P(BlockBasedTableTest, BlockAlignTest) {
- BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
- bbto.block_align = true;
- test::StringSink* sink = new test::StringSink();
- std::unique_ptr<WritableFileWriter> file_writer(
- test::GetWritableFileWriter(sink, "" /* don't care */));
- Options options;
- options.compression = kNoCompression;
- options.table_factory.reset(NewBlockBasedTableFactory(bbto));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- InternalKeyComparator ikc(options.comparator);
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- std::string column_family_name;
- std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
- TableBuilderOptions(ioptions, moptions, ikc,
- &int_tbl_prop_collector_factories, kNoCompression,
- 0 /* sample_for_compression */, CompressionOptions(),
- false /* skip_filters */, column_family_name, -1),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- file_writer.get()));
- for (int i = 1; i <= 10000; ++i) {
- std::ostringstream ostr;
- ostr << std::setfill('0') << std::setw(5) << i;
- std::string key = ostr.str();
- std::string value = "val";
- InternalKey ik(key, 0, kTypeValue);
- builder->Add(ik.Encode(), value);
- }
- ASSERT_OK(builder->Finish());
- file_writer->Flush();
- test::RandomRWStringSink ss_rw(sink);
- std::unique_ptr<RandomAccessFileReader> file_reader(
- test::GetRandomAccessFileReader(
- new test::StringSource(ss_rw.contents(), 73342, true)));
- // Helper function to get version, global_seqno, global_seqno_offset
- std::function<void()> VerifyBlockAlignment = [&]() {
- TableProperties* props = nullptr;
- ASSERT_OK(ReadTableProperties(file_reader.get(), ss_rw.contents().size(),
- kBlockBasedTableMagicNumber, ioptions,
- &props, true /* compression_type_missing */));
- uint64_t data_block_size = props->data_size / props->num_data_blocks;
- ASSERT_EQ(data_block_size, 4096);
- ASSERT_EQ(props->data_size, data_block_size * props->num_data_blocks);
- delete props;
- };
- VerifyBlockAlignment();
- // The below block of code verifies that we can read back the keys. Set
- // block_align to false when creating the reader to ensure we can flip between
- // the two modes without any issues
- std::unique_ptr<TableReader> table_reader;
- bbto.block_align = false;
- Options options2;
- options2.table_factory.reset(NewBlockBasedTableFactory(bbto));
- ImmutableCFOptions ioptions2(options2);
- const MutableCFOptions moptions2(options2);
- ASSERT_OK(ioptions.table_factory->NewTableReader(
- TableReaderOptions(ioptions2, moptions2.prefix_extractor.get(),
- EnvOptions(),
- GetPlainInternalComparator(options2.comparator)),
- std::move(file_reader), ss_rw.contents().size(), &table_reader));
- std::unique_ptr<InternalIterator> db_iter(table_reader->NewIterator(
- ReadOptions(), moptions2.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- int expected_key = 1;
- for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
- std::ostringstream ostr;
- ostr << std::setfill('0') << std::setw(5) << expected_key++;
- std::string key = ostr.str();
- std::string value = "val";
- ASSERT_OK(db_iter->status());
- ASSERT_EQ(ExtractUserKey(db_iter->key()).ToString(), key);
- ASSERT_EQ(db_iter->value().ToString(), value);
- }
- expected_key--;
- ASSERT_EQ(expected_key, 10000);
- table_reader.reset();
- }
- TEST_P(BlockBasedTableTest, PropertiesBlockRestartPointTest) {
- BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
- bbto.block_align = true;
- test::StringSink* sink = new test::StringSink();
- std::unique_ptr<WritableFileWriter> file_writer(
- test::GetWritableFileWriter(sink, "" /* don't care */));
- Options options;
- options.compression = kNoCompression;
- options.table_factory.reset(NewBlockBasedTableFactory(bbto));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- InternalKeyComparator ikc(options.comparator);
- std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
- int_tbl_prop_collector_factories;
- std::string column_family_name;
- std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
- TableBuilderOptions(ioptions, moptions, ikc,
- &int_tbl_prop_collector_factories, kNoCompression,
- 0 /* sample_for_compression */, CompressionOptions(),
- false /* skip_filters */, column_family_name, -1),
- TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
- file_writer.get()));
- for (int i = 1; i <= 10000; ++i) {
- std::ostringstream ostr;
- ostr << std::setfill('0') << std::setw(5) << i;
- std::string key = ostr.str();
- std::string value = "val";
- InternalKey ik(key, 0, kTypeValue);
- builder->Add(ik.Encode(), value);
- }
- ASSERT_OK(builder->Finish());
- file_writer->Flush();
- test::RandomRWStringSink ss_rw(sink);
- std::unique_ptr<RandomAccessFileReader> file_reader(
- test::GetRandomAccessFileReader(
- new test::StringSource(ss_rw.contents(), 73342, true)));
- {
- RandomAccessFileReader* file = file_reader.get();
- uint64_t file_size = ss_rw.contents().size();
- Footer footer;
- ASSERT_OK(ReadFooterFromFile(file, nullptr /* prefetch_buffer */, file_size,
- &footer, kBlockBasedTableMagicNumber));
- auto BlockFetchHelper = [&](const BlockHandle& handle, BlockType block_type,
- BlockContents* contents) {
- ReadOptions read_options;
- read_options.verify_checksums = false;
- PersistentCacheOptions cache_options;
- BlockFetcher block_fetcher(
- file, nullptr /* prefetch_buffer */, footer, read_options, handle,
- contents, ioptions, false /* decompress */,
- false /*maybe_compressed*/, block_type,
- UncompressionDict::GetEmptyDict(), cache_options);
- ASSERT_OK(block_fetcher.ReadBlockContents());
- };
- // -- Read metaindex block
- auto metaindex_handle = footer.metaindex_handle();
- BlockContents metaindex_contents;
- BlockFetchHelper(metaindex_handle, BlockType::kMetaIndex,
- &metaindex_contents);
- Block metaindex_block(std::move(metaindex_contents),
- kDisableGlobalSequenceNumber);
- std::unique_ptr<InternalIterator> meta_iter(metaindex_block.NewDataIterator(
- BytewiseComparator(), BytewiseComparator()));
- bool found_properties_block = true;
- ASSERT_OK(SeekToPropertiesBlock(meta_iter.get(), &found_properties_block));
- ASSERT_TRUE(found_properties_block);
- // -- Read properties block
- Slice v = meta_iter->value();
- BlockHandle properties_handle;
- ASSERT_OK(properties_handle.DecodeFrom(&v));
- BlockContents properties_contents;
- BlockFetchHelper(properties_handle, BlockType::kProperties,
- &properties_contents);
- Block properties_block(std::move(properties_contents),
- kDisableGlobalSequenceNumber);
- ASSERT_EQ(properties_block.NumRestarts(), 1u);
- }
- }
- TEST_P(BlockBasedTableTest, PropertiesMetaBlockLast) {
- // The properties meta-block should come at the end since we always need to
- // read it when opening a file, unlike index/filter/other meta-blocks, which
- // are sometimes read depending on the user's configuration. This ordering
- // allows us to do a small readahead on the end of the file to read properties
- // and meta-index blocks with one I/O.
- TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
- c.Add("a1", "val1");
- c.Add("b2", "val2");
- c.Add("c3", "val3");
- c.Add("d4", "val4");
- c.Add("e5", "val5");
- c.Add("f6", "val6");
- c.Add("g7", "val7");
- c.Add("h8", "val8");
- c.Add("j9", "val9");
- // write an SST file
- Options options;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.filter_policy.reset(NewBloomFilterPolicy(
- 8 /* bits_per_key */, false /* use_block_based_filter */));
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- ImmutableCFOptions ioptions(options);
- MutableCFOptions moptions(options);
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- c.Finish(options, ioptions, moptions, table_options,
- GetPlainInternalComparator(options.comparator), &keys, &kvmap);
- // get file reader
- test::StringSink* table_sink = c.TEST_GetSink();
- std::unique_ptr<RandomAccessFileReader> table_reader{
- test::GetRandomAccessFileReader(
- new test::StringSource(table_sink->contents(), 0 /* unique_id */,
- false /* allow_mmap_reads */))};
- size_t table_size = table_sink->contents().size();
- // read footer
- Footer footer;
- ASSERT_OK(ReadFooterFromFile(table_reader.get(),
- nullptr /* prefetch_buffer */, table_size,
- &footer, kBlockBasedTableMagicNumber));
- // read metaindex
- auto metaindex_handle = footer.metaindex_handle();
- BlockContents metaindex_contents;
- PersistentCacheOptions pcache_opts;
- BlockFetcher block_fetcher(
- table_reader.get(), nullptr /* prefetch_buffer */, footer, ReadOptions(),
- metaindex_handle, &metaindex_contents, ioptions, false /* decompress */,
- false /*maybe_compressed*/, BlockType::kMetaIndex,
- UncompressionDict::GetEmptyDict(), pcache_opts,
- nullptr /*memory_allocator*/);
- ASSERT_OK(block_fetcher.ReadBlockContents());
- Block metaindex_block(std::move(metaindex_contents),
- kDisableGlobalSequenceNumber);
- // verify properties block comes last
- std::unique_ptr<InternalIterator> metaindex_iter{
- metaindex_block.NewDataIterator(options.comparator, options.comparator)};
- uint64_t max_offset = 0;
- std::string key_at_max_offset;
- for (metaindex_iter->SeekToFirst(); metaindex_iter->Valid();
- metaindex_iter->Next()) {
- BlockHandle handle;
- Slice value = metaindex_iter->value();
- ASSERT_OK(handle.DecodeFrom(&value));
- if (handle.offset() > max_offset) {
- max_offset = handle.offset();
- key_at_max_offset = metaindex_iter->key().ToString();
- }
- }
- ASSERT_EQ(kPropertiesBlock, key_at_max_offset);
- // index handle is stored in footer rather than metaindex block, so need
- // separate logic to verify it comes before properties block.
- ASSERT_GT(max_offset, footer.index_handle().offset());
- c.ResetTableReader();
- }
- TEST_P(BlockBasedTableTest, BadOptions) {
- ROCKSDB_NAMESPACE::Options options;
- options.compression = kNoCompression;
- BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
- bbto.block_size = 4000;
- bbto.block_align = true;
- const std::string kDBPath =
- test::PerThreadDBPath("block_based_table_bad_options_test");
- options.table_factory.reset(NewBlockBasedTableFactory(bbto));
- DestroyDB(kDBPath, options);
- ROCKSDB_NAMESPACE::DB* db;
- ASSERT_NOK(ROCKSDB_NAMESPACE::DB::Open(options, kDBPath, &db));
- bbto.block_size = 4096;
- options.compression = kSnappyCompression;
- options.table_factory.reset(NewBlockBasedTableFactory(bbto));
- ASSERT_NOK(ROCKSDB_NAMESPACE::DB::Open(options, kDBPath, &db));
- }
- TEST_F(BBTTailPrefetchTest, TestTailPrefetchStats) {
- TailPrefetchStats tpstats;
- ASSERT_EQ(0, tpstats.GetSuggestedPrefetchSize());
- tpstats.RecordEffectiveSize(size_t{1000});
- tpstats.RecordEffectiveSize(size_t{1005});
- tpstats.RecordEffectiveSize(size_t{1002});
- ASSERT_EQ(1005, tpstats.GetSuggestedPrefetchSize());
- // One single super large value shouldn't influence much
- tpstats.RecordEffectiveSize(size_t{1002000});
- tpstats.RecordEffectiveSize(size_t{999});
- ASSERT_LE(1005, tpstats.GetSuggestedPrefetchSize());
- ASSERT_GT(1200, tpstats.GetSuggestedPrefetchSize());
- // Only history of 32 is kept
- for (int i = 0; i < 32; i++) {
- tpstats.RecordEffectiveSize(size_t{100});
- }
- ASSERT_EQ(100, tpstats.GetSuggestedPrefetchSize());
- // 16 large values and 16 small values. The result should be closer
- // to the small value as the algorithm.
- for (int i = 0; i < 16; i++) {
- tpstats.RecordEffectiveSize(size_t{1000});
- }
- tpstats.RecordEffectiveSize(size_t{10});
- tpstats.RecordEffectiveSize(size_t{20});
- for (int i = 0; i < 6; i++) {
- tpstats.RecordEffectiveSize(size_t{100});
- }
- ASSERT_LE(80, tpstats.GetSuggestedPrefetchSize());
- ASSERT_GT(200, tpstats.GetSuggestedPrefetchSize());
- }
- TEST_F(BBTTailPrefetchTest, FilePrefetchBufferMinOffset) {
- TailPrefetchStats tpstats;
- FilePrefetchBuffer buffer(nullptr, 0, 0, false, true);
- buffer.TryReadFromCache(500, 10, nullptr);
- buffer.TryReadFromCache(480, 10, nullptr);
- buffer.TryReadFromCache(490, 10, nullptr);
- ASSERT_EQ(480, buffer.min_offset_read());
- }
- TEST_P(BlockBasedTableTest, DataBlockHashIndex) {
- const int kNumKeys = 500;
- const int kKeySize = 8;
- const int kValSize = 40;
- BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
- table_options.data_block_index_type =
- BlockBasedTableOptions::kDataBlockBinaryAndHash;
- Options options;
- options.comparator = BytewiseComparator();
- options.table_factory.reset(new BlockBasedTableFactory(table_options));
- TableConstructor c(options.comparator);
- static Random rnd(1048);
- for (int i = 0; i < kNumKeys; i++) {
- // padding one "0" to mark existent keys.
- std::string random_key(RandomString(&rnd, kKeySize - 1) + "1");
- InternalKey k(random_key, 0, kTypeValue);
- c.Add(k.Encode().ToString(), RandomString(&rnd, kValSize));
- }
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- const InternalKeyComparator internal_comparator(options.comparator);
- c.Finish(options, ioptions, moptions, table_options, internal_comparator,
- &keys, &kvmap);
- auto reader = c.GetTableReader();
- std::unique_ptr<InternalIterator> seek_iter;
- seek_iter.reset(reader->NewIterator(
- ReadOptions(), moptions.prefix_extractor.get(), /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized));
- for (int i = 0; i < 2; ++i) {
- ReadOptions ro;
- // for every kv, we seek using two method: Get() and Seek()
- // Get() will use the SuffixIndexHash in Block. For non-existent key it
- // will invalidate the iterator
- // Seek() will use the default BinarySeek() in Block. So for non-existent
- // key it will land at the closest key that is large than target.
- // Search for existent keys
- for (auto& kv : kvmap) {
- if (i == 0) {
- // Search using Seek()
- seek_iter->Seek(kv.first);
- ASSERT_OK(seek_iter->status());
- ASSERT_TRUE(seek_iter->Valid());
- ASSERT_EQ(seek_iter->key(), kv.first);
- ASSERT_EQ(seek_iter->value(), kv.second);
- } else {
- // Search using Get()
- PinnableSlice value;
- std::string user_key = ExtractUserKey(kv.first).ToString();
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, user_key, &value, nullptr,
- nullptr, true, nullptr, nullptr);
- ASSERT_OK(reader->Get(ro, kv.first, &get_context,
- moptions.prefix_extractor.get()));
- ASSERT_EQ(get_context.State(), GetContext::kFound);
- ASSERT_EQ(value, Slice(kv.second));
- value.Reset();
- }
- }
- // Search for non-existent keys
- for (auto& kv : kvmap) {
- std::string user_key = ExtractUserKey(kv.first).ToString();
- user_key.back() = '0'; // make it non-existent key
- InternalKey internal_key(user_key, 0, kTypeValue);
- std::string encoded_key = internal_key.Encode().ToString();
- if (i == 0) { // Search using Seek()
- seek_iter->Seek(encoded_key);
- ASSERT_OK(seek_iter->status());
- if (seek_iter->Valid()) {
- ASSERT_TRUE(BytewiseComparator()->Compare(
- user_key, ExtractUserKey(seek_iter->key())) < 0);
- }
- } else { // Search using Get()
- PinnableSlice value;
- GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
- GetContext::kNotFound, user_key, &value, nullptr,
- nullptr, true, nullptr, nullptr);
- ASSERT_OK(reader->Get(ro, encoded_key, &get_context,
- moptions.prefix_extractor.get()));
- ASSERT_EQ(get_context.State(), GetContext::kNotFound);
- value.Reset();
- }
- }
- }
- }
- // BlockBasedTableIterator should invalidate itself and return
- // OutOfBound()=true immediately after Seek(), to allow LevelIterator
- // filter out corresponding level.
- TEST_P(BlockBasedTableTest, OutOfBoundOnSeek) {
- TableConstructor c(BytewiseComparator(), true /*convert_to_internal_key*/);
- c.Add("foo", "v1");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- BlockBasedTableOptions table_opt(GetBlockBasedTableOptions());
- options.table_factory.reset(NewBlockBasedTableFactory(table_opt));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_opt,
- GetPlainInternalComparator(BytewiseComparator()), &keys, &kvmap);
- auto* reader = c.GetTableReader();
- ReadOptions read_opt;
- std::string upper_bound = "bar";
- Slice upper_bound_slice(upper_bound);
- read_opt.iterate_upper_bound = &upper_bound_slice;
- std::unique_ptr<InternalIterator> iter;
- iter.reset(new KeyConvertingIterator(reader->NewIterator(
- read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
- iter->SeekToFirst();
- ASSERT_FALSE(iter->Valid());
- ASSERT_TRUE(iter->IsOutOfBound());
- iter.reset(new KeyConvertingIterator(reader->NewIterator(
- read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
- iter->Seek("foo");
- ASSERT_FALSE(iter->Valid());
- ASSERT_TRUE(iter->IsOutOfBound());
- }
- // BlockBasedTableIterator should invalidate itself and return
- // OutOfBound()=true after Next(), if it finds current index key is no smaller
- // than upper bound, unless it is pointing to the last data block.
- TEST_P(BlockBasedTableTest, OutOfBoundOnNext) {
- TableConstructor c(BytewiseComparator(), true /*convert_to_internal_key*/);
- c.Add("bar", "v");
- c.Add("foo", "v");
- std::vector<std::string> keys;
- stl_wrappers::KVMap kvmap;
- Options options;
- BlockBasedTableOptions table_opt(GetBlockBasedTableOptions());
- table_opt.flush_block_policy_factory =
- std::make_shared<FlushBlockEveryKeyPolicyFactory>();
- options.table_factory.reset(NewBlockBasedTableFactory(table_opt));
- const ImmutableCFOptions ioptions(options);
- const MutableCFOptions moptions(options);
- c.Finish(options, ioptions, moptions, table_opt,
- GetPlainInternalComparator(BytewiseComparator()), &keys, &kvmap);
- auto* reader = c.GetTableReader();
- ReadOptions read_opt;
- std::string ub1 = "bar_after";
- Slice ub_slice1(ub1);
- read_opt.iterate_upper_bound = &ub_slice1;
- std::unique_ptr<InternalIterator> iter;
- iter.reset(new KeyConvertingIterator(reader->NewIterator(
- read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
- iter->Seek("bar");
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("bar", iter->key());
- iter->Next();
- ASSERT_FALSE(iter->Valid());
- ASSERT_TRUE(iter->IsOutOfBound());
- std::string ub2 = "foo_after";
- Slice ub_slice2(ub2);
- read_opt.iterate_upper_bound = &ub_slice2;
- iter.reset(new KeyConvertingIterator(reader->NewIterator(
- read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
- /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
- iter->Seek("foo");
- ASSERT_TRUE(iter->Valid());
- ASSERT_EQ("foo", iter->key());
- iter->Next();
- ASSERT_FALSE(iter->Valid());
- ASSERT_FALSE(iter->IsOutOfBound());
- }
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|