block_test.cc 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. #include "table/block_based/block.h"
  7. #include <algorithm>
  8. #include <cstdio>
  9. #include <set>
  10. #include <string>
  11. #include <unordered_set>
  12. #include <utility>
  13. #include <vector>
  14. #include "db/db_test_util.h"
  15. #include "db/dbformat.h"
  16. #include "db/memtable.h"
  17. #include "db/write_batch_internal.h"
  18. #include "rocksdb/db.h"
  19. #include "rocksdb/env.h"
  20. #include "rocksdb/iterator.h"
  21. #include "rocksdb/slice_transform.h"
  22. #include "rocksdb/table.h"
  23. #include "table/block_based/block_based_table_reader.h"
  24. #include "table/block_based/block_builder.h"
  25. #include "table/format.h"
  26. #include "test_util/testharness.h"
  27. #include "test_util/testutil.h"
  28. #include "util/random.h"
  29. namespace ROCKSDB_NAMESPACE {
  30. std::string GenerateInternalKey(int primary_key, int secondary_key,
  31. int padding_size, Random *rnd,
  32. size_t ts_sz = 0) {
  33. char buf[50];
  34. char *p = &buf[0];
  35. snprintf(buf, sizeof(buf), "%6d%4d", primary_key, secondary_key);
  36. std::string k(p);
  37. if (padding_size) {
  38. k += rnd->RandomString(padding_size);
  39. }
  40. AppendInternalKeyFooter(&k, 0 /* seqno */, kTypeValue);
  41. std::string key_with_ts;
  42. if (ts_sz > 0) {
  43. PadInternalKeyWithMinTimestamp(&key_with_ts, k, ts_sz);
  44. return key_with_ts;
  45. }
  46. return k;
  47. }
  48. // Generate random key value pairs.
  49. // The generated key will be sorted. You can tune the parameters to generated
  50. // different kinds of test key/value pairs for different scenario.
  51. void GenerateRandomKVs(std::vector<std::string> *keys,
  52. std::vector<std::string> *values, const int from,
  53. const int len, const int step = 1,
  54. const int padding_size = 0,
  55. const int keys_share_prefix = 1, size_t ts_sz = 0) {
  56. Random rnd(302);
  57. // generate different prefix
  58. for (int i = from; i < from + len; i += step) {
  59. // generating keys that shares the prefix
  60. for (int j = 0; j < keys_share_prefix; ++j) {
  61. // `DataBlockIter` assumes it reads only internal keys.
  62. keys->emplace_back(GenerateInternalKey(i, j, padding_size, &rnd, ts_sz));
  63. // 100 bytes values
  64. values->emplace_back(rnd.RandomString(100));
  65. }
  66. }
  67. }
  68. // Test Param 1): key use delta encoding.
  69. // Test Param 2): user-defined timestamp test mode.
  70. // Test Param 3): data block index type.
  71. class BlockTest : public testing::Test,
  72. public testing::WithParamInterface<
  73. std::tuple<bool, test::UserDefinedTimestampTestMode,
  74. BlockBasedTableOptions::DataBlockIndexType>> {
  75. public:
  76. bool keyUseDeltaEncoding() const { return std::get<0>(GetParam()); }
  77. bool isUDTEnabled() const {
  78. return test::IsUDTEnabled(std::get<1>(GetParam()));
  79. }
  80. bool shouldPersistUDT() const {
  81. return test::ShouldPersistUDT(std::get<1>(GetParam()));
  82. }
  83. BlockBasedTableOptions::DataBlockIndexType dataBlockIndexType() const {
  84. return std::get<2>(GetParam());
  85. }
  86. };
  87. // block test
  88. TEST_P(BlockTest, SimpleTest) {
  89. Random rnd(301);
  90. Options options = Options();
  91. if (isUDTEnabled()) {
  92. options.comparator = test::BytewiseComparatorWithU64TsWrapper();
  93. }
  94. size_t ts_sz = options.comparator->timestamp_size();
  95. std::vector<std::string> keys;
  96. std::vector<std::string> values;
  97. BlockBasedTableOptions::DataBlockIndexType index_type =
  98. isUDTEnabled() ? BlockBasedTableOptions::kDataBlockBinarySearch
  99. : dataBlockIndexType();
  100. BlockBuilder builder(16, keyUseDeltaEncoding(),
  101. false /* use_value_delta_encoding */, index_type,
  102. 0.75 /* data_block_hash_table_util_ratio */, ts_sz,
  103. shouldPersistUDT(), false /* is_user_key */);
  104. int num_records = 100000;
  105. GenerateRandomKVs(&keys, &values, 0, num_records, 1 /* step */,
  106. 0 /* padding_size */, 1 /* keys_share_prefix */, ts_sz);
  107. // add a bunch of records to a block
  108. for (int i = 0; i < num_records; i++) {
  109. builder.Add(keys[i], values[i]);
  110. }
  111. // read serialized contents of the block
  112. Slice rawblock = builder.Finish();
  113. // create block reader
  114. BlockContents contents;
  115. contents.data = rawblock;
  116. Block reader(std::move(contents));
  117. // read contents of block sequentially
  118. int count = 0;
  119. InternalIterator *iter = reader.NewDataIterator(
  120. options.comparator, kDisableGlobalSequenceNumber, nullptr /* iter */,
  121. nullptr /* stats */, false /* block_contents_pinned */,
  122. shouldPersistUDT());
  123. for (iter->SeekToFirst(); iter->Valid(); count++, iter->Next()) {
  124. // read kv from block
  125. Slice k = iter->key();
  126. Slice v = iter->value();
  127. // compare with lookaside array
  128. ASSERT_EQ(k.ToString().compare(keys[count]), 0);
  129. ASSERT_EQ(v.ToString().compare(values[count]), 0);
  130. }
  131. delete iter;
  132. // read block contents randomly
  133. iter = reader.NewDataIterator(
  134. options.comparator, kDisableGlobalSequenceNumber, nullptr /* iter */,
  135. nullptr /* stats */, false /* block_contents_pinned */,
  136. shouldPersistUDT());
  137. for (int i = 0; i < num_records; i++) {
  138. // find a random key in the lookaside array
  139. int index = rnd.Uniform(num_records);
  140. Slice k(keys[index]);
  141. // search in block for this key
  142. iter->Seek(k);
  143. ASSERT_TRUE(iter->Valid());
  144. Slice v = iter->value();
  145. ASSERT_EQ(v.ToString().compare(values[index]), 0);
  146. }
  147. delete iter;
  148. }
  149. // return the block contents
  150. BlockContents GetBlockContents(
  151. std::unique_ptr<BlockBuilder> *builder,
  152. const std::vector<std::string> &keys,
  153. const std::vector<std::string> &values, bool key_use_delta_encoding,
  154. size_t ts_sz, bool should_persist_udt, const int /*prefix_group_size*/ = 1,
  155. BlockBasedTableOptions::DataBlockIndexType dblock_index_type =
  156. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch) {
  157. builder->reset(
  158. new BlockBuilder(1 /* restart interval */, key_use_delta_encoding,
  159. false /* use_value_delta_encoding */, dblock_index_type,
  160. 0.75 /* data_block_hash_table_util_ratio */, ts_sz,
  161. should_persist_udt, false /* is_user_key */));
  162. // Add only half of the keys
  163. for (size_t i = 0; i < keys.size(); ++i) {
  164. (*builder)->Add(keys[i], values[i]);
  165. }
  166. Slice rawblock = (*builder)->Finish();
  167. BlockContents contents;
  168. contents.data = rawblock;
  169. return contents;
  170. }
  171. void CheckBlockContents(BlockContents contents, const int max_key,
  172. const std::vector<std::string> &keys,
  173. const std::vector<std::string> &values,
  174. bool is_udt_enabled, bool should_persist_udt) {
  175. const size_t prefix_size = 6;
  176. // create block reader
  177. BlockContents contents_ref(contents.data);
  178. Block reader1(std::move(contents));
  179. Block reader2(std::move(contents_ref));
  180. std::unique_ptr<const SliceTransform> prefix_extractor(
  181. NewFixedPrefixTransform(prefix_size));
  182. std::unique_ptr<InternalIterator> regular_iter(reader2.NewDataIterator(
  183. is_udt_enabled ? test::BytewiseComparatorWithU64TsWrapper()
  184. : BytewiseComparator(),
  185. kDisableGlobalSequenceNumber, nullptr /* iter */, nullptr /* stats */,
  186. false /* block_contents_pinned */, should_persist_udt));
  187. // Seek existent keys
  188. for (size_t i = 0; i < keys.size(); i++) {
  189. regular_iter->Seek(keys[i]);
  190. ASSERT_OK(regular_iter->status());
  191. ASSERT_TRUE(regular_iter->Valid());
  192. Slice v = regular_iter->value();
  193. ASSERT_EQ(v.ToString().compare(values[i]), 0);
  194. }
  195. // Seek non-existent keys.
  196. // For hash index, if no key with a given prefix is not found, iterator will
  197. // simply be set as invalid; whereas the binary search based iterator will
  198. // return the one that is closest.
  199. for (int i = 1; i < max_key - 1; i += 2) {
  200. // `DataBlockIter` assumes its APIs receive only internal keys.
  201. auto key = GenerateInternalKey(i, 0, 0, nullptr,
  202. is_udt_enabled ? 8 : 0 /* ts_sz */);
  203. regular_iter->Seek(key);
  204. ASSERT_TRUE(regular_iter->Valid());
  205. }
  206. }
  207. // In this test case, no two key share same prefix.
  208. TEST_P(BlockTest, SimpleIndexHash) {
  209. const int kMaxKey = 100000;
  210. size_t ts_sz = isUDTEnabled() ? 8 : 0;
  211. std::vector<std::string> keys;
  212. std::vector<std::string> values;
  213. GenerateRandomKVs(&keys, &values, 0 /* first key id */,
  214. kMaxKey /* last key id */, 2 /* step */,
  215. 8 /* padding size (8 bytes randomly generated suffix) */,
  216. 1 /* keys_share_prefix */, ts_sz);
  217. std::unique_ptr<BlockBuilder> builder;
  218. auto contents = GetBlockContents(
  219. &builder, keys, values, keyUseDeltaEncoding(), ts_sz, shouldPersistUDT(),
  220. 1 /* prefix_group_size */,
  221. isUDTEnabled()
  222. ? BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch
  223. : dataBlockIndexType());
  224. CheckBlockContents(std::move(contents), kMaxKey, keys, values, isUDTEnabled(),
  225. shouldPersistUDT());
  226. }
  227. TEST_P(BlockTest, IndexHashWithSharedPrefix) {
  228. const int kMaxKey = 100000;
  229. // for each prefix, there will be 5 keys starts with it.
  230. const int kPrefixGroup = 5;
  231. size_t ts_sz = isUDTEnabled() ? 8 : 0;
  232. std::vector<std::string> keys;
  233. std::vector<std::string> values;
  234. // Generate keys with same prefix.
  235. GenerateRandomKVs(&keys, &values, 0, // first key id
  236. kMaxKey, // last key id
  237. 2 /* step */,
  238. 10 /* padding size (8 bytes randomly generated suffix) */,
  239. kPrefixGroup /* keys_share_prefix */, ts_sz);
  240. std::unique_ptr<BlockBuilder> builder;
  241. auto contents = GetBlockContents(
  242. &builder, keys, values, keyUseDeltaEncoding(), isUDTEnabled(),
  243. shouldPersistUDT(), kPrefixGroup,
  244. isUDTEnabled()
  245. ? BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch
  246. : dataBlockIndexType());
  247. CheckBlockContents(std::move(contents), kMaxKey, keys, values, isUDTEnabled(),
  248. shouldPersistUDT());
  249. }
  250. // Param 0: key use delta encoding
  251. // Param 1: user-defined timestamp test mode
  252. // Param 2: data block index type. User-defined timestamp feature is not
  253. // compatible with `kDataBlockBinaryAndHash` data block index type because the
  254. // user comparator doesn't provide a `CanKeysWithDifferentByteContentsBeEqual`
  255. // override. This combination is disabled.
  256. INSTANTIATE_TEST_CASE_P(
  257. P, BlockTest,
  258. ::testing::Combine(
  259. ::testing::Bool(), ::testing::ValuesIn(test::GetUDTTestModes()),
  260. ::testing::Values(
  261. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch,
  262. BlockBasedTableOptions::DataBlockIndexType::
  263. kDataBlockBinaryAndHash)));
  264. // A slow and accurate version of BlockReadAmpBitmap that simply store
  265. // all the marked ranges in a set.
  266. class BlockReadAmpBitmapSlowAndAccurate {
  267. public:
  268. void Mark(size_t start_offset, size_t end_offset) {
  269. assert(end_offset >= start_offset);
  270. marked_ranges_.emplace(end_offset, start_offset);
  271. }
  272. void ResetCheckSequence() { iter_valid_ = false; }
  273. // Return true if any byte in this range was Marked
  274. // This does linear search from the previous position. When calling
  275. // multiple times, `offset` needs to be incremental to get correct results.
  276. // Call ResetCheckSequence() to reset it.
  277. bool IsPinMarked(size_t offset) {
  278. if (iter_valid_) {
  279. // Has existing iterator, try linear search from
  280. // the iterator.
  281. for (int i = 0; i < 64; i++) {
  282. if (offset < iter_->second) {
  283. return false;
  284. }
  285. if (offset <= iter_->first) {
  286. return true;
  287. }
  288. iter_++;
  289. if (iter_ == marked_ranges_.end()) {
  290. iter_valid_ = false;
  291. return false;
  292. }
  293. }
  294. }
  295. // Initial call or have linear searched too many times.
  296. // Do binary search.
  297. iter_ = marked_ranges_.lower_bound(
  298. std::make_pair(offset, static_cast<size_t>(0)));
  299. if (iter_ == marked_ranges_.end()) {
  300. iter_valid_ = false;
  301. return false;
  302. }
  303. iter_valid_ = true;
  304. return offset <= iter_->first && offset >= iter_->second;
  305. }
  306. private:
  307. std::set<std::pair<size_t, size_t>> marked_ranges_;
  308. std::set<std::pair<size_t, size_t>>::iterator iter_;
  309. bool iter_valid_ = false;
  310. };
  311. TEST_F(BlockTest, BlockReadAmpBitmap) {
  312. uint32_t pin_offset = 0;
  313. SyncPoint::GetInstance()->SetCallBack(
  314. "BlockReadAmpBitmap:rnd", [&pin_offset](void *arg) {
  315. pin_offset = *(static_cast<uint32_t *>(arg));
  316. });
  317. SyncPoint::GetInstance()->EnableProcessing();
  318. std::vector<size_t> block_sizes = {
  319. 1, // 1 byte
  320. 32, // 32 bytes
  321. 61, // 61 bytes
  322. 64, // 64 bytes
  323. 512, // 0.5 KB
  324. 1024, // 1 KB
  325. 1024 * 4, // 4 KB
  326. 1024 * 10, // 10 KB
  327. 1024 * 50, // 50 KB
  328. 1024 * 1024 * 4, // 5 MB
  329. 777,
  330. 124653,
  331. };
  332. const size_t kBytesPerBit = 64;
  333. Random rnd(301);
  334. for (size_t block_size : block_sizes) {
  335. std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
  336. BlockReadAmpBitmap read_amp_bitmap(block_size, kBytesPerBit, stats.get());
  337. BlockReadAmpBitmapSlowAndAccurate read_amp_slow_and_accurate;
  338. size_t needed_bits = (block_size / kBytesPerBit);
  339. if (block_size % kBytesPerBit != 0) {
  340. needed_bits++;
  341. }
  342. ASSERT_EQ(stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES), block_size);
  343. // Generate some random entries
  344. std::vector<size_t> random_entry_offsets;
  345. for (int i = 0; i < 1000; i++) {
  346. random_entry_offsets.push_back(rnd.Next() % block_size);
  347. }
  348. std::sort(random_entry_offsets.begin(), random_entry_offsets.end());
  349. auto it =
  350. std::unique(random_entry_offsets.begin(), random_entry_offsets.end());
  351. random_entry_offsets.resize(
  352. std::distance(random_entry_offsets.begin(), it));
  353. std::vector<std::pair<size_t, size_t>> random_entries;
  354. for (size_t i = 0; i < random_entry_offsets.size(); i++) {
  355. size_t entry_start = random_entry_offsets[i];
  356. size_t entry_end;
  357. if (i + 1 < random_entry_offsets.size()) {
  358. entry_end = random_entry_offsets[i + 1] - 1;
  359. } else {
  360. entry_end = block_size - 1;
  361. }
  362. random_entries.emplace_back(entry_start, entry_end);
  363. }
  364. for (size_t i = 0; i < random_entries.size(); i++) {
  365. read_amp_slow_and_accurate.ResetCheckSequence();
  366. auto &current_entry = random_entries[rnd.Next() % random_entries.size()];
  367. read_amp_bitmap.Mark(static_cast<uint32_t>(current_entry.first),
  368. static_cast<uint32_t>(current_entry.second));
  369. read_amp_slow_and_accurate.Mark(current_entry.first,
  370. current_entry.second);
  371. size_t total_bits = 0;
  372. for (size_t bit_idx = 0; bit_idx < needed_bits; bit_idx++) {
  373. total_bits += read_amp_slow_and_accurate.IsPinMarked(
  374. bit_idx * kBytesPerBit + pin_offset);
  375. }
  376. size_t expected_estimate_useful = total_bits * kBytesPerBit;
  377. size_t got_estimate_useful =
  378. stats->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
  379. ASSERT_EQ(expected_estimate_useful, got_estimate_useful);
  380. }
  381. }
  382. SyncPoint::GetInstance()->DisableProcessing();
  383. SyncPoint::GetInstance()->ClearAllCallBacks();
  384. }
  385. TEST_F(BlockTest, BlockWithReadAmpBitmap) {
  386. Random rnd(301);
  387. Options options = Options();
  388. std::vector<std::string> keys;
  389. std::vector<std::string> values;
  390. BlockBuilder builder(16);
  391. int num_records = 10000;
  392. GenerateRandomKVs(&keys, &values, 0, num_records, 1 /* step */);
  393. // add a bunch of records to a block
  394. for (int i = 0; i < num_records; i++) {
  395. builder.Add(keys[i], values[i]);
  396. }
  397. Slice rawblock = builder.Finish();
  398. const size_t kBytesPerBit = 8;
  399. // Read the block sequentially using Next()
  400. {
  401. std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
  402. // create block reader
  403. BlockContents contents;
  404. contents.data = rawblock;
  405. Block reader(std::move(contents), kBytesPerBit, stats.get());
  406. // read contents of block sequentially
  407. size_t read_bytes = 0;
  408. DataBlockIter *iter = reader.NewDataIterator(
  409. options.comparator, kDisableGlobalSequenceNumber, nullptr, stats.get());
  410. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  411. iter->value();
  412. read_bytes += iter->TEST_CurrentEntrySize();
  413. double semi_acc_read_amp =
  414. static_cast<double>(read_bytes) / rawblock.size();
  415. double read_amp = static_cast<double>(stats->getTickerCount(
  416. READ_AMP_ESTIMATE_USEFUL_BYTES)) /
  417. stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
  418. // Error in read amplification will be less than 1% if we are reading
  419. // sequentially
  420. double error_pct = fabs(semi_acc_read_amp - read_amp) * 100;
  421. EXPECT_LT(error_pct, 1);
  422. }
  423. delete iter;
  424. }
  425. // Read the block sequentially using Seek()
  426. {
  427. std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
  428. // create block reader
  429. BlockContents contents;
  430. contents.data = rawblock;
  431. Block reader(std::move(contents), kBytesPerBit, stats.get());
  432. size_t read_bytes = 0;
  433. DataBlockIter *iter = reader.NewDataIterator(
  434. options.comparator, kDisableGlobalSequenceNumber, nullptr, stats.get());
  435. for (int i = 0; i < num_records; i++) {
  436. Slice k(keys[i]);
  437. // search in block for this key
  438. iter->Seek(k);
  439. iter->value();
  440. read_bytes += iter->TEST_CurrentEntrySize();
  441. double semi_acc_read_amp =
  442. static_cast<double>(read_bytes) / rawblock.size();
  443. double read_amp = static_cast<double>(stats->getTickerCount(
  444. READ_AMP_ESTIMATE_USEFUL_BYTES)) /
  445. stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
  446. // Error in read amplification will be less than 1% if we are reading
  447. // sequentially
  448. double error_pct = fabs(semi_acc_read_amp - read_amp) * 100;
  449. EXPECT_LT(error_pct, 1);
  450. }
  451. delete iter;
  452. }
  453. // Read the block randomly
  454. {
  455. std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
  456. // create block reader
  457. BlockContents contents;
  458. contents.data = rawblock;
  459. Block reader(std::move(contents), kBytesPerBit, stats.get());
  460. size_t read_bytes = 0;
  461. DataBlockIter *iter = reader.NewDataIterator(
  462. options.comparator, kDisableGlobalSequenceNumber, nullptr, stats.get());
  463. std::unordered_set<int> read_keys;
  464. for (int i = 0; i < num_records; i++) {
  465. int index = rnd.Uniform(num_records);
  466. Slice k(keys[index]);
  467. iter->Seek(k);
  468. iter->value();
  469. if (read_keys.find(index) == read_keys.end()) {
  470. read_keys.insert(index);
  471. read_bytes += iter->TEST_CurrentEntrySize();
  472. }
  473. double semi_acc_read_amp =
  474. static_cast<double>(read_bytes) / rawblock.size();
  475. double read_amp = static_cast<double>(stats->getTickerCount(
  476. READ_AMP_ESTIMATE_USEFUL_BYTES)) /
  477. stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
  478. double error_pct = fabs(semi_acc_read_amp - read_amp) * 100;
  479. // Error in read amplification will be less than 2% if we are reading
  480. // randomly
  481. EXPECT_LT(error_pct, 2);
  482. }
  483. delete iter;
  484. }
  485. }
  486. TEST_F(BlockTest, ReadAmpBitmapPow2) {
  487. std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
  488. ASSERT_EQ(BlockReadAmpBitmap(100, 1, stats.get()).GetBytesPerBit(), 1u);
  489. ASSERT_EQ(BlockReadAmpBitmap(100, 2, stats.get()).GetBytesPerBit(), 2u);
  490. ASSERT_EQ(BlockReadAmpBitmap(100, 4, stats.get()).GetBytesPerBit(), 4u);
  491. ASSERT_EQ(BlockReadAmpBitmap(100, 8, stats.get()).GetBytesPerBit(), 8u);
  492. ASSERT_EQ(BlockReadAmpBitmap(100, 16, stats.get()).GetBytesPerBit(), 16u);
  493. ASSERT_EQ(BlockReadAmpBitmap(100, 32, stats.get()).GetBytesPerBit(), 32u);
  494. ASSERT_EQ(BlockReadAmpBitmap(100, 3, stats.get()).GetBytesPerBit(), 2u);
  495. ASSERT_EQ(BlockReadAmpBitmap(100, 7, stats.get()).GetBytesPerBit(), 4u);
  496. ASSERT_EQ(BlockReadAmpBitmap(100, 11, stats.get()).GetBytesPerBit(), 8u);
  497. ASSERT_EQ(BlockReadAmpBitmap(100, 17, stats.get()).GetBytesPerBit(), 16u);
  498. ASSERT_EQ(BlockReadAmpBitmap(100, 33, stats.get()).GetBytesPerBit(), 32u);
  499. ASSERT_EQ(BlockReadAmpBitmap(100, 35, stats.get()).GetBytesPerBit(), 32u);
  500. }
  501. class IndexBlockTest
  502. : public testing::Test,
  503. public testing::WithParamInterface<
  504. std::tuple<bool, bool, bool, test::UserDefinedTimestampTestMode>> {
  505. public:
  506. IndexBlockTest() = default;
  507. bool keyIncludesSeq() const { return std::get<0>(GetParam()); }
  508. bool useValueDeltaEncoding() const { return std::get<1>(GetParam()); }
  509. bool includeFirstKey() const { return std::get<2>(GetParam()); }
  510. bool isUDTEnabled() const {
  511. return test::IsUDTEnabled(std::get<3>(GetParam()));
  512. }
  513. bool shouldPersistUDT() const {
  514. return test::ShouldPersistUDT(std::get<3>(GetParam()));
  515. }
  516. };
  517. // Similar to GenerateRandomKVs but for index block contents.
  518. void GenerateRandomIndexEntries(std::vector<std::string> *separators,
  519. std::vector<BlockHandle> *block_handles,
  520. std::vector<std::string> *first_keys,
  521. const int len, size_t ts_sz = 0,
  522. bool zero_seqno = false) {
  523. Random rnd(42);
  524. // For each of `len` blocks, we need to generate a first and last key.
  525. // Let's generate n*2 random keys, sort them, group into consecutive pairs.
  526. std::set<std::string> keys;
  527. while ((int)keys.size() < len * 2) {
  528. // Keys need to be at least 8 bytes long to look like internal keys.
  529. std::string new_key = test::RandomKey(&rnd, 12);
  530. if (zero_seqno) {
  531. AppendInternalKeyFooter(&new_key, 0 /* seqno */, kTypeValue);
  532. }
  533. if (ts_sz > 0) {
  534. std::string key;
  535. PadInternalKeyWithMinTimestamp(&key, new_key, ts_sz);
  536. keys.insert(std::move(key));
  537. } else {
  538. keys.insert(std::move(new_key));
  539. }
  540. }
  541. uint64_t offset = 0;
  542. for (auto it = keys.begin(); it != keys.end();) {
  543. first_keys->emplace_back(*it++);
  544. separators->emplace_back(*it++);
  545. uint64_t size = rnd.Uniform(1024 * 16);
  546. BlockHandle handle(offset, size);
  547. offset += size + BlockBasedTable::kBlockTrailerSize;
  548. block_handles->emplace_back(handle);
  549. }
  550. }
  551. TEST_P(IndexBlockTest, IndexValueEncodingTest) {
  552. Random rnd(301);
  553. Options options = Options();
  554. if (isUDTEnabled()) {
  555. options.comparator = test::BytewiseComparatorWithU64TsWrapper();
  556. }
  557. size_t ts_sz = options.comparator->timestamp_size();
  558. std::vector<std::string> separators;
  559. std::vector<BlockHandle> block_handles;
  560. std::vector<std::string> first_keys;
  561. const bool kUseDeltaEncoding = true;
  562. BlockBuilder builder(16, kUseDeltaEncoding, useValueDeltaEncoding(),
  563. BlockBasedTableOptions::kDataBlockBinarySearch,
  564. 0.75 /* data_block_hash_table_util_ratio */, ts_sz,
  565. shouldPersistUDT(), !keyIncludesSeq());
  566. int num_records = 100;
  567. GenerateRandomIndexEntries(&separators, &block_handles, &first_keys,
  568. num_records, ts_sz, false /* zero_seqno */);
  569. BlockHandle last_encoded_handle;
  570. for (int i = 0; i < num_records; i++) {
  571. std::string first_key_to_persist_buf;
  572. Slice first_internal_key = first_keys[i];
  573. if (ts_sz > 0 && !shouldPersistUDT()) {
  574. StripTimestampFromInternalKey(&first_key_to_persist_buf, first_keys[i],
  575. ts_sz);
  576. first_internal_key = first_key_to_persist_buf;
  577. }
  578. IndexValue entry(block_handles[i], first_internal_key);
  579. std::string encoded_entry;
  580. std::string delta_encoded_entry;
  581. entry.EncodeTo(&encoded_entry, includeFirstKey(), nullptr);
  582. if (useValueDeltaEncoding() && i > 0) {
  583. entry.EncodeTo(&delta_encoded_entry, includeFirstKey(),
  584. &last_encoded_handle);
  585. }
  586. last_encoded_handle = entry.handle;
  587. const Slice delta_encoded_entry_slice(delta_encoded_entry);
  588. if (keyIncludesSeq()) {
  589. builder.Add(separators[i], encoded_entry, &delta_encoded_entry_slice);
  590. } else {
  591. const Slice user_key = ExtractUserKey(separators[i]);
  592. builder.Add(user_key, encoded_entry, &delta_encoded_entry_slice);
  593. }
  594. }
  595. // read serialized contents of the block
  596. Slice rawblock = builder.Finish();
  597. // create block reader
  598. BlockContents contents;
  599. contents.data = rawblock;
  600. Block reader(std::move(contents));
  601. const bool kTotalOrderSeek = true;
  602. IndexBlockIter *kNullIter = nullptr;
  603. Statistics *kNullStats = nullptr;
  604. // read contents of block sequentially
  605. InternalIteratorBase<IndexValue> *iter = reader.NewIndexIterator(
  606. options.comparator, kDisableGlobalSequenceNumber, kNullIter, kNullStats,
  607. kTotalOrderSeek, includeFirstKey(), keyIncludesSeq(),
  608. !useValueDeltaEncoding(), false /* block_contents_pinned */,
  609. shouldPersistUDT());
  610. iter->SeekToFirst();
  611. for (int index = 0; index < num_records; ++index) {
  612. ASSERT_TRUE(iter->Valid());
  613. Slice k = iter->key();
  614. IndexValue v = iter->value();
  615. if (keyIncludesSeq()) {
  616. EXPECT_EQ(separators[index], k.ToString());
  617. } else {
  618. const Slice user_key = ExtractUserKey(separators[index]);
  619. EXPECT_EQ(user_key, k);
  620. }
  621. EXPECT_EQ(block_handles[index].offset(), v.handle.offset());
  622. EXPECT_EQ(block_handles[index].size(), v.handle.size());
  623. EXPECT_EQ(includeFirstKey() ? first_keys[index] : "",
  624. v.first_internal_key.ToString());
  625. iter->Next();
  626. }
  627. delete iter;
  628. // read block contents randomly
  629. iter = reader.NewIndexIterator(
  630. options.comparator, kDisableGlobalSequenceNumber, kNullIter, kNullStats,
  631. kTotalOrderSeek, includeFirstKey(), keyIncludesSeq(),
  632. !useValueDeltaEncoding(), false /* block_contents_pinned */,
  633. shouldPersistUDT());
  634. for (int i = 0; i < num_records * 2; i++) {
  635. // find a random key in the lookaside array
  636. int index = rnd.Uniform(num_records);
  637. Slice k(separators[index]);
  638. // search in block for this key
  639. iter->Seek(k);
  640. ASSERT_TRUE(iter->Valid());
  641. IndexValue v = iter->value();
  642. if (keyIncludesSeq()) {
  643. EXPECT_EQ(separators[index], iter->key().ToString());
  644. } else {
  645. const Slice user_key = ExtractUserKey(separators[index]);
  646. EXPECT_EQ(user_key, iter->key());
  647. }
  648. EXPECT_EQ(block_handles[index].offset(), v.handle.offset());
  649. EXPECT_EQ(block_handles[index].size(), v.handle.size());
  650. EXPECT_EQ(includeFirstKey() ? first_keys[index] : "",
  651. v.first_internal_key.ToString());
  652. }
  653. delete iter;
  654. }
  655. // Param 0: key includes sequence number (whether to use user key or internal
  656. // key as key entry in index block).
  657. // Param 1: use value delta encoding
  658. // Param 2: include first key
  659. // Param 3: user-defined timestamp test mode
  660. INSTANTIATE_TEST_CASE_P(
  661. P, IndexBlockTest,
  662. ::testing::Combine(::testing::Bool(), ::testing::Bool(), ::testing::Bool(),
  663. ::testing::ValuesIn(test::GetUDTTestModes())));
  664. class BlockPerKVChecksumTest : public DBTestBase {
  665. public:
  666. BlockPerKVChecksumTest()
  667. : DBTestBase("block_per_kv_checksum", /*env_do_fsync=*/false) {}
  668. template <typename TBlockIter>
  669. void TestIterateForward(std::unique_ptr<TBlockIter> &biter,
  670. size_t &verification_count) {
  671. while (biter->Valid()) {
  672. verification_count = 0;
  673. biter->Next();
  674. if (biter->Valid()) {
  675. ASSERT_GE(verification_count, 1);
  676. }
  677. }
  678. }
  679. template <typename TBlockIter>
  680. void TestIterateBackward(std::unique_ptr<TBlockIter> &biter,
  681. size_t &verification_count) {
  682. while (biter->Valid()) {
  683. verification_count = 0;
  684. biter->Prev();
  685. if (biter->Valid()) {
  686. ASSERT_GE(verification_count, 1);
  687. }
  688. }
  689. }
  690. template <typename TBlockIter>
  691. void TestSeekToFirst(std::unique_ptr<TBlockIter> &biter,
  692. size_t &verification_count) {
  693. verification_count = 0;
  694. biter->SeekToFirst();
  695. ASSERT_GE(verification_count, 1);
  696. TestIterateForward(biter, verification_count);
  697. }
  698. template <typename TBlockIter>
  699. void TestSeekToLast(std::unique_ptr<TBlockIter> &biter,
  700. size_t &verification_count) {
  701. verification_count = 0;
  702. biter->SeekToLast();
  703. ASSERT_GE(verification_count, 1);
  704. TestIterateBackward(biter, verification_count);
  705. }
  706. template <typename TBlockIter>
  707. void TestSeekForPrev(std::unique_ptr<TBlockIter> &biter,
  708. size_t &verification_count, std::string k) {
  709. verification_count = 0;
  710. biter->SeekForPrev(k);
  711. ASSERT_GE(verification_count, 1);
  712. TestIterateBackward(biter, verification_count);
  713. }
  714. template <typename TBlockIter>
  715. void TestSeek(std::unique_ptr<TBlockIter> &biter, size_t &verification_count,
  716. std::string k) {
  717. verification_count = 0;
  718. biter->Seek(k);
  719. ASSERT_GE(verification_count, 1);
  720. TestIterateForward(biter, verification_count);
  721. }
  722. bool VerifyChecksum(uint32_t checksum_len, const char *checksum_ptr,
  723. const Slice &key, const Slice &val) {
  724. if (!checksum_len) {
  725. return checksum_ptr == nullptr;
  726. }
  727. return ProtectionInfo64().ProtectKV(key, val).Verify(
  728. static_cast<uint8_t>(checksum_len), checksum_ptr);
  729. }
  730. };
  731. namespace {
  732. const BlockBasedTableOptions *kTableOptions() {
  733. static BlockBasedTableOptions opts{};
  734. return &opts;
  735. }
  736. Decompressor *kDecompressor() {
  737. static auto mgr = GetBuiltinCompressionManager(
  738. GetCompressFormatForVersion(kTableOptions()->format_version));
  739. static auto decomp = mgr->GetDecompressor();
  740. return decomp.get();
  741. }
  742. } // namespace
  743. TEST_F(BlockPerKVChecksumTest, EmptyBlock) {
  744. // Tests that empty block code path is not broken by per kv checksum.
  745. BlockBuilder builder(
  746. 16 /* block_restart_interval */, true /* use_delta_encoding */,
  747. false /* use_value_delta_encoding */,
  748. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch);
  749. Slice raw_block = builder.Finish();
  750. BlockContents contents;
  751. contents.data = raw_block;
  752. std::unique_ptr<Block_kData> data_block;
  753. Options options = Options();
  754. uint8_t protection_bytes_per_key = 8;
  755. BlockCreateContext create_context{
  756. kTableOptions(), nullptr,
  757. nullptr /* statistics */, kDecompressor(),
  758. protection_bytes_per_key, options.comparator};
  759. create_context.Create(&data_block, std::move(contents));
  760. std::unique_ptr<DataBlockIter> biter{data_block->NewDataIterator(
  761. options.comparator, kDisableGlobalSequenceNumber)};
  762. biter->SeekToFirst();
  763. ASSERT_FALSE(biter->Valid());
  764. ASSERT_OK(biter->status());
  765. Random rnd(33);
  766. biter->SeekForGet(GenerateInternalKey(1, 1, 10, &rnd));
  767. ASSERT_FALSE(biter->Valid());
  768. ASSERT_OK(biter->status());
  769. biter->SeekToLast();
  770. ASSERT_FALSE(biter->Valid());
  771. ASSERT_OK(biter->status());
  772. biter->Seek(GenerateInternalKey(1, 1, 10, &rnd));
  773. ASSERT_FALSE(biter->Valid());
  774. ASSERT_OK(biter->status());
  775. biter->SeekForPrev(GenerateInternalKey(1, 1, 10, &rnd));
  776. ASSERT_FALSE(biter->Valid());
  777. ASSERT_OK(biter->status());
  778. }
  779. TEST_F(BlockPerKVChecksumTest, UnsupportedOptionValue) {
  780. Options options = Options();
  781. options.block_protection_bytes_per_key = 128;
  782. Destroy(options);
  783. ASSERT_TRUE(TryReopen(options).IsNotSupported());
  784. }
  785. TEST_F(BlockPerKVChecksumTest, InitializeProtectionInfo) {
  786. // Make sure that the checksum construction code path does not break
  787. // when the block is itself already corrupted.
  788. Options options = Options();
  789. uint8_t protection_bytes_per_key = 8;
  790. BlockCreateContext create_context{
  791. kTableOptions(), nullptr /* ioptions */, nullptr /* statistics */,
  792. kDecompressor(), protection_bytes_per_key, options.comparator};
  793. {
  794. std::string invalid_content = "1";
  795. Slice raw_block = invalid_content;
  796. BlockContents contents;
  797. contents.data = raw_block;
  798. std::unique_ptr<Block_kData> data_block;
  799. create_context.Create(&data_block, std::move(contents));
  800. std::unique_ptr<DataBlockIter> iter{data_block->NewDataIterator(
  801. options.comparator, kDisableGlobalSequenceNumber)};
  802. ASSERT_TRUE(iter->status().IsCorruption());
  803. }
  804. {
  805. std::string invalid_content = "1";
  806. Slice raw_block = invalid_content;
  807. BlockContents contents;
  808. contents.data = raw_block;
  809. std::unique_ptr<Block_kIndex> index_block;
  810. create_context.Create(&index_block, std::move(contents));
  811. std::unique_ptr<IndexBlockIter> iter{index_block->NewIndexIterator(
  812. options.comparator, kDisableGlobalSequenceNumber, nullptr, nullptr,
  813. true, false, true, true)};
  814. ASSERT_TRUE(iter->status().IsCorruption());
  815. }
  816. {
  817. std::string invalid_content = "1";
  818. Slice raw_block = invalid_content;
  819. BlockContents contents;
  820. contents.data = raw_block;
  821. std::unique_ptr<Block_kMetaIndex> meta_block;
  822. create_context.Create(&meta_block, std::move(contents));
  823. std::unique_ptr<MetaBlockIter> iter{meta_block->NewMetaIterator(true)};
  824. ASSERT_TRUE(iter->status().IsCorruption());
  825. }
  826. }
  827. TEST_F(BlockPerKVChecksumTest, ApproximateMemory) {
  828. // Tests that ApproximateMemoryUsage() includes memory used by block kv
  829. // checksum.
  830. const int kNumRecords = 20;
  831. std::vector<std::string> keys;
  832. std::vector<std::string> values;
  833. GenerateRandomKVs(&keys, &values, 0, kNumRecords, 1 /* step */,
  834. 24 /* padding_size */);
  835. std::unique_ptr<BlockBuilder> builder;
  836. auto generate_block_content = [&]() {
  837. builder = std::make_unique<BlockBuilder>(16 /* restart_interval */);
  838. for (int i = 0; i < kNumRecords; ++i) {
  839. builder->Add(keys[i], values[i]);
  840. }
  841. Slice raw_block = builder->Finish();
  842. BlockContents contents;
  843. contents.data = raw_block;
  844. return contents;
  845. };
  846. Options options = Options();
  847. uint8_t protection_bytes_per_key = 8;
  848. BlockCreateContext with_checksum_create_context{
  849. kTableOptions(),
  850. nullptr /* ioptions */,
  851. nullptr /* statistics */,
  852. kDecompressor(),
  853. protection_bytes_per_key,
  854. options.comparator,
  855. true /* index_value_is_full */};
  856. BlockCreateContext create_context{kTableOptions(),
  857. nullptr /* ioptions */,
  858. nullptr /* statistics */,
  859. kDecompressor(),
  860. 0,
  861. options.comparator,
  862. true /* index_value_is_full */};
  863. {
  864. std::unique_ptr<Block_kData> data_block;
  865. create_context.Create(&data_block, generate_block_content());
  866. size_t block_memory = data_block->ApproximateMemoryUsage();
  867. std::unique_ptr<Block_kData> with_checksum_data_block;
  868. with_checksum_create_context.Create(&with_checksum_data_block,
  869. generate_block_content());
  870. ASSERT_GT(with_checksum_data_block->ApproximateMemoryUsage() - block_memory,
  871. 100);
  872. }
  873. {
  874. std::unique_ptr<Block_kData> meta_block;
  875. create_context.Create(&meta_block, generate_block_content());
  876. size_t block_memory = meta_block->ApproximateMemoryUsage();
  877. std::unique_ptr<Block_kData> with_checksum_meta_block;
  878. with_checksum_create_context.Create(&with_checksum_meta_block,
  879. generate_block_content());
  880. // Rough comparison to avoid flaky test due to memory allocation alignment.
  881. ASSERT_GT(with_checksum_meta_block->ApproximateMemoryUsage() - block_memory,
  882. 100);
  883. }
  884. {
  885. // Index block has different contents.
  886. std::vector<std::string> separators;
  887. std::vector<BlockHandle> block_handles;
  888. std::vector<std::string> first_keys;
  889. GenerateRandomIndexEntries(&separators, &block_handles, &first_keys,
  890. kNumRecords);
  891. auto generate_index_content = [&]() {
  892. builder = std::make_unique<BlockBuilder>(16 /* restart_interval */);
  893. BlockHandle last_encoded_handle;
  894. for (int i = 0; i < kNumRecords; ++i) {
  895. IndexValue entry(block_handles[i], first_keys[i]);
  896. std::string encoded_entry;
  897. std::string delta_encoded_entry;
  898. entry.EncodeTo(&encoded_entry, false, nullptr);
  899. last_encoded_handle = entry.handle;
  900. const Slice delta_encoded_entry_slice(delta_encoded_entry);
  901. builder->Add(separators[i], encoded_entry, &delta_encoded_entry_slice);
  902. }
  903. Slice raw_block = builder->Finish();
  904. BlockContents contents;
  905. contents.data = raw_block;
  906. return contents;
  907. };
  908. std::unique_ptr<Block_kIndex> index_block;
  909. create_context.Create(&index_block, generate_index_content());
  910. size_t block_memory = index_block->ApproximateMemoryUsage();
  911. std::unique_ptr<Block_kIndex> with_checksum_index_block;
  912. with_checksum_create_context.Create(&with_checksum_index_block,
  913. generate_index_content());
  914. ASSERT_GT(
  915. with_checksum_index_block->ApproximateMemoryUsage() - block_memory,
  916. 100);
  917. }
  918. }
  919. std::string GetDataBlockIndexTypeStr(
  920. BlockBasedTableOptions::DataBlockIndexType t) {
  921. return t == BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch
  922. ? "BinarySearch"
  923. : "BinaryAndHash";
  924. }
  925. class DataBlockKVChecksumTest
  926. : public BlockPerKVChecksumTest,
  927. public testing::WithParamInterface<std::tuple<
  928. BlockBasedTableOptions::DataBlockIndexType,
  929. uint8_t /* block_protection_bytes_per_key */,
  930. uint32_t /* restart_interval*/, bool /* use_delta_encoding */>> {
  931. public:
  932. DataBlockKVChecksumTest() = default;
  933. BlockBasedTableOptions::DataBlockIndexType GetDataBlockIndexType() const {
  934. return std::get<0>(GetParam());
  935. }
  936. uint8_t GetChecksumLen() const { return std::get<1>(GetParam()); }
  937. uint32_t GetRestartInterval() const { return std::get<2>(GetParam()); }
  938. bool GetUseDeltaEncoding() const { return std::get<3>(GetParam()); }
  939. std::unique_ptr<Block_kData> GenerateDataBlock(
  940. std::vector<std::string> &keys, std::vector<std::string> &values,
  941. int num_record) {
  942. BlockCreateContext create_context{
  943. kTableOptions(), nullptr /* statistics */, nullptr /* ioptions */,
  944. kDecompressor(), GetChecksumLen(), Options().comparator};
  945. builder_ = std::make_unique<BlockBuilder>(
  946. static_cast<int>(GetRestartInterval()),
  947. GetUseDeltaEncoding() /* use_delta_encoding */,
  948. false /* use_value_delta_encoding */, GetDataBlockIndexType());
  949. for (int i = 0; i < num_record; i++) {
  950. builder_->Add(keys[i], values[i]);
  951. }
  952. Slice raw_block = builder_->Finish();
  953. BlockContents contents;
  954. contents.data = raw_block;
  955. std::unique_ptr<Block_kData> data_block;
  956. create_context.Create(&data_block, std::move(contents));
  957. return data_block;
  958. }
  959. std::unique_ptr<BlockBuilder> builder_;
  960. };
  961. INSTANTIATE_TEST_CASE_P(
  962. P, DataBlockKVChecksumTest,
  963. ::testing::Combine(
  964. ::testing::Values(
  965. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch,
  966. BlockBasedTableOptions::DataBlockIndexType::
  967. kDataBlockBinaryAndHash),
  968. ::testing::Values(0, 1, 2, 4, 8) /* protection_bytes_per_key */,
  969. ::testing::Values(1, 2, 3, 8, 16) /* restart_interval */,
  970. ::testing::Values(false, true)) /* delta_encoding */,
  971. [](const testing::TestParamInfo<std::tuple<
  972. BlockBasedTableOptions::DataBlockIndexType, uint8_t, uint32_t, bool>>
  973. &args) {
  974. std::ostringstream oss;
  975. oss << GetDataBlockIndexTypeStr(std::get<0>(args.param))
  976. << "ProtectionPerKey" << std::to_string(std::get<1>(args.param))
  977. << "RestartInterval" << std::to_string(std::get<2>(args.param))
  978. << "DeltaEncode" << std::to_string(std::get<3>(args.param));
  979. return oss.str();
  980. });
  981. TEST_P(DataBlockKVChecksumTest, ChecksumConstructionAndVerification) {
  982. uint8_t protection_bytes_per_key = GetChecksumLen();
  983. std::vector<int> num_restart_intervals = {1, 16};
  984. for (const auto num_restart_interval : num_restart_intervals) {
  985. const int kNumRecords =
  986. num_restart_interval * static_cast<int>(GetRestartInterval());
  987. std::vector<std::string> keys;
  988. std::vector<std::string> values;
  989. GenerateRandomKVs(&keys, &values, 0, kNumRecords + 1, 1 /* step */,
  990. 24 /* padding_size */);
  991. SyncPoint::GetInstance()->DisableProcessing();
  992. std::unique_ptr<Block_kData> data_block =
  993. GenerateDataBlock(keys, values, kNumRecords);
  994. const char *checksum_ptr = data_block->TEST_GetKVChecksum();
  995. // Check checksum of correct length is generated
  996. for (int i = 0; i < kNumRecords; i++) {
  997. ASSERT_TRUE(VerifyChecksum(protection_bytes_per_key,
  998. checksum_ptr + i * protection_bytes_per_key,
  999. keys[i], values[i]));
  1000. }
  1001. std::vector<SequenceNumber> seqnos{kDisableGlobalSequenceNumber, 0};
  1002. // Could just use a boolean flag. Use a counter here just to keep open the
  1003. // possibility of checking the exact number of verifications in the future.
  1004. size_t verification_count = 0;
  1005. // The SyncPoint is placed before checking checksum_len == 0 in
  1006. // Block::VerifyChecksum(). So verification count is incremented even with
  1007. // protection_bytes_per_key = 0. No actual checksum computation is done in
  1008. // that case (see Block::VerifyChecksum()).
  1009. SyncPoint::GetInstance()->SetCallBack(
  1010. "Block::VerifyChecksum::checksum_len",
  1011. [&verification_count, protection_bytes_per_key](void *checksum_len) {
  1012. ASSERT_EQ((*static_cast<uint8_t *>(checksum_len)),
  1013. protection_bytes_per_key);
  1014. ++verification_count;
  1015. });
  1016. SyncPoint::GetInstance()->EnableProcessing();
  1017. for (const auto seqno : seqnos) {
  1018. std::unique_ptr<DataBlockIter> biter{
  1019. data_block->NewDataIterator(Options().comparator, seqno)};
  1020. // SeekForGet() some key that does not exist
  1021. biter->SeekForGet(keys[kNumRecords]);
  1022. TestIterateForward(biter, verification_count);
  1023. verification_count = 0;
  1024. biter->SeekForGet(keys[kNumRecords / 2]);
  1025. ASSERT_GE(verification_count, 1);
  1026. TestIterateForward(biter, verification_count);
  1027. TestSeekToFirst(biter, verification_count);
  1028. TestSeekToLast(biter, verification_count);
  1029. TestSeekForPrev(biter, verification_count, keys[kNumRecords / 2]);
  1030. TestSeek(biter, verification_count, keys[kNumRecords / 2]);
  1031. }
  1032. }
  1033. }
  1034. class IndexBlockKVChecksumTest
  1035. : public BlockPerKVChecksumTest,
  1036. public testing::WithParamInterface<
  1037. std::tuple<BlockBasedTableOptions::DataBlockIndexType, uint8_t,
  1038. uint32_t, bool, bool>> {
  1039. public:
  1040. IndexBlockKVChecksumTest() = default;
  1041. BlockBasedTableOptions::DataBlockIndexType GetDataBlockIndexType() const {
  1042. return std::get<0>(GetParam());
  1043. }
  1044. uint8_t GetChecksumLen() const { return std::get<1>(GetParam()); }
  1045. uint32_t GetRestartInterval() const { return std::get<2>(GetParam()); }
  1046. bool UseValueDeltaEncoding() const { return std::get<3>(GetParam()); }
  1047. bool IncludeFirstKey() const { return std::get<4>(GetParam()); }
  1048. std::unique_ptr<Block_kIndex> GenerateIndexBlock(
  1049. std::vector<std::string> &separators,
  1050. std::vector<BlockHandle> &block_handles,
  1051. std::vector<std::string> &first_keys, int num_record) {
  1052. Options options = Options();
  1053. uint8_t protection_bytes_per_key = GetChecksumLen();
  1054. BlockCreateContext create_context{
  1055. kTableOptions(),
  1056. nullptr /* ioptions */,
  1057. nullptr /* statistics */,
  1058. kDecompressor(),
  1059. protection_bytes_per_key,
  1060. options.comparator,
  1061. !UseValueDeltaEncoding() /* value_is_full */,
  1062. IncludeFirstKey()};
  1063. builder_ = std::make_unique<BlockBuilder>(
  1064. static_cast<int>(GetRestartInterval()), true /* use_delta_encoding */,
  1065. UseValueDeltaEncoding() /* use_value_delta_encoding */,
  1066. GetDataBlockIndexType());
  1067. BlockHandle last_encoded_handle;
  1068. for (int i = 0; i < num_record; i++) {
  1069. IndexValue entry(block_handles[i], first_keys[i]);
  1070. std::string encoded_entry;
  1071. std::string delta_encoded_entry;
  1072. entry.EncodeTo(&encoded_entry, IncludeFirstKey(), nullptr);
  1073. if (UseValueDeltaEncoding() && i > 0) {
  1074. entry.EncodeTo(&delta_encoded_entry, IncludeFirstKey(),
  1075. &last_encoded_handle);
  1076. }
  1077. last_encoded_handle = entry.handle;
  1078. const Slice delta_encoded_entry_slice(delta_encoded_entry);
  1079. builder_->Add(separators[i], encoded_entry, &delta_encoded_entry_slice);
  1080. }
  1081. // read serialized contents of the block
  1082. Slice raw_block = builder_->Finish();
  1083. // create block reader
  1084. BlockContents contents;
  1085. contents.data = raw_block;
  1086. std::unique_ptr<Block_kIndex> index_block;
  1087. create_context.Create(&index_block, std::move(contents));
  1088. return index_block;
  1089. }
  1090. std::unique_ptr<BlockBuilder> builder_;
  1091. };
  1092. INSTANTIATE_TEST_CASE_P(
  1093. P, IndexBlockKVChecksumTest,
  1094. ::testing::Combine(
  1095. ::testing::Values(
  1096. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch,
  1097. BlockBasedTableOptions::DataBlockIndexType::
  1098. kDataBlockBinaryAndHash),
  1099. ::testing::Values(0, 1, 2, 4, 8), ::testing::Values(1, 3, 8, 16),
  1100. ::testing::Values(true, false), ::testing::Values(true, false)),
  1101. [](const testing::TestParamInfo<
  1102. std::tuple<BlockBasedTableOptions::DataBlockIndexType, uint8_t,
  1103. uint32_t, bool, bool>> &args) {
  1104. std::ostringstream oss;
  1105. oss << GetDataBlockIndexTypeStr(std::get<0>(args.param)) << "ProtBytes"
  1106. << std::to_string(std::get<1>(args.param)) << "RestartInterval"
  1107. << std::to_string(std::get<2>(args.param)) << "ValueDeltaEncode"
  1108. << std::to_string(std::get<3>(args.param)) << "IncludeFirstKey"
  1109. << std::to_string(std::get<4>(args.param));
  1110. return oss.str();
  1111. });
  1112. TEST_P(IndexBlockKVChecksumTest, ChecksumConstructionAndVerification) {
  1113. Options options = Options();
  1114. uint8_t protection_bytes_per_key = GetChecksumLen();
  1115. std::vector<int> num_restart_intervals = {1, 16};
  1116. std::vector<SequenceNumber> seqnos{kDisableGlobalSequenceNumber, 10001};
  1117. for (const auto num_restart_interval : num_restart_intervals) {
  1118. const int kNumRecords =
  1119. num_restart_interval * static_cast<int>(GetRestartInterval());
  1120. for (const auto seqno : seqnos) {
  1121. std::vector<std::string> separators;
  1122. std::vector<BlockHandle> block_handles;
  1123. std::vector<std::string> first_keys;
  1124. GenerateRandomIndexEntries(&separators, &block_handles, &first_keys,
  1125. kNumRecords, 0 /* ts_sz */,
  1126. seqno != kDisableGlobalSequenceNumber);
  1127. SyncPoint::GetInstance()->DisableProcessing();
  1128. std::unique_ptr<Block_kIndex> index_block = GenerateIndexBlock(
  1129. separators, block_handles, first_keys, kNumRecords);
  1130. IndexBlockIter *kNullIter = nullptr;
  1131. Statistics *kNullStats = nullptr;
  1132. // read contents of block sequentially
  1133. std::unique_ptr<IndexBlockIter> biter{index_block->NewIndexIterator(
  1134. options.comparator, seqno, kNullIter, kNullStats,
  1135. true /* total_order_seek */, IncludeFirstKey() /* have_first_key */,
  1136. true /* key_includes_seq */,
  1137. !UseValueDeltaEncoding() /* value_is_full */,
  1138. true /* block_contents_pinned*/,
  1139. true /* user_defined_timestamps_persisted */,
  1140. nullptr /* prefix_index */)};
  1141. biter->SeekToFirst();
  1142. const char *checksum_ptr = index_block->TEST_GetKVChecksum();
  1143. // Check checksum of correct length is generated
  1144. for (int i = 0; i < kNumRecords; i++) {
  1145. // Obtaining the actual content written as value to index block is not
  1146. // trivial: delta-encoded value is only persisted when not at block
  1147. // restart point and that keys share some byte (see more in
  1148. // BlockBuilder::AddWithLastKeyImpl()). So here we just do verification
  1149. // using value from iterator unlike tests for DataBlockIter or
  1150. // MetaBlockIter.
  1151. ASSERT_TRUE(VerifyChecksum(protection_bytes_per_key, checksum_ptr,
  1152. biter->key(), biter->raw_value()));
  1153. }
  1154. size_t verification_count = 0;
  1155. // The SyncPoint is placed before checking checksum_len == 0 in
  1156. // Block::VerifyChecksum(). To make the testing code below simpler and not
  1157. // having to differentiate 0 vs non-0 checksum_len, we do an explicit
  1158. // assert checking on checksum_len here.
  1159. SyncPoint::GetInstance()->SetCallBack(
  1160. "Block::VerifyChecksum::checksum_len",
  1161. [&verification_count, protection_bytes_per_key](void *checksum_len) {
  1162. ASSERT_EQ((*static_cast<uint8_t *>(checksum_len)),
  1163. protection_bytes_per_key);
  1164. ++verification_count;
  1165. });
  1166. SyncPoint::GetInstance()->EnableProcessing();
  1167. TestSeekToFirst(biter, verification_count);
  1168. TestSeekToLast(biter, verification_count);
  1169. TestSeek(biter, verification_count, first_keys[kNumRecords / 2]);
  1170. }
  1171. }
  1172. }
  1173. class MetaIndexBlockKVChecksumTest
  1174. : public BlockPerKVChecksumTest,
  1175. public testing::WithParamInterface<
  1176. uint8_t /* block_protection_bytes_per_key */> {
  1177. public:
  1178. MetaIndexBlockKVChecksumTest() = default;
  1179. uint8_t GetChecksumLen() const { return GetParam(); }
  1180. uint32_t GetRestartInterval() const { return 1; }
  1181. std::unique_ptr<Block_kMetaIndex> GenerateMetaIndexBlock(
  1182. std::vector<std::string> &keys, std::vector<std::string> &values,
  1183. int num_record) {
  1184. Options options = Options();
  1185. uint8_t protection_bytes_per_key = GetChecksumLen();
  1186. BlockCreateContext create_context{
  1187. kTableOptions(), nullptr /* ioptions */, nullptr /* statistics */,
  1188. kDecompressor(), protection_bytes_per_key, options.comparator};
  1189. builder_ =
  1190. std::make_unique<BlockBuilder>(static_cast<int>(GetRestartInterval()));
  1191. // add a bunch of records to a block
  1192. for (int i = 0; i < num_record; i++) {
  1193. builder_->Add(keys[i], values[i]);
  1194. }
  1195. Slice raw_block = builder_->Finish();
  1196. BlockContents contents;
  1197. contents.data = raw_block;
  1198. std::unique_ptr<Block_kMetaIndex> meta_block;
  1199. create_context.Create(&meta_block, std::move(contents));
  1200. return meta_block;
  1201. }
  1202. std::unique_ptr<BlockBuilder> builder_;
  1203. };
  1204. INSTANTIATE_TEST_CASE_P(P, MetaIndexBlockKVChecksumTest,
  1205. ::testing::Values(0, 1, 2, 4, 8),
  1206. [](const testing::TestParamInfo<uint8_t> &args) {
  1207. std::ostringstream oss;
  1208. oss << "ProtBytes" << std::to_string(args.param);
  1209. return oss.str();
  1210. });
  1211. TEST_P(MetaIndexBlockKVChecksumTest, ChecksumConstructionAndVerification) {
  1212. Options options = Options();
  1213. uint8_t protection_bytes_per_key = GetChecksumLen();
  1214. BlockCreateContext create_context{
  1215. kTableOptions(), nullptr /* ioptions */, nullptr /* statistics */,
  1216. kDecompressor(), protection_bytes_per_key, options.comparator};
  1217. std::vector<int> num_restart_intervals = {1, 16};
  1218. for (const auto num_restart_interval : num_restart_intervals) {
  1219. const int kNumRecords = num_restart_interval * GetRestartInterval();
  1220. std::vector<std::string> keys;
  1221. std::vector<std::string> values;
  1222. GenerateRandomKVs(&keys, &values, 0, kNumRecords + 1, 1 /* step */,
  1223. 24 /* padding_size */);
  1224. SyncPoint::GetInstance()->DisableProcessing();
  1225. std::unique_ptr<Block_kMetaIndex> meta_block =
  1226. GenerateMetaIndexBlock(keys, values, kNumRecords);
  1227. const char *checksum_ptr = meta_block->TEST_GetKVChecksum();
  1228. // Check checksum of correct length is generated
  1229. for (int i = 0; i < kNumRecords; i++) {
  1230. ASSERT_TRUE(VerifyChecksum(protection_bytes_per_key,
  1231. checksum_ptr + i * protection_bytes_per_key,
  1232. keys[i], values[i]));
  1233. }
  1234. size_t verification_count = 0;
  1235. // The SyncPoint is placed before checking checksum_len == 0 in
  1236. // Block::VerifyChecksum(). To make the testing code below simpler and not
  1237. // having to differentiate 0 vs non-0 checksum_len, we do an explicit assert
  1238. // checking on checksum_len here.
  1239. SyncPoint::GetInstance()->SetCallBack(
  1240. "Block::VerifyChecksum::checksum_len",
  1241. [&verification_count, protection_bytes_per_key](void *checksum_len) {
  1242. ASSERT_EQ((*static_cast<uint8_t *>(checksum_len)),
  1243. protection_bytes_per_key);
  1244. ++verification_count;
  1245. });
  1246. SyncPoint::GetInstance()->EnableProcessing();
  1247. // Check that block iterator does checksum verification
  1248. std::unique_ptr<MetaBlockIter> biter{
  1249. meta_block->NewMetaIterator(true /* block_contents_pinned */)};
  1250. TestSeekToFirst(biter, verification_count);
  1251. TestSeekToLast(biter, verification_count);
  1252. TestSeek(biter, verification_count, keys[kNumRecords / 2]);
  1253. TestSeekForPrev(biter, verification_count, keys[kNumRecords / 2]);
  1254. }
  1255. }
  1256. class DataBlockKVChecksumCorruptionTest : public DataBlockKVChecksumTest {
  1257. public:
  1258. DataBlockKVChecksumCorruptionTest() = default;
  1259. std::unique_ptr<DataBlockIter> GenerateDataBlockIter(
  1260. std::vector<std::string> &keys, std::vector<std::string> &values,
  1261. int num_record) {
  1262. // During Block construction, we may create block iter to initialize per kv
  1263. // checksum. Disable syncpoint that may be created for block iter methods.
  1264. SyncPoint::GetInstance()->DisableProcessing();
  1265. block_ = GenerateDataBlock(keys, values, num_record);
  1266. std::unique_ptr<DataBlockIter> biter{block_->NewDataIterator(
  1267. Options().comparator, kDisableGlobalSequenceNumber)};
  1268. SyncPoint::GetInstance()->EnableProcessing();
  1269. return biter;
  1270. }
  1271. protected:
  1272. std::unique_ptr<Block_kData> block_;
  1273. };
  1274. TEST_P(DataBlockKVChecksumCorruptionTest, CorruptEntry) {
  1275. std::vector<int> num_restart_intervals = {1, 3};
  1276. for (const auto num_restart_interval : num_restart_intervals) {
  1277. const int kNumRecords =
  1278. num_restart_interval * static_cast<int>(GetRestartInterval());
  1279. std::vector<std::string> keys;
  1280. std::vector<std::string> values;
  1281. GenerateRandomKVs(&keys, &values, 0, kNumRecords + 1, 1 /* step */,
  1282. 24 /* padding_size */);
  1283. SyncPoint::GetInstance()->SetCallBack(
  1284. "BlockIter::UpdateKey::value", [](void *arg) {
  1285. char *value = static_cast<char *>(arg);
  1286. // values generated by GenerateRandomKVs are of length 100
  1287. ++value[10];
  1288. });
  1289. // Purely for reducing the number of lines of code.
  1290. typedef std::unique_ptr<DataBlockIter> IterPtr;
  1291. typedef void(IterAPI)(IterPtr & iter, std::string &);
  1292. std::string seek_key = keys[kNumRecords / 2];
  1293. auto test_seek = [&](IterAPI iter_api) {
  1294. IterPtr biter = GenerateDataBlockIter(keys, values, kNumRecords);
  1295. ASSERT_OK(biter->status());
  1296. iter_api(biter, seek_key);
  1297. ASSERT_FALSE(biter->Valid());
  1298. ASSERT_TRUE(biter->status().IsCorruption());
  1299. };
  1300. test_seek([](IterPtr &iter, std::string &) { iter->SeekToFirst(); });
  1301. test_seek([](IterPtr &iter, std::string &) { iter->SeekToLast(); });
  1302. test_seek([](IterPtr &iter, std::string &k) { iter->Seek(k); });
  1303. test_seek([](IterPtr &iter, std::string &k) { iter->SeekForPrev(k); });
  1304. test_seek([](IterPtr &iter, std::string &k) { iter->SeekForGet(k); });
  1305. typedef void (DataBlockIter::*IterStepAPI)();
  1306. auto test_step = [&](IterStepAPI iter_api, std::string &k) {
  1307. IterPtr biter = GenerateDataBlockIter(keys, values, kNumRecords);
  1308. SyncPoint::GetInstance()->DisableProcessing();
  1309. biter->Seek(k);
  1310. ASSERT_TRUE(biter->Valid());
  1311. ASSERT_OK(biter->status());
  1312. SyncPoint::GetInstance()->EnableProcessing();
  1313. std::invoke(iter_api, biter);
  1314. ASSERT_FALSE(biter->Valid());
  1315. ASSERT_TRUE(biter->status().IsCorruption());
  1316. };
  1317. if (kNumRecords > 1) {
  1318. test_step(&DataBlockIter::Prev, seek_key);
  1319. test_step(&DataBlockIter::Next, seek_key);
  1320. }
  1321. }
  1322. }
  1323. INSTANTIATE_TEST_CASE_P(
  1324. P, DataBlockKVChecksumCorruptionTest,
  1325. ::testing::Combine(
  1326. ::testing::Values(
  1327. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch,
  1328. BlockBasedTableOptions::DataBlockIndexType::
  1329. kDataBlockBinaryAndHash),
  1330. ::testing::Values(4, 8) /* block_protection_bytes_per_key */,
  1331. ::testing::Values(1, 3, 8, 16) /* restart_interval */,
  1332. ::testing::Values(false, true)),
  1333. [](const testing::TestParamInfo<std::tuple<
  1334. BlockBasedTableOptions::DataBlockIndexType, uint8_t, uint32_t, bool>>
  1335. &args) {
  1336. std::ostringstream oss;
  1337. oss << GetDataBlockIndexTypeStr(std::get<0>(args.param)) << "ProtBytes"
  1338. << std::to_string(std::get<1>(args.param)) << "RestartInterval"
  1339. << std::to_string(std::get<2>(args.param)) << "DeltaEncode"
  1340. << std::to_string(std::get<3>(args.param));
  1341. return oss.str();
  1342. });
  1343. class IndexBlockKVChecksumCorruptionTest : public IndexBlockKVChecksumTest {
  1344. public:
  1345. IndexBlockKVChecksumCorruptionTest() = default;
  1346. std::unique_ptr<IndexBlockIter> GenerateIndexBlockIter(
  1347. std::vector<std::string> &separators,
  1348. std::vector<BlockHandle> &block_handles,
  1349. std::vector<std::string> &first_keys, int num_record,
  1350. SequenceNumber seqno) {
  1351. SyncPoint::GetInstance()->DisableProcessing();
  1352. block_ =
  1353. GenerateIndexBlock(separators, block_handles, first_keys, num_record);
  1354. std::unique_ptr<IndexBlockIter> biter{block_->NewIndexIterator(
  1355. Options().comparator, seqno, nullptr, nullptr,
  1356. true /* total_order_seek */, IncludeFirstKey() /* have_first_key */,
  1357. true /* key_includes_seq */,
  1358. !UseValueDeltaEncoding() /* value_is_full */,
  1359. true /* block_contents_pinned */,
  1360. true /* user_defined_timestamps_persisted */,
  1361. nullptr /* prefix_index */)};
  1362. SyncPoint::GetInstance()->EnableProcessing();
  1363. return biter;
  1364. }
  1365. protected:
  1366. std::unique_ptr<Block_kIndex> block_;
  1367. };
  1368. INSTANTIATE_TEST_CASE_P(
  1369. P, IndexBlockKVChecksumCorruptionTest,
  1370. ::testing::Combine(
  1371. ::testing::Values(
  1372. BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch,
  1373. BlockBasedTableOptions::DataBlockIndexType::
  1374. kDataBlockBinaryAndHash),
  1375. ::testing::Values(4, 8) /* block_protection_bytes_per_key */,
  1376. ::testing::Values(1, 3, 8, 16) /* restart_interval */,
  1377. ::testing::Values(true, false), ::testing::Values(true, false)),
  1378. [](const testing::TestParamInfo<
  1379. std::tuple<BlockBasedTableOptions::DataBlockIndexType, uint8_t,
  1380. uint32_t, bool, bool>> &args) {
  1381. std::ostringstream oss;
  1382. oss << GetDataBlockIndexTypeStr(std::get<0>(args.param)) << "ProtBytes"
  1383. << std::to_string(std::get<1>(args.param)) << "RestartInterval"
  1384. << std::to_string(std::get<2>(args.param)) << "ValueDeltaEncode"
  1385. << std::to_string(std::get<3>(args.param)) << "IncludeFirstKey"
  1386. << std::to_string(std::get<4>(args.param));
  1387. return oss.str();
  1388. });
  1389. TEST_P(IndexBlockKVChecksumCorruptionTest, CorruptEntry) {
  1390. std::vector<int> num_restart_intervals = {1, 3};
  1391. std::vector<SequenceNumber> seqnos{kDisableGlobalSequenceNumber, 10001};
  1392. for (const auto num_restart_interval : num_restart_intervals) {
  1393. const int kNumRecords =
  1394. num_restart_interval * static_cast<int>(GetRestartInterval());
  1395. for (const auto seqno : seqnos) {
  1396. std::vector<std::string> separators;
  1397. std::vector<BlockHandle> block_handles;
  1398. std::vector<std::string> first_keys;
  1399. GenerateRandomIndexEntries(&separators, &block_handles, &first_keys,
  1400. kNumRecords, 0 /* ts_sz */,
  1401. seqno != kDisableGlobalSequenceNumber);
  1402. SyncPoint::GetInstance()->SetCallBack(
  1403. "BlockIter::UpdateKey::value", [](void *arg) {
  1404. char *value = static_cast<char *>(arg);
  1405. // value can be delta-encoded with different lengths, so we corrupt
  1406. // first bytes here to be safe
  1407. ++value[0];
  1408. });
  1409. typedef std::unique_ptr<IndexBlockIter> IterPtr;
  1410. typedef void(IterAPI)(IterPtr & iter, std::string &);
  1411. std::string seek_key = first_keys[kNumRecords / 2];
  1412. auto test_seek = [&](IterAPI iter_api) {
  1413. std::unique_ptr<IndexBlockIter> biter = GenerateIndexBlockIter(
  1414. separators, block_handles, first_keys, kNumRecords, seqno);
  1415. ASSERT_OK(biter->status());
  1416. iter_api(biter, seek_key);
  1417. ASSERT_FALSE(biter->Valid());
  1418. ASSERT_TRUE(biter->status().IsCorruption());
  1419. };
  1420. test_seek([](IterPtr &iter, std::string &) { iter->SeekToFirst(); });
  1421. test_seek([](IterPtr &iter, std::string &) { iter->SeekToLast(); });
  1422. test_seek([](IterPtr &iter, std::string &k) { iter->Seek(k); });
  1423. typedef void (IndexBlockIter::*IterStepAPI)();
  1424. auto test_step = [&](IterStepAPI iter_api, std::string &k) {
  1425. std::unique_ptr<IndexBlockIter> biter = GenerateIndexBlockIter(
  1426. separators, block_handles, first_keys, kNumRecords, seqno);
  1427. SyncPoint::GetInstance()->DisableProcessing();
  1428. biter->Seek(k);
  1429. ASSERT_TRUE(biter->Valid());
  1430. ASSERT_OK(biter->status());
  1431. SyncPoint::GetInstance()->EnableProcessing();
  1432. std::invoke(iter_api, biter);
  1433. ASSERT_FALSE(biter->Valid());
  1434. ASSERT_TRUE(biter->status().IsCorruption());
  1435. };
  1436. if (kNumRecords > 1) {
  1437. test_step(&IndexBlockIter::Prev, seek_key);
  1438. test_step(&IndexBlockIter::Next, seek_key);
  1439. }
  1440. }
  1441. }
  1442. }
  1443. class MetaIndexBlockKVChecksumCorruptionTest
  1444. : public MetaIndexBlockKVChecksumTest {
  1445. public:
  1446. MetaIndexBlockKVChecksumCorruptionTest() = default;
  1447. std::unique_ptr<MetaBlockIter> GenerateMetaIndexBlockIter(
  1448. std::vector<std::string> &keys, std::vector<std::string> &values,
  1449. int num_record) {
  1450. SyncPoint::GetInstance()->DisableProcessing();
  1451. block_ = GenerateMetaIndexBlock(keys, values, num_record);
  1452. std::unique_ptr<MetaBlockIter> biter{
  1453. block_->NewMetaIterator(true /* block_contents_pinned */)};
  1454. SyncPoint::GetInstance()->EnableProcessing();
  1455. return biter;
  1456. }
  1457. protected:
  1458. std::unique_ptr<Block_kMetaIndex> block_;
  1459. };
  1460. INSTANTIATE_TEST_CASE_P(
  1461. P, MetaIndexBlockKVChecksumCorruptionTest,
  1462. ::testing::Values(4, 8) /* block_protection_bytes_per_key */,
  1463. [](const testing::TestParamInfo<uint8_t> &args) {
  1464. std::ostringstream oss;
  1465. oss << "ProtBytes" << std::to_string(args.param);
  1466. return oss.str();
  1467. });
  1468. TEST_P(MetaIndexBlockKVChecksumCorruptionTest, CorruptEntry) {
  1469. Options options = Options();
  1470. std::vector<int> num_restart_intervals = {1, 3};
  1471. for (const auto num_restart_interval : num_restart_intervals) {
  1472. const int kNumRecords =
  1473. num_restart_interval * static_cast<int>(GetRestartInterval());
  1474. std::vector<std::string> keys;
  1475. std::vector<std::string> values;
  1476. GenerateRandomKVs(&keys, &values, 0, kNumRecords + 1, 1 /* step */,
  1477. 24 /* padding_size */);
  1478. SyncPoint::GetInstance()->SetCallBack(
  1479. "BlockIter::UpdateKey::value", [](void *arg) {
  1480. char *value = static_cast<char *>(arg);
  1481. // values generated by GenerateRandomKVs are of length 100
  1482. ++value[10];
  1483. });
  1484. typedef std::unique_ptr<MetaBlockIter> IterPtr;
  1485. typedef void(IterAPI)(IterPtr & iter, std::string &);
  1486. typedef void (MetaBlockIter::*IterStepAPI)();
  1487. std::string seek_key = keys[kNumRecords / 2];
  1488. auto test_seek = [&](IterAPI iter_api) {
  1489. IterPtr biter = GenerateMetaIndexBlockIter(keys, values, kNumRecords);
  1490. ASSERT_OK(biter->status());
  1491. iter_api(biter, seek_key);
  1492. ASSERT_FALSE(biter->Valid());
  1493. ASSERT_TRUE(biter->status().IsCorruption());
  1494. };
  1495. test_seek([](IterPtr &iter, std::string &) { iter->SeekToFirst(); });
  1496. test_seek([](IterPtr &iter, std::string &) { iter->SeekToLast(); });
  1497. test_seek([](IterPtr &iter, std::string &k) { iter->Seek(k); });
  1498. test_seek([](IterPtr &iter, std::string &k) { iter->SeekForPrev(k); });
  1499. auto test_step = [&](IterStepAPI iter_api, const std::string &k) {
  1500. IterPtr biter = GenerateMetaIndexBlockIter(keys, values, kNumRecords);
  1501. SyncPoint::GetInstance()->DisableProcessing();
  1502. biter->Seek(k);
  1503. ASSERT_TRUE(biter->Valid());
  1504. ASSERT_OK(biter->status());
  1505. SyncPoint::GetInstance()->EnableProcessing();
  1506. std::invoke(iter_api, biter);
  1507. ASSERT_FALSE(biter->Valid());
  1508. ASSERT_TRUE(biter->status().IsCorruption());
  1509. };
  1510. if (kNumRecords > 1) {
  1511. test_step(&MetaBlockIter::Prev, seek_key);
  1512. test_step(&MetaBlockIter::Next, seek_key);
  1513. }
  1514. }
  1515. }
  1516. } // namespace ROCKSDB_NAMESPACE
  1517. int main(int argc, char **argv) {
  1518. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  1519. ::testing::InitGoogleTest(&argc, argv);
  1520. return RUN_ALL_TESTS();
  1521. }