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 ¤t_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