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 
7 #include <stdio.h>
8 #include <algorithm>
9 #include <set>
10 #include <string>
11 #include <unordered_set>
12 #include <utility>
13 #include <vector>
14 
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.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 
30 namespace ROCKSDB_NAMESPACE {
31 
RandomString(Random * rnd,int len)32 static std::string RandomString(Random *rnd, int len) {
33   std::string r;
34   test::RandomString(rnd, len, &r);
35   return r;
36 }
GenerateKey(int primary_key,int secondary_key,int padding_size,Random * rnd)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 
47   return k;
48 }
49 
50 // Generate random key value pairs.
51 // The generated key will be sorted. You can tune the parameters to generated
52 // different kinds of test key/value pairs for different scenario.
GenerateRandomKVs(std::vector<std::string> * keys,std::vector<std::string> * values,const int from,const int len,const int step=1,const int padding_size=0,const int keys_share_prefix=1)53 void GenerateRandomKVs(std::vector<std::string> *keys,
54                        std::vector<std::string> *values, const int from,
55                        const int len, const int step = 1,
56                        const int padding_size = 0,
57                        const int keys_share_prefix = 1) {
58   Random rnd(302);
59 
60   // generate different prefix
61   for (int i = from; i < from + len; i += step) {
62     // generating keys that shares the prefix
63     for (int j = 0; j < keys_share_prefix; ++j) {
64       keys->emplace_back(GenerateKey(i, j, padding_size, &rnd));
65 
66       // 100 bytes values
67       values->emplace_back(RandomString(&rnd, 100));
68     }
69   }
70 }
71 
72 class BlockTest : public testing::Test {};
73 
74 // block test
TEST_F(BlockTest,SimpleTest)75 TEST_F(BlockTest, SimpleTest) {
76   Random rnd(301);
77   Options options = Options();
78 
79   std::vector<std::string> keys;
80   std::vector<std::string> values;
81   BlockBuilder builder(16);
82   int num_records = 100000;
83 
84   GenerateRandomKVs(&keys, &values, 0, num_records);
85   // add a bunch of records to a block
86   for (int i = 0; i < num_records; i++) {
87     builder.Add(keys[i], values[i]);
88   }
89 
90   // read serialized contents of the block
91   Slice rawblock = builder.Finish();
92 
93   // create block reader
94   BlockContents contents;
95   contents.data = rawblock;
96   Block reader(std::move(contents));
97 
98   // read contents of block sequentially
99   int count = 0;
100   InternalIterator *iter = reader.NewDataIterator(
101       options.comparator, options.comparator, kDisableGlobalSequenceNumber);
102   for (iter->SeekToFirst(); iter->Valid(); count++, iter->Next()) {
103     // read kv from block
104     Slice k = iter->key();
105     Slice v = iter->value();
106 
107     // compare with lookaside array
108     ASSERT_EQ(k.ToString().compare(keys[count]), 0);
109     ASSERT_EQ(v.ToString().compare(values[count]), 0);
110   }
111   delete iter;
112 
113   // read block contents randomly
114   iter = reader.NewDataIterator(options.comparator, options.comparator,
115                                 kDisableGlobalSequenceNumber);
116   for (int i = 0; i < num_records; i++) {
117     // find a random key in the lookaside array
118     int index = rnd.Uniform(num_records);
119     Slice k(keys[index]);
120 
121     // search in block for this key
122     iter->Seek(k);
123     ASSERT_TRUE(iter->Valid());
124     Slice v = iter->value();
125     ASSERT_EQ(v.ToString().compare(values[index]), 0);
126   }
127   delete iter;
128 }
129 
130 // return the block contents
GetBlockContents(std::unique_ptr<BlockBuilder> * builder,const std::vector<std::string> & keys,const std::vector<std::string> & values,const int=1)131 BlockContents GetBlockContents(std::unique_ptr<BlockBuilder> *builder,
132                                const std::vector<std::string> &keys,
133                                const std::vector<std::string> &values,
134                                const int /*prefix_group_size*/ = 1) {
135   builder->reset(new BlockBuilder(1 /* restart interval */));
136 
137   // Add only half of the keys
138   for (size_t i = 0; i < keys.size(); ++i) {
139     (*builder)->Add(keys[i], values[i]);
140   }
141   Slice rawblock = (*builder)->Finish();
142 
143   BlockContents contents;
144   contents.data = rawblock;
145 
146   return contents;
147 }
148 
CheckBlockContents(BlockContents contents,const int max_key,const std::vector<std::string> & keys,const std::vector<std::string> & values)149 void CheckBlockContents(BlockContents contents, const int max_key,
150                         const std::vector<std::string> &keys,
151                         const std::vector<std::string> &values) {
152   const size_t prefix_size = 6;
153   // create block reader
154   BlockContents contents_ref(contents.data);
155   Block reader1(std::move(contents));
156   Block reader2(std::move(contents_ref));
157 
158   std::unique_ptr<const SliceTransform> prefix_extractor(
159       NewFixedPrefixTransform(prefix_size));
160 
161   std::unique_ptr<InternalIterator> regular_iter(
162       reader2.NewDataIterator(BytewiseComparator(), BytewiseComparator(),
163                               kDisableGlobalSequenceNumber));
164 
165   // Seek existent keys
166   for (size_t i = 0; i < keys.size(); i++) {
167     regular_iter->Seek(keys[i]);
168     ASSERT_OK(regular_iter->status());
169     ASSERT_TRUE(regular_iter->Valid());
170 
171     Slice v = regular_iter->value();
172     ASSERT_EQ(v.ToString().compare(values[i]), 0);
173   }
174 
175   // Seek non-existent keys.
176   // For hash index, if no key with a given prefix is not found, iterator will
177   // simply be set as invalid; whereas the binary search based iterator will
178   // return the one that is closest.
179   for (int i = 1; i < max_key - 1; i += 2) {
180     auto key = GenerateKey(i, 0, 0, nullptr);
181     regular_iter->Seek(key);
182     ASSERT_TRUE(regular_iter->Valid());
183   }
184 }
185 
186 // In this test case, no two key share same prefix.
TEST_F(BlockTest,SimpleIndexHash)187 TEST_F(BlockTest, SimpleIndexHash) {
188   const int kMaxKey = 100000;
189   std::vector<std::string> keys;
190   std::vector<std::string> values;
191   GenerateRandomKVs(&keys, &values, 0 /* first key id */,
192                     kMaxKey /* last key id */, 2 /* step */,
193                     8 /* padding size (8 bytes randomly generated suffix) */);
194 
195   std::unique_ptr<BlockBuilder> builder;
196   auto contents = GetBlockContents(&builder, keys, values);
197 
198   CheckBlockContents(std::move(contents), kMaxKey, keys, values);
199 }
200 
TEST_F(BlockTest,IndexHashWithSharedPrefix)201 TEST_F(BlockTest, IndexHashWithSharedPrefix) {
202   const int kMaxKey = 100000;
203   // for each prefix, there will be 5 keys starts with it.
204   const int kPrefixGroup = 5;
205   std::vector<std::string> keys;
206   std::vector<std::string> values;
207   // Generate keys with same prefix.
208   GenerateRandomKVs(&keys, &values, 0,  // first key id
209                     kMaxKey,            // last key id
210                     2,                  // step
211                     10,                 // padding size,
212                     kPrefixGroup);
213 
214   std::unique_ptr<BlockBuilder> builder;
215   auto contents = GetBlockContents(&builder, keys, values, kPrefixGroup);
216 
217   CheckBlockContents(std::move(contents), kMaxKey, keys, values);
218 }
219 
220 // A slow and accurate version of BlockReadAmpBitmap that simply store
221 // all the marked ranges in a set.
222 class BlockReadAmpBitmapSlowAndAccurate {
223  public:
Mark(size_t start_offset,size_t end_offset)224   void Mark(size_t start_offset, size_t end_offset) {
225     assert(end_offset >= start_offset);
226     marked_ranges_.emplace(end_offset, start_offset);
227   }
228 
ResetCheckSequence()229   void ResetCheckSequence() { iter_valid_ = false; }
230 
231   // Return true if any byte in this range was Marked
232   // This does linear search from the previous position. When calling
233   // multiple times, `offset` needs to be incremental to get correct results.
234   // Call ResetCheckSequence() to reset it.
IsPinMarked(size_t offset)235   bool IsPinMarked(size_t offset) {
236     if (iter_valid_) {
237       // Has existing iterator, try linear search from
238       // the iterator.
239       for (int i = 0; i < 64; i++) {
240         if (offset < iter_->second) {
241           return false;
242         }
243         if (offset <= iter_->first) {
244           return true;
245         }
246 
247         iter_++;
248         if (iter_ == marked_ranges_.end()) {
249           iter_valid_ = false;
250           return false;
251         }
252       }
253     }
254     // Initial call or have linear searched too many times.
255     // Do binary search.
256     iter_ = marked_ranges_.lower_bound(
257         std::make_pair(offset, static_cast<size_t>(0)));
258     if (iter_ == marked_ranges_.end()) {
259       iter_valid_ = false;
260       return false;
261     }
262     iter_valid_ = true;
263     return offset <= iter_->first && offset >= iter_->second;
264   }
265 
266  private:
267   std::set<std::pair<size_t, size_t>> marked_ranges_;
268   std::set<std::pair<size_t, size_t>>::iterator iter_;
269   bool iter_valid_ = false;
270 };
271 
TEST_F(BlockTest,BlockReadAmpBitmap)272 TEST_F(BlockTest, BlockReadAmpBitmap) {
273   uint32_t pin_offset = 0;
274   SyncPoint::GetInstance()->SetCallBack(
275       "BlockReadAmpBitmap:rnd", [&pin_offset](void *arg) {
276         pin_offset = *(static_cast<uint32_t *>(arg));
277       });
278   SyncPoint::GetInstance()->EnableProcessing();
279   std::vector<size_t> block_sizes = {
280       1,                // 1 byte
281       32,               // 32 bytes
282       61,               // 61 bytes
283       64,               // 64 bytes
284       512,              // 0.5 KB
285       1024,             // 1 KB
286       1024 * 4,         // 4 KB
287       1024 * 10,        // 10 KB
288       1024 * 50,        // 50 KB
289       1024 * 1024 * 4,  // 5 MB
290       777,
291       124653,
292   };
293   const size_t kBytesPerBit = 64;
294 
295   Random rnd(301);
296   for (size_t block_size : block_sizes) {
297     std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
298     BlockReadAmpBitmap read_amp_bitmap(block_size, kBytesPerBit, stats.get());
299     BlockReadAmpBitmapSlowAndAccurate read_amp_slow_and_accurate;
300 
301     size_t needed_bits = (block_size / kBytesPerBit);
302     if (block_size % kBytesPerBit != 0) {
303       needed_bits++;
304     }
305 
306     ASSERT_EQ(stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES), block_size);
307 
308     // Generate some random entries
309     std::vector<size_t> random_entry_offsets;
310     for (int i = 0; i < 1000; i++) {
311       random_entry_offsets.push_back(rnd.Next() % block_size);
312     }
313     std::sort(random_entry_offsets.begin(), random_entry_offsets.end());
314     auto it =
315         std::unique(random_entry_offsets.begin(), random_entry_offsets.end());
316     random_entry_offsets.resize(
317         std::distance(random_entry_offsets.begin(), it));
318 
319     std::vector<std::pair<size_t, size_t>> random_entries;
320     for (size_t i = 0; i < random_entry_offsets.size(); i++) {
321       size_t entry_start = random_entry_offsets[i];
322       size_t entry_end;
323       if (i + 1 < random_entry_offsets.size()) {
324         entry_end = random_entry_offsets[i + 1] - 1;
325       } else {
326         entry_end = block_size - 1;
327       }
328       random_entries.emplace_back(entry_start, entry_end);
329     }
330 
331     for (size_t i = 0; i < random_entries.size(); i++) {
332       read_amp_slow_and_accurate.ResetCheckSequence();
333       auto &current_entry = random_entries[rnd.Next() % random_entries.size()];
334 
335       read_amp_bitmap.Mark(static_cast<uint32_t>(current_entry.first),
336                            static_cast<uint32_t>(current_entry.second));
337       read_amp_slow_and_accurate.Mark(current_entry.first,
338                                       current_entry.second);
339 
340       size_t total_bits = 0;
341       for (size_t bit_idx = 0; bit_idx < needed_bits; bit_idx++) {
342         total_bits += read_amp_slow_and_accurate.IsPinMarked(
343             bit_idx * kBytesPerBit + pin_offset);
344       }
345       size_t expected_estimate_useful = total_bits * kBytesPerBit;
346       size_t got_estimate_useful =
347           stats->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
348       ASSERT_EQ(expected_estimate_useful, got_estimate_useful);
349     }
350   }
351   SyncPoint::GetInstance()->DisableProcessing();
352   SyncPoint::GetInstance()->ClearAllCallBacks();
353 }
354 
TEST_F(BlockTest,BlockWithReadAmpBitmap)355 TEST_F(BlockTest, BlockWithReadAmpBitmap) {
356   Random rnd(301);
357   Options options = Options();
358 
359   std::vector<std::string> keys;
360   std::vector<std::string> values;
361   BlockBuilder builder(16);
362   int num_records = 10000;
363 
364   GenerateRandomKVs(&keys, &values, 0, num_records, 1);
365   // add a bunch of records to a block
366   for (int i = 0; i < num_records; i++) {
367     builder.Add(keys[i], values[i]);
368   }
369 
370   Slice rawblock = builder.Finish();
371   const size_t kBytesPerBit = 8;
372 
373   // Read the block sequentially using Next()
374   {
375     std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
376 
377     // create block reader
378     BlockContents contents;
379     contents.data = rawblock;
380     Block reader(std::move(contents), kBytesPerBit, stats.get());
381 
382     // read contents of block sequentially
383     size_t read_bytes = 0;
384     DataBlockIter *iter = reader.NewDataIterator(
385         options.comparator, options.comparator, kDisableGlobalSequenceNumber,
386         nullptr, stats.get());
387     for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
388       iter->value();
389       read_bytes += iter->TEST_CurrentEntrySize();
390 
391       double semi_acc_read_amp =
392           static_cast<double>(read_bytes) / rawblock.size();
393       double read_amp = static_cast<double>(stats->getTickerCount(
394                             READ_AMP_ESTIMATE_USEFUL_BYTES)) /
395                         stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
396 
397       // Error in read amplification will be less than 1% if we are reading
398       // sequentially
399       double error_pct = fabs(semi_acc_read_amp - read_amp) * 100;
400       EXPECT_LT(error_pct, 1);
401     }
402 
403     delete iter;
404   }
405 
406   // Read the block sequentially using Seek()
407   {
408     std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
409 
410     // create block reader
411     BlockContents contents;
412     contents.data = rawblock;
413     Block reader(std::move(contents), kBytesPerBit, stats.get());
414 
415     size_t read_bytes = 0;
416     DataBlockIter *iter = reader.NewDataIterator(
417         options.comparator, options.comparator, kDisableGlobalSequenceNumber,
418         nullptr, stats.get());
419     for (int i = 0; i < num_records; i++) {
420       Slice k(keys[i]);
421 
422       // search in block for this key
423       iter->Seek(k);
424       iter->value();
425       read_bytes += iter->TEST_CurrentEntrySize();
426 
427       double semi_acc_read_amp =
428           static_cast<double>(read_bytes) / rawblock.size();
429       double read_amp = static_cast<double>(stats->getTickerCount(
430                             READ_AMP_ESTIMATE_USEFUL_BYTES)) /
431                         stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
432 
433       // Error in read amplification will be less than 1% if we are reading
434       // sequentially
435       double error_pct = fabs(semi_acc_read_amp - read_amp) * 100;
436       EXPECT_LT(error_pct, 1);
437     }
438     delete iter;
439   }
440 
441   // Read the block randomly
442   {
443     std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
444 
445     // create block reader
446     BlockContents contents;
447     contents.data = rawblock;
448     Block reader(std::move(contents), kBytesPerBit, stats.get());
449 
450     size_t read_bytes = 0;
451     DataBlockIter *iter = reader.NewDataIterator(
452         options.comparator, options.comparator, kDisableGlobalSequenceNumber,
453         nullptr, stats.get());
454     std::unordered_set<int> read_keys;
455     for (int i = 0; i < num_records; i++) {
456       int index = rnd.Uniform(num_records);
457       Slice k(keys[index]);
458 
459       iter->Seek(k);
460       iter->value();
461       if (read_keys.find(index) == read_keys.end()) {
462         read_keys.insert(index);
463         read_bytes += iter->TEST_CurrentEntrySize();
464       }
465 
466       double semi_acc_read_amp =
467           static_cast<double>(read_bytes) / rawblock.size();
468       double read_amp = static_cast<double>(stats->getTickerCount(
469                             READ_AMP_ESTIMATE_USEFUL_BYTES)) /
470                         stats->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
471 
472       double error_pct = fabs(semi_acc_read_amp - read_amp) * 100;
473       // Error in read amplification will be less than 2% if we are reading
474       // randomly
475       EXPECT_LT(error_pct, 2);
476     }
477     delete iter;
478   }
479 }
480 
TEST_F(BlockTest,ReadAmpBitmapPow2)481 TEST_F(BlockTest, ReadAmpBitmapPow2) {
482   std::shared_ptr<Statistics> stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
483   ASSERT_EQ(BlockReadAmpBitmap(100, 1, stats.get()).GetBytesPerBit(), 1u);
484   ASSERT_EQ(BlockReadAmpBitmap(100, 2, stats.get()).GetBytesPerBit(), 2u);
485   ASSERT_EQ(BlockReadAmpBitmap(100, 4, stats.get()).GetBytesPerBit(), 4u);
486   ASSERT_EQ(BlockReadAmpBitmap(100, 8, stats.get()).GetBytesPerBit(), 8u);
487   ASSERT_EQ(BlockReadAmpBitmap(100, 16, stats.get()).GetBytesPerBit(), 16u);
488   ASSERT_EQ(BlockReadAmpBitmap(100, 32, stats.get()).GetBytesPerBit(), 32u);
489 
490   ASSERT_EQ(BlockReadAmpBitmap(100, 3, stats.get()).GetBytesPerBit(), 2u);
491   ASSERT_EQ(BlockReadAmpBitmap(100, 7, stats.get()).GetBytesPerBit(), 4u);
492   ASSERT_EQ(BlockReadAmpBitmap(100, 11, stats.get()).GetBytesPerBit(), 8u);
493   ASSERT_EQ(BlockReadAmpBitmap(100, 17, stats.get()).GetBytesPerBit(), 16u);
494   ASSERT_EQ(BlockReadAmpBitmap(100, 33, stats.get()).GetBytesPerBit(), 32u);
495   ASSERT_EQ(BlockReadAmpBitmap(100, 35, stats.get()).GetBytesPerBit(), 32u);
496 }
497 
498 class IndexBlockTest
499     : public testing::Test,
500       public testing::WithParamInterface<std::tuple<bool, bool>> {
501  public:
502   IndexBlockTest() = default;
503 
useValueDeltaEncoding() const504   bool useValueDeltaEncoding() const { return std::get<0>(GetParam()); }
includeFirstKey() const505   bool includeFirstKey() const { return std::get<1>(GetParam()); }
506 };
507 
508 // Similar to GenerateRandomKVs but for index block contents.
GenerateRandomIndexEntries(std::vector<std::string> * separators,std::vector<BlockHandle> * block_handles,std::vector<std::string> * first_keys,const int len)509 void GenerateRandomIndexEntries(std::vector<std::string> *separators,
510                                 std::vector<BlockHandle> *block_handles,
511                                 std::vector<std::string> *first_keys,
512                                 const int len) {
513   Random rnd(42);
514 
515   // For each of `len` blocks, we need to generate a first and last key.
516   // Let's generate n*2 random keys, sort them, group into consecutive pairs.
517   std::set<std::string> keys;
518   while ((int)keys.size() < len * 2) {
519     // Keys need to be at least 8 bytes long to look like internal keys.
520     keys.insert(test::RandomKey(&rnd, 12));
521   }
522 
523   uint64_t offset = 0;
524   for (auto it = keys.begin(); it != keys.end();) {
525     first_keys->emplace_back(*it++);
526     separators->emplace_back(*it++);
527     uint64_t size = rnd.Uniform(1024 * 16);
528     BlockHandle handle(offset, size);
529     offset += size + kBlockTrailerSize;
530     block_handles->emplace_back(handle);
531   }
532 }
533 
TEST_P(IndexBlockTest,IndexValueEncodingTest)534 TEST_P(IndexBlockTest, IndexValueEncodingTest) {
535   Random rnd(301);
536   Options options = Options();
537 
538   std::vector<std::string> separators;
539   std::vector<BlockHandle> block_handles;
540   std::vector<std::string> first_keys;
541   const bool kUseDeltaEncoding = true;
542   BlockBuilder builder(16, kUseDeltaEncoding, useValueDeltaEncoding());
543   int num_records = 100;
544 
545   GenerateRandomIndexEntries(&separators, &block_handles, &first_keys,
546                              num_records);
547   BlockHandle last_encoded_handle;
548   for (int i = 0; i < num_records; i++) {
549     IndexValue entry(block_handles[i], first_keys[i]);
550     std::string encoded_entry;
551     std::string delta_encoded_entry;
552     entry.EncodeTo(&encoded_entry, includeFirstKey(), nullptr);
553     if (useValueDeltaEncoding() && i > 0) {
554       entry.EncodeTo(&delta_encoded_entry, includeFirstKey(),
555                      &last_encoded_handle);
556     }
557     last_encoded_handle = entry.handle;
558     const Slice delta_encoded_entry_slice(delta_encoded_entry);
559     builder.Add(separators[i], encoded_entry, &delta_encoded_entry_slice);
560   }
561 
562   // read serialized contents of the block
563   Slice rawblock = builder.Finish();
564 
565   // create block reader
566   BlockContents contents;
567   contents.data = rawblock;
568   Block reader(std::move(contents));
569 
570   const bool kTotalOrderSeek = true;
571   const bool kIncludesSeq = true;
572   const bool kValueIsFull = !useValueDeltaEncoding();
573   IndexBlockIter *kNullIter = nullptr;
574   Statistics *kNullStats = nullptr;
575   // read contents of block sequentially
576   InternalIteratorBase<IndexValue> *iter = reader.NewIndexIterator(
577       options.comparator, options.comparator, kDisableGlobalSequenceNumber,
578       kNullIter, kNullStats, kTotalOrderSeek, includeFirstKey(), kIncludesSeq,
579       kValueIsFull);
580   iter->SeekToFirst();
581   for (int index = 0; index < num_records; ++index) {
582     ASSERT_TRUE(iter->Valid());
583 
584     Slice k = iter->key();
585     IndexValue v = iter->value();
586 
587     EXPECT_EQ(separators[index], k.ToString());
588     EXPECT_EQ(block_handles[index].offset(), v.handle.offset());
589     EXPECT_EQ(block_handles[index].size(), v.handle.size());
590     EXPECT_EQ(includeFirstKey() ? first_keys[index] : "",
591               v.first_internal_key.ToString());
592 
593     iter->Next();
594   }
595   delete iter;
596 
597   // read block contents randomly
598   iter = reader.NewIndexIterator(options.comparator, options.comparator,
599                                  kDisableGlobalSequenceNumber, kNullIter,
600                                  kNullStats, kTotalOrderSeek, includeFirstKey(),
601                                  kIncludesSeq, kValueIsFull);
602   for (int i = 0; i < num_records * 2; i++) {
603     // find a random key in the lookaside array
604     int index = rnd.Uniform(num_records);
605     Slice k(separators[index]);
606 
607     // search in block for this key
608     iter->Seek(k);
609     ASSERT_TRUE(iter->Valid());
610     IndexValue v = iter->value();
611     EXPECT_EQ(separators[index], iter->key().ToString());
612     EXPECT_EQ(block_handles[index].offset(), v.handle.offset());
613     EXPECT_EQ(block_handles[index].size(), v.handle.size());
614     EXPECT_EQ(includeFirstKey() ? first_keys[index] : "",
615               v.first_internal_key.ToString());
616   }
617   delete iter;
618 }
619 
620 INSTANTIATE_TEST_CASE_P(P, IndexBlockTest,
621                         ::testing::Values(std::make_tuple(false, false),
622                                           std::make_tuple(false, true),
623                                           std::make_tuple(true, false),
624                                           std::make_tuple(true, true)));
625 
626 }  // namespace ROCKSDB_NAMESPACE
627 
main(int argc,char ** argv)628 int main(int argc, char **argv) {
629   ::testing::InitGoogleTest(&argc, argv);
630   return RUN_ALL_TESTS();
631 }
632