data_block_hash_index_test.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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. #include <cstdlib>
  6. #include <string>
  7. #include <unordered_map>
  8. #include "db/table_properties_collector.h"
  9. #include "rocksdb/slice.h"
  10. #include "table/block_based/block.h"
  11. #include "table/block_based/block_based_table_reader.h"
  12. #include "table/block_based/block_builder.h"
  13. #include "table/block_based/data_block_hash_index.h"
  14. #include "table/get_context.h"
  15. #include "table/table_builder.h"
  16. #include "test_util/testharness.h"
  17. #include "test_util/testutil.h"
  18. namespace ROCKSDB_NAMESPACE {
  19. bool SearchForOffset(DataBlockHashIndex& index, const char* data,
  20. uint16_t map_offset, const Slice& key,
  21. uint8_t& restart_point) {
  22. uint8_t entry = index.Lookup(data, map_offset, key);
  23. if (entry == kCollision) {
  24. return true;
  25. }
  26. if (entry == kNoEntry) {
  27. return false;
  28. }
  29. return entry == restart_point;
  30. }
  31. // Random KV generator similer to block_test
  32. static std::string RandomString(Random* rnd, int len) {
  33. std::string r;
  34. test::RandomString(rnd, len, &r);
  35. return r;
  36. }
  37. std::string GenerateKey(int primary_key, int secondary_key, int padding_size,
  38. Random* rnd) {
  39. char buf[50];
  40. char* p = &buf[0];
  41. snprintf(buf, sizeof(buf), "%6d%4d", primary_key, secondary_key);
  42. std::string k(p);
  43. if (padding_size) {
  44. k += RandomString(rnd, padding_size);
  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) {
  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. keys->emplace_back(GenerateKey(i, j, padding_size, &rnd));
  62. // 100 bytes values
  63. values->emplace_back(RandomString(&rnd, 100));
  64. }
  65. }
  66. }
  67. TEST(DataBlockHashIndex, DataBlockHashTestSmall) {
  68. DataBlockHashIndexBuilder builder;
  69. builder.Initialize(0.75 /*util_ratio*/);
  70. for (int j = 0; j < 5; j++) {
  71. for (uint8_t i = 0; i < 2 + j; i++) {
  72. std::string key("key" + std::to_string(i));
  73. uint8_t restart_point = i;
  74. builder.Add(key, restart_point);
  75. }
  76. size_t estimated_size = builder.EstimateSize();
  77. std::string buffer("fake"), buffer2;
  78. size_t original_size = buffer.size();
  79. estimated_size += original_size;
  80. builder.Finish(buffer);
  81. ASSERT_EQ(buffer.size(), estimated_size);
  82. buffer2 = buffer; // test for the correctness of relative offset
  83. Slice s(buffer2);
  84. DataBlockHashIndex index;
  85. uint16_t map_offset;
  86. index.Initialize(s.data(), static_cast<uint16_t>(s.size()), &map_offset);
  87. // the additional hash map should start at the end of the buffer
  88. ASSERT_EQ(original_size, map_offset);
  89. for (uint8_t i = 0; i < 2; i++) {
  90. std::string key("key" + std::to_string(i));
  91. uint8_t restart_point = i;
  92. ASSERT_TRUE(
  93. SearchForOffset(index, s.data(), map_offset, key, restart_point));
  94. }
  95. builder.Reset();
  96. }
  97. }
  98. TEST(DataBlockHashIndex, DataBlockHashTest) {
  99. // bucket_num = 200, #keys = 100. 50% utilization
  100. DataBlockHashIndexBuilder builder;
  101. builder.Initialize(0.75 /*util_ratio*/);
  102. for (uint8_t i = 0; i < 100; i++) {
  103. std::string key("key" + std::to_string(i));
  104. uint8_t restart_point = i;
  105. builder.Add(key, restart_point);
  106. }
  107. size_t estimated_size = builder.EstimateSize();
  108. std::string buffer("fake content"), buffer2;
  109. size_t original_size = buffer.size();
  110. estimated_size += original_size;
  111. builder.Finish(buffer);
  112. ASSERT_EQ(buffer.size(), estimated_size);
  113. buffer2 = buffer; // test for the correctness of relative offset
  114. Slice s(buffer2);
  115. DataBlockHashIndex index;
  116. uint16_t map_offset;
  117. index.Initialize(s.data(), static_cast<uint16_t>(s.size()), &map_offset);
  118. // the additional hash map should start at the end of the buffer
  119. ASSERT_EQ(original_size, map_offset);
  120. for (uint8_t i = 0; i < 100; i++) {
  121. std::string key("key" + std::to_string(i));
  122. uint8_t restart_point = i;
  123. ASSERT_TRUE(
  124. SearchForOffset(index, s.data(), map_offset, key, restart_point));
  125. }
  126. }
  127. TEST(DataBlockHashIndex, DataBlockHashTestCollision) {
  128. // bucket_num = 2. There will be intense hash collisions
  129. DataBlockHashIndexBuilder builder;
  130. builder.Initialize(0.75 /*util_ratio*/);
  131. for (uint8_t i = 0; i < 100; i++) {
  132. std::string key("key" + std::to_string(i));
  133. uint8_t restart_point = i;
  134. builder.Add(key, restart_point);
  135. }
  136. size_t estimated_size = builder.EstimateSize();
  137. std::string buffer("some other fake content to take up space"), buffer2;
  138. size_t original_size = buffer.size();
  139. estimated_size += original_size;
  140. builder.Finish(buffer);
  141. ASSERT_EQ(buffer.size(), estimated_size);
  142. buffer2 = buffer; // test for the correctness of relative offset
  143. Slice s(buffer2);
  144. DataBlockHashIndex index;
  145. uint16_t map_offset;
  146. index.Initialize(s.data(), static_cast<uint16_t>(s.size()), &map_offset);
  147. // the additional hash map should start at the end of the buffer
  148. ASSERT_EQ(original_size, map_offset);
  149. for (uint8_t i = 0; i < 100; i++) {
  150. std::string key("key" + std::to_string(i));
  151. uint8_t restart_point = i;
  152. ASSERT_TRUE(
  153. SearchForOffset(index, s.data(), map_offset, key, restart_point));
  154. }
  155. }
  156. TEST(DataBlockHashIndex, DataBlockHashTestLarge) {
  157. DataBlockHashIndexBuilder builder;
  158. builder.Initialize(0.75 /*util_ratio*/);
  159. std::unordered_map<std::string, uint8_t> m;
  160. for (uint8_t i = 0; i < 100; i++) {
  161. if (i % 2) {
  162. continue; // leave half of the keys out
  163. }
  164. std::string key = "key" + std::to_string(i);
  165. uint8_t restart_point = i;
  166. builder.Add(key, restart_point);
  167. m[key] = restart_point;
  168. }
  169. size_t estimated_size = builder.EstimateSize();
  170. std::string buffer("filling stuff"), buffer2;
  171. size_t original_size = buffer.size();
  172. estimated_size += original_size;
  173. builder.Finish(buffer);
  174. ASSERT_EQ(buffer.size(), estimated_size);
  175. buffer2 = buffer; // test for the correctness of relative offset
  176. Slice s(buffer2);
  177. DataBlockHashIndex index;
  178. uint16_t map_offset;
  179. index.Initialize(s.data(), static_cast<uint16_t>(s.size()), &map_offset);
  180. // the additional hash map should start at the end of the buffer
  181. ASSERT_EQ(original_size, map_offset);
  182. for (uint8_t i = 0; i < 100; i++) {
  183. std::string key = "key" + std::to_string(i);
  184. uint8_t restart_point = i;
  185. if (m.count(key)) {
  186. ASSERT_TRUE(m[key] == restart_point);
  187. ASSERT_TRUE(
  188. SearchForOffset(index, s.data(), map_offset, key, restart_point));
  189. } else {
  190. // we allow false positve, so don't test the nonexisting keys.
  191. // when false positive happens, the search will continue to the
  192. // restart intervals to see if the key really exist.
  193. }
  194. }
  195. }
  196. TEST(DataBlockHashIndex, RestartIndexExceedMax) {
  197. DataBlockHashIndexBuilder builder;
  198. builder.Initialize(0.75 /*util_ratio*/);
  199. std::unordered_map<std::string, uint8_t> m;
  200. for (uint8_t i = 0; i <= 253; i++) {
  201. std::string key = "key" + std::to_string(i);
  202. uint8_t restart_point = i;
  203. builder.Add(key, restart_point);
  204. }
  205. ASSERT_TRUE(builder.Valid());
  206. builder.Reset();
  207. for (uint8_t i = 0; i <= 254; i++) {
  208. std::string key = "key" + std::to_string(i);
  209. uint8_t restart_point = i;
  210. builder.Add(key, restart_point);
  211. }
  212. ASSERT_FALSE(builder.Valid());
  213. builder.Reset();
  214. ASSERT_TRUE(builder.Valid());
  215. }
  216. TEST(DataBlockHashIndex, BlockRestartIndexExceedMax) {
  217. Options options = Options();
  218. BlockBuilder builder(1 /* block_restart_interval */,
  219. true /* use_delta_encoding */,
  220. false /* use_value_delta_encoding */,
  221. BlockBasedTableOptions::kDataBlockBinaryAndHash);
  222. // #restarts <= 253. HashIndex is valid
  223. for (int i = 0; i <= 253; i++) {
  224. std::string ukey = "key" + std::to_string(i);
  225. InternalKey ikey(ukey, 0, kTypeValue);
  226. builder.Add(ikey.Encode().ToString(), "value");
  227. }
  228. {
  229. // read serialized contents of the block
  230. Slice rawblock = builder.Finish();
  231. // create block reader
  232. BlockContents contents;
  233. contents.data = rawblock;
  234. Block reader(std::move(contents), kDisableGlobalSequenceNumber);
  235. ASSERT_EQ(reader.IndexType(),
  236. BlockBasedTableOptions::kDataBlockBinaryAndHash);
  237. }
  238. builder.Reset();
  239. // #restarts > 253. HashIndex is not used
  240. for (int i = 0; i <= 254; i++) {
  241. std::string ukey = "key" + std::to_string(i);
  242. InternalKey ikey(ukey, 0, kTypeValue);
  243. builder.Add(ikey.Encode().ToString(), "value");
  244. }
  245. {
  246. // read serialized contents of the block
  247. Slice rawblock = builder.Finish();
  248. // create block reader
  249. BlockContents contents;
  250. contents.data = rawblock;
  251. Block reader(std::move(contents), kDisableGlobalSequenceNumber);
  252. ASSERT_EQ(reader.IndexType(),
  253. BlockBasedTableOptions::kDataBlockBinarySearch);
  254. }
  255. }
  256. TEST(DataBlockHashIndex, BlockSizeExceedMax) {
  257. Options options = Options();
  258. std::string ukey(10, 'k');
  259. InternalKey ikey(ukey, 0, kTypeValue);
  260. BlockBuilder builder(1 /* block_restart_interval */,
  261. false /* use_delta_encoding */,
  262. false /* use_value_delta_encoding */,
  263. BlockBasedTableOptions::kDataBlockBinaryAndHash);
  264. {
  265. // insert a large value. The block size plus HashIndex is 65536.
  266. std::string value(65502, 'v');
  267. builder.Add(ikey.Encode().ToString(), value);
  268. // read serialized contents of the block
  269. Slice rawblock = builder.Finish();
  270. ASSERT_LE(rawblock.size(), kMaxBlockSizeSupportedByHashIndex);
  271. std::cerr << "block size: " << rawblock.size() << std::endl;
  272. // create block reader
  273. BlockContents contents;
  274. contents.data = rawblock;
  275. Block reader(std::move(contents), kDisableGlobalSequenceNumber);
  276. ASSERT_EQ(reader.IndexType(),
  277. BlockBasedTableOptions::kDataBlockBinaryAndHash);
  278. }
  279. builder.Reset();
  280. {
  281. // insert a large value. The block size plus HashIndex would be 65537.
  282. // This excceed the max block size supported by HashIndex (65536).
  283. // So when build finishes HashIndex will not be created for the block.
  284. std::string value(65503, 'v');
  285. builder.Add(ikey.Encode().ToString(), value);
  286. // read serialized contents of the block
  287. Slice rawblock = builder.Finish();
  288. ASSERT_LE(rawblock.size(), kMaxBlockSizeSupportedByHashIndex);
  289. std::cerr << "block size: " << rawblock.size() << std::endl;
  290. // create block reader
  291. BlockContents contents;
  292. contents.data = rawblock;
  293. Block reader(std::move(contents), kDisableGlobalSequenceNumber);
  294. // the index type have fallen back to binary when build finish.
  295. ASSERT_EQ(reader.IndexType(),
  296. BlockBasedTableOptions::kDataBlockBinarySearch);
  297. }
  298. }
  299. TEST(DataBlockHashIndex, BlockTestSingleKey) {
  300. Options options = Options();
  301. BlockBuilder builder(16 /* block_restart_interval */,
  302. true /* use_delta_encoding */,
  303. false /* use_value_delta_encoding */,
  304. BlockBasedTableOptions::kDataBlockBinaryAndHash);
  305. std::string ukey("gopher");
  306. std::string value("gold");
  307. InternalKey ikey(ukey, 10, kTypeValue);
  308. builder.Add(ikey.Encode().ToString(), value /*value*/);
  309. // read serialized contents of the block
  310. Slice rawblock = builder.Finish();
  311. // create block reader
  312. BlockContents contents;
  313. contents.data = rawblock;
  314. Block reader(std::move(contents), kDisableGlobalSequenceNumber);
  315. const InternalKeyComparator icmp(BytewiseComparator());
  316. auto iter = reader.NewDataIterator(&icmp, icmp.user_comparator());
  317. bool may_exist;
  318. // search in block for the key just inserted
  319. {
  320. InternalKey seek_ikey(ukey, 10, kValueTypeForSeek);
  321. may_exist = iter->SeekForGet(seek_ikey.Encode().ToString());
  322. ASSERT_TRUE(may_exist);
  323. ASSERT_TRUE(iter->Valid());
  324. ASSERT_EQ(
  325. options.comparator->Compare(iter->key(), ikey.Encode().ToString()), 0);
  326. ASSERT_EQ(iter->value(), value);
  327. }
  328. // search in block for the existing ukey, but with higher seqno
  329. {
  330. InternalKey seek_ikey(ukey, 20, kValueTypeForSeek);
  331. // HashIndex should be able to set the iter correctly
  332. may_exist = iter->SeekForGet(seek_ikey.Encode().ToString());
  333. ASSERT_TRUE(may_exist);
  334. ASSERT_TRUE(iter->Valid());
  335. // user key should match
  336. ASSERT_EQ(options.comparator->Compare(ExtractUserKey(iter->key()), ukey),
  337. 0);
  338. // seek_key seqno number should be greater than that of iter result
  339. ASSERT_GT(GetInternalKeySeqno(seek_ikey.Encode()),
  340. GetInternalKeySeqno(iter->key()));
  341. ASSERT_EQ(iter->value(), value);
  342. }
  343. // Search in block for the existing ukey, but with lower seqno
  344. // in this case, hash can find the only occurrence of the user_key, but
  345. // ParseNextDataKey() will skip it as it does not have a older seqno.
  346. // In this case, GetForSeek() is effective to locate the user_key, and
  347. // iter->Valid() == false indicates that we've reached to the end of
  348. // the block and the caller should continue searching the next block.
  349. {
  350. InternalKey seek_ikey(ukey, 5, kValueTypeForSeek);
  351. may_exist = iter->SeekForGet(seek_ikey.Encode().ToString());
  352. ASSERT_TRUE(may_exist);
  353. ASSERT_FALSE(iter->Valid()); // should have reached to the end of block
  354. }
  355. delete iter;
  356. }
  357. TEST(DataBlockHashIndex, BlockTestLarge) {
  358. Random rnd(1019);
  359. Options options = Options();
  360. std::vector<std::string> keys;
  361. std::vector<std::string> values;
  362. BlockBuilder builder(16 /* block_restart_interval */,
  363. true /* use_delta_encoding */,
  364. false /* use_value_delta_encoding */,
  365. BlockBasedTableOptions::kDataBlockBinaryAndHash);
  366. int num_records = 500;
  367. GenerateRandomKVs(&keys, &values, 0, num_records);
  368. // Generate keys. Adding a trailing "1" to indicate existent keys.
  369. // Later will Seeking for keys with a trailing "0" to test seeking
  370. // non-existent keys.
  371. for (int i = 0; i < num_records; i++) {
  372. std::string ukey(keys[i] + "1" /* existing key marker */);
  373. InternalKey ikey(ukey, 0, kTypeValue);
  374. builder.Add(ikey.Encode().ToString(), values[i]);
  375. }
  376. // read serialized contents of the block
  377. Slice rawblock = builder.Finish();
  378. // create block reader
  379. BlockContents contents;
  380. contents.data = rawblock;
  381. Block reader(std::move(contents), kDisableGlobalSequenceNumber);
  382. const InternalKeyComparator icmp(BytewiseComparator());
  383. // random seek existent keys
  384. for (int i = 0; i < num_records; i++) {
  385. auto iter = reader.NewDataIterator(&icmp, icmp.user_comparator());
  386. // find a random key in the lookaside array
  387. int index = rnd.Uniform(num_records);
  388. std::string ukey(keys[index] + "1" /* existing key marker */);
  389. InternalKey ikey(ukey, 0, kTypeValue);
  390. // search in block for this key
  391. bool may_exist = iter->SeekForGet(ikey.Encode().ToString());
  392. ASSERT_TRUE(may_exist);
  393. ASSERT_TRUE(iter->Valid());
  394. ASSERT_EQ(values[index], iter->value());
  395. delete iter;
  396. }
  397. // random seek non-existent user keys
  398. // In this case A), the user_key cannot be found in HashIndex. The key may
  399. // exist in the next block. So the iter is set invalidated to tell the
  400. // caller to search the next block. This test case belongs to this case A).
  401. //
  402. // Note that for non-existent keys, there is possibility of false positive,
  403. // i.e. the key is still hashed into some restart interval.
  404. // Two additional possible outcome:
  405. // B) linear seek the restart interval and not found, the iter stops at the
  406. // starting of the next restart interval. The key does not exist
  407. // anywhere.
  408. // C) linear seek the restart interval and not found, the iter stops at the
  409. // the end of the block, i.e. restarts_. The key may exist in the next
  410. // block.
  411. // So these combinations are possible when searching non-existent user_key:
  412. //
  413. // case# may_exist iter->Valid()
  414. // A true false
  415. // B false true
  416. // C true false
  417. for (int i = 0; i < num_records; i++) {
  418. auto iter = reader.NewDataIterator(&icmp, icmp.user_comparator());
  419. // find a random key in the lookaside array
  420. int index = rnd.Uniform(num_records);
  421. std::string ukey(keys[index] + "0" /* non-existing key marker */);
  422. InternalKey ikey(ukey, 0, kTypeValue);
  423. // search in block for this key
  424. bool may_exist = iter->SeekForGet(ikey.Encode().ToString());
  425. if (!may_exist) {
  426. ASSERT_TRUE(iter->Valid());
  427. }
  428. if (!iter->Valid()) {
  429. ASSERT_TRUE(may_exist);
  430. }
  431. delete iter;
  432. }
  433. }
  434. // helper routine for DataBlockHashIndex.BlockBoundary
  435. void TestBoundary(InternalKey& ik1, std::string& v1, InternalKey& ik2,
  436. std::string& v2, InternalKey& seek_ikey,
  437. GetContext& get_context, Options& options) {
  438. std::unique_ptr<WritableFileWriter> file_writer;
  439. std::unique_ptr<RandomAccessFileReader> file_reader;
  440. std::unique_ptr<TableReader> table_reader;
  441. int level_ = -1;
  442. std::vector<std::string> keys;
  443. const ImmutableCFOptions ioptions(options);
  444. const MutableCFOptions moptions(options);
  445. const InternalKeyComparator internal_comparator(options.comparator);
  446. EnvOptions soptions;
  447. soptions.use_mmap_reads = ioptions.allow_mmap_reads;
  448. file_writer.reset(
  449. test::GetWritableFileWriter(new test::StringSink(), "" /* don't care */));
  450. std::unique_ptr<TableBuilder> builder;
  451. std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
  452. int_tbl_prop_collector_factories;
  453. std::string column_family_name;
  454. builder.reset(ioptions.table_factory->NewTableBuilder(
  455. TableBuilderOptions(ioptions, moptions, internal_comparator,
  456. &int_tbl_prop_collector_factories,
  457. options.compression, options.sample_for_compression,
  458. CompressionOptions(), false /* skip_filters */,
  459. column_family_name, level_),
  460. TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
  461. file_writer.get()));
  462. builder->Add(ik1.Encode().ToString(), v1);
  463. builder->Add(ik2.Encode().ToString(), v2);
  464. EXPECT_TRUE(builder->status().ok());
  465. Status s = builder->Finish();
  466. file_writer->Flush();
  467. EXPECT_TRUE(s.ok()) << s.ToString();
  468. EXPECT_EQ(
  469. test::GetStringSinkFromLegacyWriter(file_writer.get())->contents().size(),
  470. builder->FileSize());
  471. // Open the table
  472. file_reader.reset(test::GetRandomAccessFileReader(new test::StringSource(
  473. test::GetStringSinkFromLegacyWriter(file_writer.get())->contents(),
  474. 0 /*uniq_id*/, ioptions.allow_mmap_reads)));
  475. const bool kSkipFilters = true;
  476. const bool kImmortal = true;
  477. ioptions.table_factory->NewTableReader(
  478. TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
  479. internal_comparator, !kSkipFilters, !kImmortal,
  480. level_),
  481. std::move(file_reader),
  482. test::GetStringSinkFromLegacyWriter(file_writer.get())->contents().size(),
  483. &table_reader);
  484. // Search using Get()
  485. ReadOptions ro;
  486. ASSERT_OK(table_reader->Get(ro, seek_ikey.Encode().ToString(), &get_context,
  487. moptions.prefix_extractor.get()));
  488. }
  489. TEST(DataBlockHashIndex, BlockBoundary) {
  490. BlockBasedTableOptions table_options;
  491. table_options.data_block_index_type =
  492. BlockBasedTableOptions::kDataBlockBinaryAndHash;
  493. table_options.block_restart_interval = 1;
  494. table_options.block_size = 4096;
  495. Options options;
  496. options.comparator = BytewiseComparator();
  497. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  498. // insert two large k/v pair. Given that the block_size is 4096, one k/v
  499. // pair will take up one block.
  500. // [ k1/v1 ][ k2/v2 ]
  501. // [ Block N ][ Block N+1 ]
  502. {
  503. // [ "aab"@100 ][ "axy"@10 ]
  504. // | Block N ][ Block N+1 ]
  505. // seek for "axy"@60
  506. std::string uk1("aab");
  507. InternalKey ik1(uk1, 100, kTypeValue);
  508. std::string v1(4100, '1'); // large value
  509. std::string uk2("axy");
  510. InternalKey ik2(uk2, 10, kTypeValue);
  511. std::string v2(4100, '2'); // large value
  512. PinnableSlice value;
  513. std::string seek_ukey("axy");
  514. InternalKey seek_ikey(seek_ukey, 60, kTypeValue);
  515. GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
  516. GetContext::kNotFound, seek_ukey, &value, nullptr,
  517. nullptr, true, nullptr, nullptr);
  518. TestBoundary(ik1, v1, ik2, v2, seek_ikey, get_context, options);
  519. ASSERT_EQ(get_context.State(), GetContext::kFound);
  520. ASSERT_EQ(value, v2);
  521. value.Reset();
  522. }
  523. {
  524. // [ "axy"@100 ][ "axy"@10 ]
  525. // | Block N ][ Block N+1 ]
  526. // seek for "axy"@60
  527. std::string uk1("axy");
  528. InternalKey ik1(uk1, 100, kTypeValue);
  529. std::string v1(4100, '1'); // large value
  530. std::string uk2("axy");
  531. InternalKey ik2(uk2, 10, kTypeValue);
  532. std::string v2(4100, '2'); // large value
  533. PinnableSlice value;
  534. std::string seek_ukey("axy");
  535. InternalKey seek_ikey(seek_ukey, 60, kTypeValue);
  536. GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
  537. GetContext::kNotFound, seek_ukey, &value, nullptr,
  538. nullptr, true, nullptr, nullptr);
  539. TestBoundary(ik1, v1, ik2, v2, seek_ikey, get_context, options);
  540. ASSERT_EQ(get_context.State(), GetContext::kFound);
  541. ASSERT_EQ(value, v2);
  542. value.Reset();
  543. }
  544. {
  545. // [ "axy"@100 ][ "axy"@10 ]
  546. // | Block N ][ Block N+1 ]
  547. // seek for "axy"@120
  548. std::string uk1("axy");
  549. InternalKey ik1(uk1, 100, kTypeValue);
  550. std::string v1(4100, '1'); // large value
  551. std::string uk2("axy");
  552. InternalKey ik2(uk2, 10, kTypeValue);
  553. std::string v2(4100, '2'); // large value
  554. PinnableSlice value;
  555. std::string seek_ukey("axy");
  556. InternalKey seek_ikey(seek_ukey, 120, kTypeValue);
  557. GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
  558. GetContext::kNotFound, seek_ukey, &value, nullptr,
  559. nullptr, true, nullptr, nullptr);
  560. TestBoundary(ik1, v1, ik2, v2, seek_ikey, get_context, options);
  561. ASSERT_EQ(get_context.State(), GetContext::kFound);
  562. ASSERT_EQ(value, v1);
  563. value.Reset();
  564. }
  565. {
  566. // [ "axy"@100 ][ "axy"@10 ]
  567. // | Block N ][ Block N+1 ]
  568. // seek for "axy"@5
  569. std::string uk1("axy");
  570. InternalKey ik1(uk1, 100, kTypeValue);
  571. std::string v1(4100, '1'); // large value
  572. std::string uk2("axy");
  573. InternalKey ik2(uk2, 10, kTypeValue);
  574. std::string v2(4100, '2'); // large value
  575. PinnableSlice value;
  576. std::string seek_ukey("axy");
  577. InternalKey seek_ikey(seek_ukey, 5, kTypeValue);
  578. GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
  579. GetContext::kNotFound, seek_ukey, &value, nullptr,
  580. nullptr, true, nullptr, nullptr);
  581. TestBoundary(ik1, v1, ik2, v2, seek_ikey, get_context, options);
  582. ASSERT_EQ(get_context.State(), GetContext::kNotFound);
  583. value.Reset();
  584. }
  585. }
  586. } // namespace ROCKSDB_NAMESPACE
  587. int main(int argc, char** argv) {
  588. ::testing::InitGoogleTest(&argc, argv);
  589. return RUN_ALL_TESTS();
  590. }