data_block_hash_index_test.cc 24 KB

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