]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/table/table_test.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / table / table_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
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).
7c673cae
FG
5//
6// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7// Use of this source code is governed by a BSD-style license that can be
8// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
7c673cae 10#include <stdio.h>
7c673cae
FG
11#include <algorithm>
12#include <iostream>
13#include <map>
14#include <memory>
15#include <string>
16#include <vector>
17
f67539c2 18#include "block_fetcher.h"
11fdf7f2 19#include "cache/lru_cache.h"
7c673cae
FG
20#include "db/dbformat.h"
21#include "db/memtable.h"
22#include "db/write_batch_internal.h"
23#include "memtable/stl_wrappers.h"
f67539c2 24#include "meta_blocks.h"
7c673cae
FG
25#include "monitoring/statistics.h"
26#include "port/port.h"
27#include "rocksdb/cache.h"
28#include "rocksdb/db.h"
29#include "rocksdb/env.h"
f67539c2
TL
30#include "rocksdb/file_checksum.h"
31#include "rocksdb/file_system.h"
7c673cae
FG
32#include "rocksdb/iterator.h"
33#include "rocksdb/memtablerep.h"
34#include "rocksdb/perf_context.h"
35#include "rocksdb/slice_transform.h"
36#include "rocksdb/statistics.h"
37#include "rocksdb/write_buffer_manager.h"
f67539c2
TL
38#include "table/block_based/block.h"
39#include "table/block_based/block_based_table_builder.h"
40#include "table/block_based/block_based_table_factory.h"
41#include "table/block_based/block_based_table_reader.h"
42#include "table/block_based/block_builder.h"
43#include "table/block_based/flush_block_policy.h"
7c673cae
FG
44#include "table/format.h"
45#include "table/get_context.h"
46#include "table/internal_iterator.h"
f67539c2 47#include "table/plain/plain_table_factory.h"
7c673cae
FG
48#include "table/scoped_arena_iterator.h"
49#include "table/sst_file_writer_collectors.h"
f67539c2
TL
50#include "test_util/sync_point.h"
51#include "test_util/testharness.h"
52#include "test_util/testutil.h"
7c673cae 53#include "util/compression.h"
f67539c2 54#include "util/file_checksum_helper.h"
7c673cae
FG
55#include "util/random.h"
56#include "util/string_util.h"
7c673cae
FG
57#include "utilities/merge_operators.h"
58
f67539c2 59namespace ROCKSDB_NAMESPACE {
7c673cae
FG
60
61extern const uint64_t kLegacyBlockBasedTableMagicNumber;
62extern const uint64_t kLegacyPlainTableMagicNumber;
63extern const uint64_t kBlockBasedTableMagicNumber;
64extern const uint64_t kPlainTableMagicNumber;
65
66namespace {
67
f67539c2
TL
68const std::string kDummyValue(10000, 'o');
69
7c673cae
FG
70// DummyPropertiesCollector used to test BlockBasedTableProperties
71class DummyPropertiesCollector : public TablePropertiesCollector {
72 public:
20effc67 73 const char* Name() const override { return "DummyPropertiesCollector"; }
7c673cae 74
494da23a 75 Status Finish(UserCollectedProperties* /*properties*/) override {
11fdf7f2
TL
76 return Status::OK();
77 }
7c673cae 78
494da23a 79 Status Add(const Slice& /*user_key*/, const Slice& /*value*/) override {
11fdf7f2
TL
80 return Status::OK();
81 }
7c673cae 82
494da23a 83 UserCollectedProperties GetReadableProperties() const override {
7c673cae
FG
84 return UserCollectedProperties{};
85 }
86};
87
88class DummyPropertiesCollectorFactory1
89 : public TablePropertiesCollectorFactory {
90 public:
494da23a
TL
91 TablePropertiesCollector* CreateTablePropertiesCollector(
92 TablePropertiesCollectorFactory::Context /*context*/) override {
7c673cae
FG
93 return new DummyPropertiesCollector();
94 }
20effc67
TL
95 const char* Name() const override {
96 return "DummyPropertiesCollectorFactory1";
97 }
7c673cae
FG
98};
99
100class DummyPropertiesCollectorFactory2
101 : public TablePropertiesCollectorFactory {
102 public:
494da23a
TL
103 TablePropertiesCollector* CreateTablePropertiesCollector(
104 TablePropertiesCollectorFactory::Context /*context*/) override {
7c673cae
FG
105 return new DummyPropertiesCollector();
106 }
20effc67
TL
107 const char* Name() const override {
108 return "DummyPropertiesCollectorFactory2";
109 }
7c673cae
FG
110};
111
112// Return reverse of "key".
113// Used to test non-lexicographic comparators.
114std::string Reverse(const Slice& key) {
115 auto rev = key.ToString();
116 std::reverse(rev.begin(), rev.end());
117 return rev;
118}
119
120class ReverseKeyComparator : public Comparator {
121 public:
494da23a 122 const char* Name() const override {
7c673cae
FG
123 return "rocksdb.ReverseBytewiseComparator";
124 }
125
494da23a 126 int Compare(const Slice& a, const Slice& b) const override {
7c673cae
FG
127 return BytewiseComparator()->Compare(Reverse(a), Reverse(b));
128 }
129
494da23a
TL
130 void FindShortestSeparator(std::string* start,
131 const Slice& limit) const override {
7c673cae
FG
132 std::string s = Reverse(*start);
133 std::string l = Reverse(limit);
134 BytewiseComparator()->FindShortestSeparator(&s, l);
135 *start = Reverse(s);
136 }
137
494da23a 138 void FindShortSuccessor(std::string* key) const override {
7c673cae
FG
139 std::string s = Reverse(*key);
140 BytewiseComparator()->FindShortSuccessor(&s);
141 *key = Reverse(s);
142 }
143};
144
145ReverseKeyComparator reverse_key_comparator;
146
147void Increment(const Comparator* cmp, std::string* key) {
148 if (cmp == BytewiseComparator()) {
149 key->push_back('\0');
150 } else {
151 assert(cmp == &reverse_key_comparator);
152 std::string rev = Reverse(*key);
153 rev.push_back('\0');
154 *key = Reverse(rev);
155 }
156}
157
158} // namespace
159
160// Helper class for tests to unify the interface between
161// BlockBuilder/TableBuilder and Block/Table.
162class Constructor {
163 public:
164 explicit Constructor(const Comparator* cmp)
165 : data_(stl_wrappers::LessOfComparator(cmp)) {}
166 virtual ~Constructor() { }
167
168 void Add(const std::string& key, const Slice& value) {
169 data_[key] = value.ToString();
170 }
171
172 // Finish constructing the data structure with all the keys that have
173 // been added so far. Returns the keys in sorted order in "*keys"
174 // and stores the key/value pairs in "*kvmap"
175 void Finish(const Options& options, const ImmutableCFOptions& ioptions,
11fdf7f2 176 const MutableCFOptions& moptions,
7c673cae
FG
177 const BlockBasedTableOptions& table_options,
178 const InternalKeyComparator& internal_comparator,
179 std::vector<std::string>* keys, stl_wrappers::KVMap* kvmap) {
180 last_internal_key_ = &internal_comparator;
181 *kvmap = data_;
182 keys->clear();
183 for (const auto& kv : data_) {
184 keys->push_back(kv.first);
185 }
186 data_.clear();
11fdf7f2 187 Status s = FinishImpl(options, ioptions, moptions, table_options,
7c673cae
FG
188 internal_comparator, *kvmap);
189 ASSERT_TRUE(s.ok()) << s.ToString();
190 }
191
192 // Construct the data structure from the data in "data"
193 virtual Status FinishImpl(const Options& options,
194 const ImmutableCFOptions& ioptions,
11fdf7f2 195 const MutableCFOptions& moptions,
7c673cae
FG
196 const BlockBasedTableOptions& table_options,
197 const InternalKeyComparator& internal_comparator,
198 const stl_wrappers::KVMap& data) = 0;
199
11fdf7f2
TL
200 virtual InternalIterator* NewIterator(
201 const SliceTransform* prefix_extractor = nullptr) const = 0;
7c673cae
FG
202
203 virtual const stl_wrappers::KVMap& data() { return data_; }
204
205 virtual bool IsArenaMode() const { return false; }
206
207 virtual DB* db() const { return nullptr; } // Overridden in DBConstructor
208
209 virtual bool AnywayDeleteIterator() const { return false; }
210
211 protected:
212 const InternalKeyComparator* last_internal_key_;
213
214 private:
215 stl_wrappers::KVMap data_;
216};
217
7c673cae
FG
218// A helper class that converts internal format keys into user keys
219class KeyConvertingIterator : public InternalIterator {
220 public:
221 explicit KeyConvertingIterator(InternalIterator* iter,
222 bool arena_mode = false)
223 : iter_(iter), arena_mode_(arena_mode) {}
494da23a 224 ~KeyConvertingIterator() override {
7c673cae
FG
225 if (arena_mode_) {
226 iter_->~InternalIterator();
227 } else {
228 delete iter_;
229 }
230 }
494da23a
TL
231 bool Valid() const override { return iter_->Valid() && status_.ok(); }
232 void Seek(const Slice& target) override {
7c673cae
FG
233 ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
234 std::string encoded;
235 AppendInternalKey(&encoded, ikey);
236 iter_->Seek(encoded);
237 }
494da23a 238 void SeekForPrev(const Slice& target) override {
7c673cae
FG
239 ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
240 std::string encoded;
241 AppendInternalKey(&encoded, ikey);
242 iter_->SeekForPrev(encoded);
243 }
494da23a
TL
244 void SeekToFirst() override { iter_->SeekToFirst(); }
245 void SeekToLast() override { iter_->SeekToLast(); }
246 void Next() override { iter_->Next(); }
247 void Prev() override { iter_->Prev(); }
20effc67
TL
248 IterBoundCheck UpperBoundCheckResult() override {
249 return iter_->UpperBoundCheckResult();
250 }
7c673cae 251
494da23a 252 Slice key() const override {
7c673cae
FG
253 assert(Valid());
254 ParsedInternalKey parsed_key;
20effc67
TL
255 Status pik_status =
256 ParseInternalKey(iter_->key(), &parsed_key, true /* log_err_key */);
257 if (!pik_status.ok()) {
258 status_ = pik_status;
259 return Slice(status_.getState());
7c673cae
FG
260 }
261 return parsed_key.user_key;
262 }
263
494da23a
TL
264 Slice value() const override { return iter_->value(); }
265 Status status() const override {
7c673cae
FG
266 return status_.ok() ? iter_->status() : status_;
267 }
268
269 private:
270 mutable Status status_;
271 InternalIterator* iter_;
272 bool arena_mode_;
273
274 // No copying allowed
275 KeyConvertingIterator(const KeyConvertingIterator&);
276 void operator=(const KeyConvertingIterator&);
277};
278
20effc67
TL
279// `BlockConstructor` APIs always accept/return user keys.
280class BlockConstructor : public Constructor {
281 public:
282 explicit BlockConstructor(const Comparator* cmp)
283 : Constructor(cmp), comparator_(cmp), block_(nullptr) {}
284 ~BlockConstructor() override { delete block_; }
285 Status FinishImpl(const Options& /*options*/,
286 const ImmutableCFOptions& /*ioptions*/,
287 const MutableCFOptions& /*moptions*/,
288 const BlockBasedTableOptions& table_options,
289 const InternalKeyComparator& /*internal_comparator*/,
290 const stl_wrappers::KVMap& kv_map) override {
291 delete block_;
292 block_ = nullptr;
293 BlockBuilder builder(table_options.block_restart_interval);
294
295 for (const auto& kv : kv_map) {
296 // `DataBlockIter` assumes it reads only internal keys. `BlockConstructor`
297 // clients provide user keys, so we need to convert to internal key format
298 // before writing the data block.
299 ParsedInternalKey ikey(kv.first, kMaxSequenceNumber, kTypeValue);
300 std::string encoded;
301 AppendInternalKey(&encoded, ikey);
302 builder.Add(encoded, kv.second);
303 }
304 // Open the block
305 data_ = builder.Finish().ToString();
306 BlockContents contents;
307 contents.data = data_;
308 block_ = new Block(std::move(contents));
309 return Status::OK();
310 }
311 InternalIterator* NewIterator(
312 const SliceTransform* /*prefix_extractor*/) const override {
313 // `DataBlockIter` returns the internal keys it reads.
314 // `KeyConvertingIterator` converts them to user keys before they are
315 // exposed to the `BlockConstructor` clients.
316 return new KeyConvertingIterator(
317 block_->NewDataIterator(comparator_, kDisableGlobalSequenceNumber));
318 }
319
320 private:
321 const Comparator* comparator_;
322 std::string data_;
323 Block* block_;
324
325 BlockConstructor();
326};
327
328class TableConstructor : public Constructor {
7c673cae
FG
329 public:
330 explicit TableConstructor(const Comparator* cmp,
11fdf7f2 331 bool convert_to_internal_key = false,
f67539c2 332 int level = -1, SequenceNumber largest_seqno = 0)
7c673cae 333 : Constructor(cmp),
f67539c2 334 largest_seqno_(largest_seqno),
11fdf7f2 335 convert_to_internal_key_(convert_to_internal_key),
f67539c2
TL
336 level_(level) {
337 env_ = ROCKSDB_NAMESPACE::Env::Default();
338 }
494da23a 339 ~TableConstructor() override { Reset(); }
7c673cae 340
494da23a
TL
341 Status FinishImpl(const Options& options, const ImmutableCFOptions& ioptions,
342 const MutableCFOptions& moptions,
343 const BlockBasedTableOptions& /*table_options*/,
344 const InternalKeyComparator& internal_comparator,
345 const stl_wrappers::KVMap& kv_map) override {
7c673cae
FG
346 Reset();
347 soptions.use_mmap_reads = ioptions.allow_mmap_reads;
11fdf7f2
TL
348 file_writer_.reset(test::GetWritableFileWriter(new test::StringSink(),
349 "" /* don't care */));
494da23a 350 std::unique_ptr<TableBuilder> builder;
7c673cae
FG
351 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
352 int_tbl_prop_collector_factories;
f67539c2
TL
353
354 if (largest_seqno_ != 0) {
355 // Pretend that it's an external file written by SstFileWriter.
356 int_tbl_prop_collector_factories.emplace_back(
357 new SstFileWriterPropertiesCollectorFactory(2 /* version */,
358 0 /* global_seqno*/));
359 }
360
7c673cae 361 std::string column_family_name;
7c673cae 362 builder.reset(ioptions.table_factory->NewTableBuilder(
494da23a
TL
363 TableBuilderOptions(ioptions, moptions, internal_comparator,
364 &int_tbl_prop_collector_factories,
365 options.compression, options.sample_for_compression,
366 options.compression_opts, false /* skip_filters */,
367 column_family_name, level_),
7c673cae
FG
368 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
369 file_writer_.get()));
370
20effc67 371 for (const auto& kv : kv_map) {
7c673cae
FG
372 if (convert_to_internal_key_) {
373 ParsedInternalKey ikey(kv.first, kMaxSequenceNumber, kTypeValue);
374 std::string encoded;
375 AppendInternalKey(&encoded, ikey);
376 builder->Add(encoded, kv.second);
377 } else {
378 builder->Add(kv.first, kv.second);
379 }
380 EXPECT_TRUE(builder->status().ok());
381 }
382 Status s = builder->Finish();
383 file_writer_->Flush();
384 EXPECT_TRUE(s.ok()) << s.ToString();
385
11fdf7f2 386 EXPECT_EQ(TEST_GetSink()->contents().size(), builder->FileSize());
7c673cae
FG
387
388 // Open the table
389 uniq_id_ = cur_uniq_id_++;
390 file_reader_.reset(test::GetRandomAccessFileReader(new test::StringSource(
11fdf7f2
TL
391 TEST_GetSink()->contents(), uniq_id_, ioptions.allow_mmap_reads)));
392 const bool kSkipFilters = true;
393 const bool kImmortal = true;
7c673cae 394 return ioptions.table_factory->NewTableReader(
11fdf7f2
TL
395 TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
396 internal_comparator, !kSkipFilters, !kImmortal,
20effc67
TL
397 false, level_, largest_seqno_, &block_cache_tracer_,
398 moptions.write_buffer_size),
11fdf7f2
TL
399 std::move(file_reader_), TEST_GetSink()->contents().size(),
400 &table_reader_);
7c673cae
FG
401 }
402
494da23a 403 InternalIterator* NewIterator(
11fdf7f2 404 const SliceTransform* prefix_extractor) const override {
f67539c2 405 InternalIterator* iter = table_reader_->NewIterator(
20effc67
TL
406 read_options_, prefix_extractor, /*arena=*/nullptr,
407 /*skip_filters=*/false, TableReaderCaller::kUncategorized);
7c673cae
FG
408 if (convert_to_internal_key_) {
409 return new KeyConvertingIterator(iter);
410 } else {
411 return iter;
412 }
413 }
414
415 uint64_t ApproximateOffsetOf(const Slice& key) const {
416 if (convert_to_internal_key_) {
417 InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
418 const Slice skey = ikey.Encode();
f67539c2
TL
419 return table_reader_->ApproximateOffsetOf(
420 skey, TableReaderCaller::kUncategorized);
7c673cae 421 }
f67539c2
TL
422 return table_reader_->ApproximateOffsetOf(
423 key, TableReaderCaller::kUncategorized);
7c673cae
FG
424 }
425
11fdf7f2
TL
426 virtual Status Reopen(const ImmutableCFOptions& ioptions,
427 const MutableCFOptions& moptions) {
7c673cae 428 file_reader_.reset(test::GetRandomAccessFileReader(new test::StringSource(
11fdf7f2 429 TEST_GetSink()->contents(), uniq_id_, ioptions.allow_mmap_reads)));
7c673cae 430 return ioptions.table_factory->NewTableReader(
11fdf7f2
TL
431 TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
432 *last_internal_key_),
433 std::move(file_reader_), TEST_GetSink()->contents().size(),
434 &table_reader_);
7c673cae
FG
435 }
436
11fdf7f2 437 virtual TableReader* GetTableReader() { return table_reader_.get(); }
7c673cae 438
494da23a 439 bool AnywayDeleteIterator() const override {
7c673cae
FG
440 return convert_to_internal_key_;
441 }
442
443 void ResetTableReader() { table_reader_.reset(); }
444
445 bool ConvertToInternalKey() { return convert_to_internal_key_; }
446
11fdf7f2 447 test::StringSink* TEST_GetSink() {
f67539c2
TL
448 return ROCKSDB_NAMESPACE::test::GetStringSinkFromLegacyWriter(
449 file_writer_.get());
11fdf7f2
TL
450 }
451
f67539c2
TL
452 BlockCacheTracer block_cache_tracer_;
453
7c673cae
FG
454 private:
455 void Reset() {
456 uniq_id_ = 0;
457 table_reader_.reset();
458 file_writer_.reset();
459 file_reader_.reset();
460 }
461
20effc67 462 const ReadOptions read_options_;
7c673cae 463 uint64_t uniq_id_;
494da23a
TL
464 std::unique_ptr<WritableFileWriter> file_writer_;
465 std::unique_ptr<RandomAccessFileReader> file_reader_;
466 std::unique_ptr<TableReader> table_reader_;
f67539c2 467 SequenceNumber largest_seqno_;
7c673cae 468 bool convert_to_internal_key_;
11fdf7f2 469 int level_;
7c673cae
FG
470
471 TableConstructor();
472
473 static uint64_t cur_uniq_id_;
474 EnvOptions soptions;
f67539c2 475 Env* env_;
7c673cae
FG
476};
477uint64_t TableConstructor::cur_uniq_id_ = 1;
478
479class MemTableConstructor: public Constructor {
480 public:
481 explicit MemTableConstructor(const Comparator* cmp, WriteBufferManager* wb)
482 : Constructor(cmp),
483 internal_comparator_(cmp),
484 write_buffer_manager_(wb),
485 table_factory_(new SkipListFactory) {
486 options_.memtable_factory = table_factory_;
487 ImmutableCFOptions ioptions(options_);
488 memtable_ =
489 new MemTable(internal_comparator_, ioptions, MutableCFOptions(options_),
11fdf7f2 490 wb, kMaxSequenceNumber, 0 /* column_family_id */);
7c673cae
FG
491 memtable_->Ref();
492 }
494da23a
TL
493 ~MemTableConstructor() override { delete memtable_->Unref(); }
494 Status FinishImpl(const Options&, const ImmutableCFOptions& ioptions,
495 const MutableCFOptions& /*moptions*/,
496 const BlockBasedTableOptions& /*table_options*/,
497 const InternalKeyComparator& /*internal_comparator*/,
498 const stl_wrappers::KVMap& kv_map) override {
7c673cae
FG
499 delete memtable_->Unref();
500 ImmutableCFOptions mem_ioptions(ioptions);
501 memtable_ = new MemTable(internal_comparator_, mem_ioptions,
502 MutableCFOptions(options_), write_buffer_manager_,
11fdf7f2 503 kMaxSequenceNumber, 0 /* column_family_id */);
7c673cae
FG
504 memtable_->Ref();
505 int seq = 1;
20effc67 506 for (const auto& kv : kv_map) {
7c673cae
FG
507 memtable_->Add(seq, kTypeValue, kv.first, kv.second);
508 seq++;
509 }
510 return Status::OK();
511 }
494da23a 512 InternalIterator* NewIterator(
11fdf7f2 513 const SliceTransform* /*prefix_extractor*/) const override {
7c673cae
FG
514 return new KeyConvertingIterator(
515 memtable_->NewIterator(ReadOptions(), &arena_), true);
516 }
517
494da23a 518 bool AnywayDeleteIterator() const override { return true; }
7c673cae 519
494da23a 520 bool IsArenaMode() const override { return true; }
7c673cae
FG
521
522 private:
523 mutable Arena arena_;
524 InternalKeyComparator internal_comparator_;
525 Options options_;
526 WriteBufferManager* write_buffer_manager_;
527 MemTable* memtable_;
528 std::shared_ptr<SkipListFactory> table_factory_;
529};
530
531class InternalIteratorFromIterator : public InternalIterator {
532 public:
533 explicit InternalIteratorFromIterator(Iterator* it) : it_(it) {}
494da23a
TL
534 bool Valid() const override { return it_->Valid(); }
535 void Seek(const Slice& target) override { it_->Seek(target); }
536 void SeekForPrev(const Slice& target) override { it_->SeekForPrev(target); }
537 void SeekToFirst() override { it_->SeekToFirst(); }
538 void SeekToLast() override { it_->SeekToLast(); }
539 void Next() override { it_->Next(); }
540 void Prev() override { it_->Prev(); }
7c673cae
FG
541 Slice key() const override { return it_->key(); }
542 Slice value() const override { return it_->value(); }
494da23a 543 Status status() const override { return it_->status(); }
7c673cae
FG
544
545 private:
494da23a 546 std::unique_ptr<Iterator> it_;
7c673cae
FG
547};
548
549class DBConstructor: public Constructor {
550 public:
551 explicit DBConstructor(const Comparator* cmp)
552 : Constructor(cmp),
553 comparator_(cmp) {
554 db_ = nullptr;
555 NewDB();
556 }
494da23a
TL
557 ~DBConstructor() override { delete db_; }
558 Status FinishImpl(const Options& /*options*/,
559 const ImmutableCFOptions& /*ioptions*/,
560 const MutableCFOptions& /*moptions*/,
561 const BlockBasedTableOptions& /*table_options*/,
562 const InternalKeyComparator& /*internal_comparator*/,
563 const stl_wrappers::KVMap& kv_map) override {
7c673cae
FG
564 delete db_;
565 db_ = nullptr;
566 NewDB();
20effc67 567 for (const auto& kv : kv_map) {
7c673cae 568 WriteBatch batch;
20effc67 569 EXPECT_OK(batch.Put(kv.first, kv.second));
7c673cae
FG
570 EXPECT_TRUE(db_->Write(WriteOptions(), &batch).ok());
571 }
572 return Status::OK();
573 }
574
494da23a 575 InternalIterator* NewIterator(
11fdf7f2 576 const SliceTransform* /*prefix_extractor*/) const override {
7c673cae
FG
577 return new InternalIteratorFromIterator(db_->NewIterator(ReadOptions()));
578 }
579
494da23a 580 DB* db() const override { return db_; }
7c673cae
FG
581
582 private:
583 void NewDB() {
11fdf7f2 584 std::string name = test::PerThreadDBPath("table_testdb");
7c673cae
FG
585
586 Options options;
587 options.comparator = comparator_;
588 Status status = DestroyDB(name, options);
589 ASSERT_TRUE(status.ok()) << status.ToString();
590
591 options.create_if_missing = true;
592 options.error_if_exists = true;
593 options.write_buffer_size = 10000; // Something small to force merging
594 status = DB::Open(options, name, &db_);
595 ASSERT_TRUE(status.ok()) << status.ToString();
596 }
597
598 const Comparator* comparator_;
599 DB* db_;
600};
601
602enum TestType {
603 BLOCK_BASED_TABLE_TEST,
604#ifndef ROCKSDB_LITE
605 PLAIN_TABLE_SEMI_FIXED_PREFIX,
606 PLAIN_TABLE_FULL_STR_PREFIX,
607 PLAIN_TABLE_TOTAL_ORDER,
608#endif // !ROCKSDB_LITE
609 BLOCK_TEST,
610 MEMTABLE_TEST,
611 DB_TEST
612};
613
614struct TestArgs {
615 TestType type;
616 bool reverse_compare;
617 int restart_interval;
618 CompressionType compression;
20effc67 619 uint32_t compression_parallel_threads;
7c673cae
FG
620 uint32_t format_version;
621 bool use_mmap;
622};
623
20effc67
TL
624std::ostream& operator<<(std::ostream& os, const TestArgs& args) {
625 os << "type: " << args.type << " reverse_compare: " << args.reverse_compare
626 << " restart_interval: " << args.restart_interval
627 << " compression: " << args.compression
628 << " compression_parallel_threads: " << args.compression_parallel_threads
629 << " format_version: " << args.format_version
630 << " use_mmap: " << args.use_mmap;
631
632 return os;
633}
634
7c673cae
FG
635static std::vector<TestArgs> GenerateArgList() {
636 std::vector<TestArgs> test_args;
637 std::vector<TestType> test_types = {
638 BLOCK_BASED_TABLE_TEST,
639#ifndef ROCKSDB_LITE
640 PLAIN_TABLE_SEMI_FIXED_PREFIX,
641 PLAIN_TABLE_FULL_STR_PREFIX,
642 PLAIN_TABLE_TOTAL_ORDER,
643#endif // !ROCKSDB_LITE
644 BLOCK_TEST,
645 MEMTABLE_TEST, DB_TEST};
646 std::vector<bool> reverse_compare_types = {false, true};
647 std::vector<int> restart_intervals = {16, 1, 1024};
20effc67 648 std::vector<uint32_t> compression_parallel_threads = {1, 4};
7c673cae
FG
649
650 // Only add compression if it is supported
651 std::vector<std::pair<CompressionType, bool>> compression_types;
652 compression_types.emplace_back(kNoCompression, false);
653 if (Snappy_Supported()) {
654 compression_types.emplace_back(kSnappyCompression, false);
655 }
656 if (Zlib_Supported()) {
657 compression_types.emplace_back(kZlibCompression, false);
658 compression_types.emplace_back(kZlibCompression, true);
659 }
660 if (BZip2_Supported()) {
661 compression_types.emplace_back(kBZip2Compression, false);
662 compression_types.emplace_back(kBZip2Compression, true);
663 }
664 if (LZ4_Supported()) {
665 compression_types.emplace_back(kLZ4Compression, false);
666 compression_types.emplace_back(kLZ4Compression, true);
667 compression_types.emplace_back(kLZ4HCCompression, false);
668 compression_types.emplace_back(kLZ4HCCompression, true);
669 }
670 if (XPRESS_Supported()) {
671 compression_types.emplace_back(kXpressCompression, false);
672 compression_types.emplace_back(kXpressCompression, true);
673 }
674 if (ZSTD_Supported()) {
675 compression_types.emplace_back(kZSTD, false);
676 compression_types.emplace_back(kZSTD, true);
677 }
678
679 for (auto test_type : test_types) {
680 for (auto reverse_compare : reverse_compare_types) {
681#ifndef ROCKSDB_LITE
682 if (test_type == PLAIN_TABLE_SEMI_FIXED_PREFIX ||
683 test_type == PLAIN_TABLE_FULL_STR_PREFIX ||
684 test_type == PLAIN_TABLE_TOTAL_ORDER) {
685 // Plain table doesn't use restart index or compression.
686 TestArgs one_arg;
687 one_arg.type = test_type;
688 one_arg.reverse_compare = reverse_compare;
689 one_arg.restart_interval = restart_intervals[0];
690 one_arg.compression = compression_types[0].first;
20effc67
TL
691 one_arg.compression_parallel_threads = 1;
692 one_arg.format_version = 0;
7c673cae
FG
693 one_arg.use_mmap = true;
694 test_args.push_back(one_arg);
695 one_arg.use_mmap = false;
696 test_args.push_back(one_arg);
697 continue;
698 }
699#endif // !ROCKSDB_LITE
700
701 for (auto restart_interval : restart_intervals) {
702 for (auto compression_type : compression_types) {
20effc67
TL
703 for (auto num_threads : compression_parallel_threads) {
704 TestArgs one_arg;
705 one_arg.type = test_type;
706 one_arg.reverse_compare = reverse_compare;
707 one_arg.restart_interval = restart_interval;
708 one_arg.compression = compression_type.first;
709 one_arg.compression_parallel_threads = num_threads;
710 one_arg.format_version = compression_type.second ? 2 : 1;
711 one_arg.use_mmap = false;
712 test_args.push_back(one_arg);
713 }
7c673cae
FG
714 }
715 }
716 }
717 }
718 return test_args;
719}
720
721// In order to make all tests run for plain table format, including
722// those operating on empty keys, create a new prefix transformer which
723// return fixed prefix if the slice is not shorter than the prefix length,
724// and the full slice if it is shorter.
725class FixedOrLessPrefixTransform : public SliceTransform {
726 private:
727 const size_t prefix_len_;
728
729 public:
730 explicit FixedOrLessPrefixTransform(size_t prefix_len) :
731 prefix_len_(prefix_len) {
732 }
733
494da23a 734 const char* Name() const override { return "rocksdb.FixedPrefix"; }
7c673cae 735
494da23a 736 Slice Transform(const Slice& src) const override {
7c673cae
FG
737 assert(InDomain(src));
738 if (src.size() < prefix_len_) {
739 return src;
740 }
741 return Slice(src.data(), prefix_len_);
742 }
743
494da23a 744 bool InDomain(const Slice& /*src*/) const override { return true; }
7c673cae 745
494da23a 746 bool InRange(const Slice& dst) const override {
7c673cae
FG
747 return (dst.size() <= prefix_len_);
748 }
494da23a 749 bool FullLengthEnabled(size_t* /*len*/) const override { return false; }
7c673cae
FG
750};
751
752class HarnessTest : public testing::Test {
753 public:
20effc67
TL
754 explicit HarnessTest(const TestArgs& args)
755 : args_(args),
756 ioptions_(options_),
11fdf7f2 757 moptions_(options_),
20effc67
TL
758 write_buffer_(options_.db_write_buffer_size),
759 support_prev_(true),
760 only_support_prefix_seek_(false) {
761 options_.compression = args_.compression;
762 options_.compression_opts.parallel_threads =
763 args_.compression_parallel_threads;
7c673cae
FG
764 // Use shorter block size for tests to exercise block boundary
765 // conditions more.
20effc67 766 if (args_.reverse_compare) {
7c673cae
FG
767 options_.comparator = &reverse_key_comparator;
768 }
769
770 internal_comparator_.reset(
771 new test::PlainInternalKeyComparator(options_.comparator));
772
20effc67
TL
773 options_.allow_mmap_reads = args_.use_mmap;
774 switch (args_.type) {
7c673cae
FG
775 case BLOCK_BASED_TABLE_TEST:
776 table_options_.flush_block_policy_factory.reset(
777 new FlushBlockBySizePolicyFactory());
778 table_options_.block_size = 256;
20effc67
TL
779 table_options_.block_restart_interval = args_.restart_interval;
780 table_options_.index_block_restart_interval = args_.restart_interval;
781 table_options_.format_version = args_.format_version;
7c673cae
FG
782 options_.table_factory.reset(
783 new BlockBasedTableFactory(table_options_));
20effc67
TL
784 constructor_.reset(new TableConstructor(
785 options_.comparator, true /* convert_to_internal_key_ */));
7c673cae
FG
786 internal_comparator_.reset(
787 new InternalKeyComparator(options_.comparator));
788 break;
789// Plain table is not supported in ROCKSDB_LITE
790#ifndef ROCKSDB_LITE
791 case PLAIN_TABLE_SEMI_FIXED_PREFIX:
792 support_prev_ = false;
793 only_support_prefix_seek_ = true;
794 options_.prefix_extractor.reset(new FixedOrLessPrefixTransform(2));
795 options_.table_factory.reset(NewPlainTableFactory());
20effc67
TL
796 constructor_.reset(new TableConstructor(
797 options_.comparator, true /* convert_to_internal_key_ */));
7c673cae
FG
798 internal_comparator_.reset(
799 new InternalKeyComparator(options_.comparator));
800 break;
801 case PLAIN_TABLE_FULL_STR_PREFIX:
802 support_prev_ = false;
803 only_support_prefix_seek_ = true;
804 options_.prefix_extractor.reset(NewNoopTransform());
805 options_.table_factory.reset(NewPlainTableFactory());
20effc67
TL
806 constructor_.reset(new TableConstructor(
807 options_.comparator, true /* convert_to_internal_key_ */));
7c673cae
FG
808 internal_comparator_.reset(
809 new InternalKeyComparator(options_.comparator));
810 break;
811 case PLAIN_TABLE_TOTAL_ORDER:
812 support_prev_ = false;
813 only_support_prefix_seek_ = false;
814 options_.prefix_extractor = nullptr;
815
816 {
817 PlainTableOptions plain_table_options;
818 plain_table_options.user_key_len = kPlainTableVariableLength;
819 plain_table_options.bloom_bits_per_key = 0;
820 plain_table_options.hash_table_ratio = 0;
821
822 options_.table_factory.reset(
823 NewPlainTableFactory(plain_table_options));
824 }
20effc67
TL
825 constructor_.reset(new TableConstructor(
826 options_.comparator, true /* convert_to_internal_key_ */));
7c673cae
FG
827 internal_comparator_.reset(
828 new InternalKeyComparator(options_.comparator));
829 break;
830#endif // !ROCKSDB_LITE
831 case BLOCK_TEST:
832 table_options_.block_size = 256;
833 options_.table_factory.reset(
834 new BlockBasedTableFactory(table_options_));
20effc67 835 constructor_.reset(new BlockConstructor(options_.comparator));
7c673cae
FG
836 break;
837 case MEMTABLE_TEST:
838 table_options_.block_size = 256;
839 options_.table_factory.reset(
840 new BlockBasedTableFactory(table_options_));
20effc67
TL
841 constructor_.reset(
842 new MemTableConstructor(options_.comparator, &write_buffer_));
7c673cae
FG
843 break;
844 case DB_TEST:
845 table_options_.block_size = 256;
846 options_.table_factory.reset(
847 new BlockBasedTableFactory(table_options_));
20effc67 848 constructor_.reset(new DBConstructor(options_.comparator));
7c673cae
FG
849 break;
850 }
851 ioptions_ = ImmutableCFOptions(options_);
11fdf7f2 852 moptions_ = MutableCFOptions(options_);
7c673cae
FG
853 }
854
7c673cae
FG
855 void Add(const std::string& key, const std::string& value) {
856 constructor_->Add(key, value);
857 }
858
859 void Test(Random* rnd) {
860 std::vector<std::string> keys;
861 stl_wrappers::KVMap data;
11fdf7f2 862 constructor_->Finish(options_, ioptions_, moptions_, table_options_,
7c673cae
FG
863 *internal_comparator_, &keys, &data);
864
865 TestForwardScan(keys, data);
866 if (support_prev_) {
867 TestBackwardScan(keys, data);
868 }
869 TestRandomAccess(rnd, keys, data);
870 }
871
11fdf7f2 872 void TestForwardScan(const std::vector<std::string>& /*keys*/,
7c673cae
FG
873 const stl_wrappers::KVMap& data) {
874 InternalIterator* iter = constructor_->NewIterator();
875 ASSERT_TRUE(!iter->Valid());
876 iter->SeekToFirst();
20effc67 877 ASSERT_OK(iter->status());
7c673cae
FG
878 for (stl_wrappers::KVMap::const_iterator model_iter = data.begin();
879 model_iter != data.end(); ++model_iter) {
880 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
881 iter->Next();
20effc67 882 ASSERT_OK(iter->status());
7c673cae
FG
883 }
884 ASSERT_TRUE(!iter->Valid());
20effc67 885 ASSERT_OK(iter->status());
7c673cae
FG
886 if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
887 iter->~InternalIterator();
888 } else {
889 delete iter;
890 }
891 }
892
11fdf7f2 893 void TestBackwardScan(const std::vector<std::string>& /*keys*/,
7c673cae
FG
894 const stl_wrappers::KVMap& data) {
895 InternalIterator* iter = constructor_->NewIterator();
896 ASSERT_TRUE(!iter->Valid());
897 iter->SeekToLast();
20effc67 898 ASSERT_OK(iter->status());
7c673cae
FG
899 for (stl_wrappers::KVMap::const_reverse_iterator model_iter = data.rbegin();
900 model_iter != data.rend(); ++model_iter) {
901 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
902 iter->Prev();
20effc67 903 ASSERT_OK(iter->status());
7c673cae
FG
904 }
905 ASSERT_TRUE(!iter->Valid());
20effc67 906 ASSERT_OK(iter->status());
7c673cae
FG
907 if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
908 iter->~InternalIterator();
909 } else {
910 delete iter;
911 }
912 }
913
914 void TestRandomAccess(Random* rnd, const std::vector<std::string>& keys,
915 const stl_wrappers::KVMap& data) {
916 static const bool kVerbose = false;
917 InternalIterator* iter = constructor_->NewIterator();
918 ASSERT_TRUE(!iter->Valid());
919 stl_wrappers::KVMap::const_iterator model_iter = data.begin();
920 if (kVerbose) fprintf(stderr, "---\n");
921 for (int i = 0; i < 200; i++) {
922 const int toss = rnd->Uniform(support_prev_ ? 5 : 3);
923 switch (toss) {
924 case 0: {
925 if (iter->Valid()) {
926 if (kVerbose) fprintf(stderr, "Next\n");
927 iter->Next();
20effc67 928 ASSERT_OK(iter->status());
7c673cae
FG
929 ++model_iter;
930 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
931 }
932 break;
933 }
934
935 case 1: {
936 if (kVerbose) fprintf(stderr, "SeekToFirst\n");
937 iter->SeekToFirst();
20effc67 938 ASSERT_OK(iter->status());
7c673cae
FG
939 model_iter = data.begin();
940 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
941 break;
942 }
943
944 case 2: {
945 std::string key = PickRandomKey(rnd, keys);
946 model_iter = data.lower_bound(key);
947 if (kVerbose) fprintf(stderr, "Seek '%s'\n",
948 EscapeString(key).c_str());
949 iter->Seek(Slice(key));
20effc67 950 ASSERT_OK(iter->status());
7c673cae
FG
951 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
952 break;
953 }
954
955 case 3: {
956 if (iter->Valid()) {
957 if (kVerbose) fprintf(stderr, "Prev\n");
958 iter->Prev();
20effc67 959 ASSERT_OK(iter->status());
7c673cae
FG
960 if (model_iter == data.begin()) {
961 model_iter = data.end(); // Wrap around to invalid value
962 } else {
963 --model_iter;
964 }
965 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
966 }
967 break;
968 }
969
970 case 4: {
971 if (kVerbose) fprintf(stderr, "SeekToLast\n");
972 iter->SeekToLast();
20effc67 973 ASSERT_OK(iter->status());
7c673cae
FG
974 if (keys.empty()) {
975 model_iter = data.end();
976 } else {
977 std::string last = data.rbegin()->first;
978 model_iter = data.lower_bound(last);
979 }
980 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
981 break;
982 }
983 }
984 }
985 if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
986 iter->~InternalIterator();
987 } else {
988 delete iter;
989 }
990 }
991
992 std::string ToString(const stl_wrappers::KVMap& data,
993 const stl_wrappers::KVMap::const_iterator& it) {
994 if (it == data.end()) {
995 return "END";
996 } else {
997 return "'" + it->first + "->" + it->second + "'";
998 }
999 }
1000
1001 std::string ToString(const stl_wrappers::KVMap& data,
1002 const stl_wrappers::KVMap::const_reverse_iterator& it) {
1003 if (it == data.rend()) {
1004 return "END";
1005 } else {
1006 return "'" + it->first + "->" + it->second + "'";
1007 }
1008 }
1009
1010 std::string ToString(const InternalIterator* it) {
1011 if (!it->Valid()) {
1012 return "END";
1013 } else {
1014 return "'" + it->key().ToString() + "->" + it->value().ToString() + "'";
1015 }
1016 }
1017
1018 std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) {
1019 if (keys.empty()) {
1020 return "foo";
1021 } else {
1022 const int index = rnd->Uniform(static_cast<int>(keys.size()));
1023 std::string result = keys[index];
1024 switch (rnd->Uniform(support_prev_ ? 3 : 1)) {
1025 case 0:
1026 // Return an existing key
1027 break;
1028 case 1: {
1029 // Attempt to return something smaller than an existing key
1030 if (result.size() > 0 && result[result.size() - 1] > '\0'
1031 && (!only_support_prefix_seek_
1032 || options_.prefix_extractor->Transform(result).size()
1033 < result.size())) {
1034 result[result.size() - 1]--;
1035 }
1036 break;
1037 }
1038 case 2: {
1039 // Return something larger than an existing key
1040 Increment(options_.comparator, &result);
1041 break;
1042 }
1043 }
1044 return result;
1045 }
1046 }
1047
1048 // Returns nullptr if not running against a DB
1049 DB* db() const { return constructor_->db(); }
1050
1051 private:
20effc67
TL
1052 TestArgs args_;
1053 Options options_;
7c673cae 1054 ImmutableCFOptions ioptions_;
11fdf7f2 1055 MutableCFOptions moptions_;
20effc67
TL
1056 BlockBasedTableOptions table_options_;
1057 std::unique_ptr<Constructor> constructor_;
7c673cae
FG
1058 WriteBufferManager write_buffer_;
1059 bool support_prev_;
1060 bool only_support_prefix_seek_;
494da23a 1061 std::shared_ptr<InternalKeyComparator> internal_comparator_;
7c673cae
FG
1062};
1063
20effc67
TL
1064class ParameterizedHarnessTest : public HarnessTest,
1065 public testing::WithParamInterface<TestArgs> {
1066 public:
1067 ParameterizedHarnessTest() : HarnessTest(GetParam()) {}
1068};
1069
1070INSTANTIATE_TEST_CASE_P(TableTest, ParameterizedHarnessTest,
1071 ::testing::ValuesIn(GenerateArgList()));
1072
1073class DBHarnessTest : public HarnessTest {
1074 public:
1075 DBHarnessTest()
1076 : HarnessTest(TestArgs{DB_TEST, /* reverse_compare */ false,
1077 /* restart_interval */ 16, kNoCompression,
1078 /* compression_parallel_threads */ 1,
1079 /* format_version */ 0, /* use_mmap */ false}) {}
1080};
1081
7c673cae
FG
1082static bool Between(uint64_t val, uint64_t low, uint64_t high) {
1083 bool result = (val >= low) && (val <= high);
1084 if (!result) {
1085 fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
1086 (unsigned long long)(val),
1087 (unsigned long long)(low),
1088 (unsigned long long)(high));
1089 }
1090 return result;
1091}
1092
1093// Tests against all kinds of tables
1094class TableTest : public testing::Test {
1095 public:
1096 const InternalKeyComparator& GetPlainInternalComparator(
1097 const Comparator* comp) {
1098 if (!plain_internal_comparator) {
1099 plain_internal_comparator.reset(
1100 new test::PlainInternalKeyComparator(comp));
1101 }
1102 return *plain_internal_comparator;
1103 }
1104 void IndexTest(BlockBasedTableOptions table_options);
1105
1106 private:
1107 std::unique_ptr<InternalKeyComparator> plain_internal_comparator;
1108};
1109
1110class GeneralTableTest : public TableTest {};
11fdf7f2
TL
1111class BlockBasedTableTest
1112 : public TableTest,
1113 virtual public ::testing::WithParamInterface<uint32_t> {
1114 public:
f67539c2
TL
1115 BlockBasedTableTest() : format_(GetParam()) {
1116 env_ = ROCKSDB_NAMESPACE::Env::Default();
1117 }
11fdf7f2
TL
1118
1119 BlockBasedTableOptions GetBlockBasedTableOptions() {
1120 BlockBasedTableOptions options;
1121 options.format_version = format_;
1122 return options;
1123 }
1124
f67539c2
TL
1125 void SetupTracingTest(TableConstructor* c) {
1126 test_path_ = test::PerThreadDBPath("block_based_table_tracing_test");
1127 EXPECT_OK(env_->CreateDir(test_path_));
1128 trace_file_path_ = test_path_ + "/block_cache_trace_file";
1129 TraceOptions trace_opt;
1130 std::unique_ptr<TraceWriter> trace_writer;
1131 EXPECT_OK(NewFileTraceWriter(env_, EnvOptions(), trace_file_path_,
1132 &trace_writer));
20effc67
TL
1133 // Always return Status::OK().
1134 assert(c->block_cache_tracer_
1135 .StartTrace(env_, trace_opt, std::move(trace_writer))
1136 .ok());
f67539c2
TL
1137 {
1138 std::string user_key = "k01";
1139 InternalKey internal_key(user_key, 0, kTypeValue);
1140 std::string encoded_key = internal_key.Encode().ToString();
1141 c->Add(encoded_key, kDummyValue);
1142 }
1143 {
1144 std::string user_key = "k02";
1145 InternalKey internal_key(user_key, 0, kTypeValue);
1146 std::string encoded_key = internal_key.Encode().ToString();
1147 c->Add(encoded_key, kDummyValue);
1148 }
1149 }
1150
1151 void VerifyBlockAccessTrace(
1152 TableConstructor* c,
1153 const std::vector<BlockCacheTraceRecord>& expected_records) {
1154 c->block_cache_tracer_.EndTrace();
1155
20effc67
TL
1156 {
1157 std::unique_ptr<TraceReader> trace_reader;
1158 Status s =
1159 NewFileTraceReader(env_, EnvOptions(), trace_file_path_, &trace_reader);
1160 EXPECT_OK(s);
1161 BlockCacheTraceReader reader(std::move(trace_reader));
1162 BlockCacheTraceHeader header;
1163 EXPECT_OK(reader.ReadHeader(&header));
1164 uint32_t index = 0;
1165 while (s.ok()) {
1166 BlockCacheTraceRecord access;
1167 s = reader.ReadAccess(&access);
1168 if (!s.ok()) {
1169 break;
f67539c2 1170 }
20effc67
TL
1171 ASSERT_LT(index, expected_records.size());
1172 EXPECT_NE("", access.block_key);
1173 EXPECT_EQ(access.block_type, expected_records[index].block_type);
1174 EXPECT_GT(access.block_size, 0);
1175 EXPECT_EQ(access.caller, expected_records[index].caller);
1176 EXPECT_EQ(access.no_insert, expected_records[index].no_insert);
1177 EXPECT_EQ(access.is_cache_hit, expected_records[index].is_cache_hit);
1178 // Get
1179 if (access.caller == TableReaderCaller::kUserGet) {
1180 EXPECT_EQ(access.referenced_key,
1181 expected_records[index].referenced_key);
1182 EXPECT_EQ(access.get_id, expected_records[index].get_id);
1183 EXPECT_EQ(access.get_from_user_specified_snapshot,
1184 expected_records[index].get_from_user_specified_snapshot);
1185 if (access.block_type == TraceType::kBlockTraceDataBlock) {
1186 EXPECT_GT(access.referenced_data_size, 0);
1187 EXPECT_GT(access.num_keys_in_block, 0);
1188 EXPECT_EQ(access.referenced_key_exist_in_block,
1189 expected_records[index].referenced_key_exist_in_block);
1190 }
1191 } else {
1192 EXPECT_EQ(access.referenced_key, "");
1193 EXPECT_EQ(access.get_id, 0);
1194 EXPECT_TRUE(access.get_from_user_specified_snapshot == Boolean::kFalse);
1195 EXPECT_EQ(access.referenced_data_size, 0);
1196 EXPECT_EQ(access.num_keys_in_block, 0);
1197 EXPECT_TRUE(access.referenced_key_exist_in_block == Boolean::kFalse);
1198 }
1199 index++;
f67539c2 1200 }
20effc67 1201 EXPECT_EQ(index, expected_records.size());
f67539c2 1202 }
f67539c2
TL
1203 EXPECT_OK(env_->DeleteFile(trace_file_path_));
1204 EXPECT_OK(env_->DeleteDir(test_path_));
1205 }
1206
11fdf7f2
TL
1207 protected:
1208 uint64_t IndexUncompressedHelper(bool indexCompress);
1209
1210 private:
1211 uint32_t format_;
f67539c2
TL
1212 Env* env_;
1213 std::string trace_file_path_;
1214 std::string test_path_;
11fdf7f2 1215};
7c673cae
FG
1216class PlainTableTest : public TableTest {};
1217class TablePropertyTest : public testing::Test {};
11fdf7f2
TL
1218class BBTTailPrefetchTest : public TableTest {};
1219
f67539c2
TL
1220// The helper class to test the file checksum
1221class FileChecksumTestHelper {
1222 public:
1223 FileChecksumTestHelper(bool convert_to_internal_key = false)
1224 : convert_to_internal_key_(convert_to_internal_key) {
f67539c2
TL
1225 }
1226 ~FileChecksumTestHelper() {}
1227
1228 void CreateWriteableFile() {
20effc67 1229 sink_ = new test::StringSink();
f67539c2
TL
1230 file_writer_.reset(test::GetWritableFileWriter(sink_, "" /* don't care */));
1231 }
1232
20effc67 1233 void SetFileChecksumGenerator(FileChecksumGenerator* checksum_generator) {
f67539c2 1234 if (file_writer_ != nullptr) {
20effc67
TL
1235 file_writer_->TEST_SetFileChecksumGenerator(checksum_generator);
1236 } else {
1237 delete checksum_generator;
f67539c2
TL
1238 }
1239 }
1240
1241 WritableFileWriter* GetFileWriter() { return file_writer_.get(); }
1242
1243 Status ResetTableBuilder(std::unique_ptr<TableBuilder>&& builder) {
1244 assert(builder != nullptr);
1245 table_builder_ = std::move(builder);
1246 return Status::OK();
1247 }
1248
1249 void AddKVtoKVMap(int num_entries) {
1250 Random rnd(test::RandomSeed());
1251 for (int i = 0; i < num_entries; i++) {
20effc67 1252 std::string v = rnd.RandomString(100);
f67539c2
TL
1253 kv_map_[test::RandomKey(&rnd, 20)] = v;
1254 }
1255 }
1256
1257 Status WriteKVAndFlushTable() {
20effc67 1258 for (const auto& kv : kv_map_) {
f67539c2
TL
1259 if (convert_to_internal_key_) {
1260 ParsedInternalKey ikey(kv.first, kMaxSequenceNumber, kTypeValue);
1261 std::string encoded;
1262 AppendInternalKey(&encoded, ikey);
1263 table_builder_->Add(encoded, kv.second);
1264 } else {
1265 table_builder_->Add(kv.first, kv.second);
1266 }
1267 EXPECT_TRUE(table_builder_->status().ok());
1268 }
1269 Status s = table_builder_->Finish();
1270 file_writer_->Flush();
1271 EXPECT_TRUE(s.ok());
1272
1273 EXPECT_EQ(sink_->contents().size(), table_builder_->FileSize());
1274 return s;
1275 }
1276
20effc67
TL
1277 std::string GetFileChecksum() {
1278 file_writer_->Close();
1279 return table_builder_->GetFileChecksum();
1280 }
f67539c2
TL
1281
1282 const char* GetFileChecksumFuncName() {
1283 return table_builder_->GetFileChecksumFuncName();
1284 }
1285
20effc67 1286 Status CalculateFileChecksum(FileChecksumGenerator* file_checksum_generator,
f67539c2 1287 std::string* checksum) {
20effc67 1288 assert(file_checksum_generator != nullptr);
f67539c2
TL
1289 cur_uniq_id_ = checksum_uniq_id_++;
1290 test::StringSink* ss_rw =
1291 ROCKSDB_NAMESPACE::test::GetStringSinkFromLegacyWriter(
1292 file_writer_.get());
1293 file_reader_.reset(test::GetRandomAccessFileReader(
1294 new test::StringSource(ss_rw->contents())));
1295 std::unique_ptr<char[]> scratch(new char[2048]);
1296 Slice result;
1297 uint64_t offset = 0;
f67539c2 1298 Status s;
20effc67
TL
1299 s = file_reader_->Read(IOOptions(), offset, 2048, &result, scratch.get(),
1300 nullptr, false);
f67539c2
TL
1301 if (!s.ok()) {
1302 return s;
1303 }
1304 while (result.size() != 0) {
20effc67 1305 file_checksum_generator->Update(scratch.get(), result.size());
f67539c2 1306 offset += static_cast<uint64_t>(result.size());
20effc67
TL
1307 s = file_reader_->Read(IOOptions(), offset, 2048, &result, scratch.get(),
1308 nullptr, false);
f67539c2
TL
1309 if (!s.ok()) {
1310 return s;
1311 }
1312 }
1313 EXPECT_EQ(offset, static_cast<uint64_t>(table_builder_->FileSize()));
20effc67
TL
1314 file_checksum_generator->Finalize();
1315 *checksum = file_checksum_generator->GetChecksum();
f67539c2
TL
1316 return Status::OK();
1317 }
1318
1319 private:
1320 bool convert_to_internal_key_;
1321 uint64_t cur_uniq_id_;
1322 std::unique_ptr<WritableFileWriter> file_writer_;
1323 std::unique_ptr<RandomAccessFileReader> file_reader_;
1324 std::unique_ptr<TableBuilder> table_builder_;
1325 stl_wrappers::KVMap kv_map_;
20effc67 1326 test::StringSink* sink_ = nullptr;
f67539c2
TL
1327
1328 static uint64_t checksum_uniq_id_;
1329};
1330
1331uint64_t FileChecksumTestHelper::checksum_uniq_id_ = 1;
1332
11fdf7f2
TL
1333INSTANTIATE_TEST_CASE_P(FormatDef, BlockBasedTableTest,
1334 testing::Values(test::kDefaultFormatVersion));
1335INSTANTIATE_TEST_CASE_P(FormatLatest, BlockBasedTableTest,
1336 testing::Values(test::kLatestFormatVersion));
7c673cae
FG
1337
1338// This test serves as the living tutorial for the prefix scan of user collected
1339// properties.
1340TEST_F(TablePropertyTest, PrefixScanTest) {
1341 UserCollectedProperties props{{"num.111.1", "1"},
1342 {"num.111.2", "2"},
1343 {"num.111.3", "3"},
1344 {"num.333.1", "1"},
1345 {"num.333.2", "2"},
1346 {"num.333.3", "3"},
1347 {"num.555.1", "1"},
1348 {"num.555.2", "2"},
1349 {"num.555.3", "3"}, };
1350
1351 // prefixes that exist
1352 for (const std::string& prefix : {"num.111", "num.333", "num.555"}) {
1353 int num = 0;
1354 for (auto pos = props.lower_bound(prefix);
1355 pos != props.end() &&
1356 pos->first.compare(0, prefix.size(), prefix) == 0;
1357 ++pos) {
1358 ++num;
1359 auto key = prefix + "." + ToString(num);
1360 ASSERT_EQ(key, pos->first);
1361 ASSERT_EQ(ToString(num), pos->second);
1362 }
1363 ASSERT_EQ(3, num);
1364 }
1365
1366 // prefixes that don't exist
1367 for (const std::string& prefix :
1368 {"num.000", "num.222", "num.444", "num.666"}) {
1369 auto pos = props.lower_bound(prefix);
1370 ASSERT_TRUE(pos == props.end() ||
1371 pos->first.compare(0, prefix.size(), prefix) != 0);
1372 }
1373}
1374
1375// This test include all the basic checks except those for index size and block
1376// size, which will be conducted in separated unit tests.
11fdf7f2 1377TEST_P(BlockBasedTableTest, BasicBlockBasedTableProperties) {
7c673cae
FG
1378 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1379
1380 c.Add("a1", "val1");
1381 c.Add("b2", "val2");
1382 c.Add("c3", "val3");
1383 c.Add("d4", "val4");
1384 c.Add("e5", "val5");
1385 c.Add("f6", "val6");
1386 c.Add("g7", "val7");
1387 c.Add("h8", "val8");
1388 c.Add("j9", "val9");
1389 uint64_t diff_internal_user_bytes = 9 * 8; // 8 is seq size, 9 k-v totally
1390
1391 std::vector<std::string> keys;
1392 stl_wrappers::KVMap kvmap;
1393 Options options;
1394 options.compression = kNoCompression;
11fdf7f2 1395 options.statistics = CreateDBStatistics();
494da23a 1396 options.statistics->set_stats_level(StatsLevel::kAll);
11fdf7f2 1397 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1398 table_options.block_restart_interval = 1;
1399 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1400
11fdf7f2
TL
1401 ImmutableCFOptions ioptions(options);
1402 MutableCFOptions moptions(options);
1403 ioptions.statistics = options.statistics.get();
1404 c.Finish(options, ioptions, moptions, table_options,
7c673cae 1405 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
11fdf7f2 1406 ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_NOT_COMPRESSED), 0);
7c673cae
FG
1407
1408 auto& props = *c.GetTableReader()->GetTableProperties();
1409 ASSERT_EQ(kvmap.size(), props.num_entries);
1410
1411 auto raw_key_size = kvmap.size() * 2ul;
1412 auto raw_value_size = kvmap.size() * 4ul;
1413
1414 ASSERT_EQ(raw_key_size + diff_internal_user_bytes, props.raw_key_size);
1415 ASSERT_EQ(raw_value_size, props.raw_value_size);
1416 ASSERT_EQ(1ul, props.num_data_blocks);
1417 ASSERT_EQ("", props.filter_policy_name); // no filter policy is used
1418
1419 // Verify data size.
1420 BlockBuilder block_builder(1);
1421 for (const auto& item : kvmap) {
1422 block_builder.Add(item.first, item.second);
1423 }
1424 Slice content = block_builder.Finish();
1425 ASSERT_EQ(content.size() + kBlockTrailerSize + diff_internal_user_bytes,
1426 props.data_size);
1427 c.ResetTableReader();
1428}
1429
11fdf7f2
TL
1430#ifdef SNAPPY
1431uint64_t BlockBasedTableTest::IndexUncompressedHelper(bool compressed) {
1432 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1433 constexpr size_t kNumKeys = 10000;
1434
1435 for (size_t k = 0; k < kNumKeys; ++k) {
1436 c.Add("key" + ToString(k), "val" + ToString(k));
1437 }
1438
1439 std::vector<std::string> keys;
1440 stl_wrappers::KVMap kvmap;
1441 Options options;
1442 options.compression = kSnappyCompression;
1443 options.statistics = CreateDBStatistics();
494da23a 1444 options.statistics->set_stats_level(StatsLevel::kAll);
11fdf7f2
TL
1445 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
1446 table_options.block_restart_interval = 1;
1447 table_options.enable_index_compression = compressed;
1448 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1449
1450 ImmutableCFOptions ioptions(options);
1451 MutableCFOptions moptions(options);
1452 ioptions.statistics = options.statistics.get();
1453 c.Finish(options, ioptions, moptions, table_options,
1454 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1455 c.ResetTableReader();
1456 return options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
1457}
1458TEST_P(BlockBasedTableTest, IndexUncompressed) {
1459 uint64_t tbl1_compressed_cnt = IndexUncompressedHelper(true);
1460 uint64_t tbl2_compressed_cnt = IndexUncompressedHelper(false);
1461 // tbl1_compressed_cnt should include 1 index block
1462 EXPECT_EQ(tbl2_compressed_cnt + 1, tbl1_compressed_cnt);
1463}
1464#endif // SNAPPY
1465
1466TEST_P(BlockBasedTableTest, BlockBasedTableProperties2) {
7c673cae
FG
1467 TableConstructor c(&reverse_key_comparator);
1468 std::vector<std::string> keys;
1469 stl_wrappers::KVMap kvmap;
1470
1471 {
1472 Options options;
1473 options.compression = CompressionType::kNoCompression;
11fdf7f2 1474 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1475 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1476
1477 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
1478 const MutableCFOptions moptions(options);
1479 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
1480 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1481
1482 auto& props = *c.GetTableReader()->GetTableProperties();
1483
1484 // Default comparator
1485 ASSERT_EQ("leveldb.BytewiseComparator", props.comparator_name);
1486 // No merge operator
1487 ASSERT_EQ("nullptr", props.merge_operator_name);
1488 // No prefix extractor
1489 ASSERT_EQ("nullptr", props.prefix_extractor_name);
1490 // No property collectors
1491 ASSERT_EQ("[]", props.property_collectors_names);
1492 // No filter policy is used
1493 ASSERT_EQ("", props.filter_policy_name);
1494 // Compression type == that set:
1495 ASSERT_EQ("NoCompression", props.compression_name);
1496 c.ResetTableReader();
1497 }
1498
1499 {
1500 Options options;
11fdf7f2 1501 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1502 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1503 options.comparator = &reverse_key_comparator;
1504 options.merge_operator = MergeOperators::CreateUInt64AddOperator();
1505 options.prefix_extractor.reset(NewNoopTransform());
1506 options.table_properties_collector_factories.emplace_back(
1507 new DummyPropertiesCollectorFactory1());
1508 options.table_properties_collector_factories.emplace_back(
1509 new DummyPropertiesCollectorFactory2());
1510
1511 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
1512 const MutableCFOptions moptions(options);
1513 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
1514 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1515
1516 auto& props = *c.GetTableReader()->GetTableProperties();
1517
1518 ASSERT_EQ("rocksdb.ReverseBytewiseComparator", props.comparator_name);
1519 ASSERT_EQ("UInt64AddOperator", props.merge_operator_name);
1520 ASSERT_EQ("rocksdb.Noop", props.prefix_extractor_name);
20effc67
TL
1521 ASSERT_EQ(
1522 "[DummyPropertiesCollectorFactory1,DummyPropertiesCollectorFactory2]",
1523 props.property_collectors_names);
7c673cae
FG
1524 ASSERT_EQ("", props.filter_policy_name); // no filter policy is used
1525 c.ResetTableReader();
1526 }
1527}
1528
11fdf7f2 1529TEST_P(BlockBasedTableTest, RangeDelBlock) {
7c673cae
FG
1530 TableConstructor c(BytewiseComparator());
1531 std::vector<std::string> keys = {"1pika", "2chu"};
1532 std::vector<std::string> vals = {"p", "c"};
1533
494da23a
TL
1534 std::vector<RangeTombstone> expected_tombstones = {
1535 {"1pika", "2chu", 0},
1536 {"2chu", "c", 1},
1537 {"2chu", "c", 0},
1538 {"c", "p", 0},
1539 };
1540
7c673cae
FG
1541 for (int i = 0; i < 2; i++) {
1542 RangeTombstone t(keys[i], vals[i], i);
1543 std::pair<InternalKey, Slice> p = t.Serialize();
1544 c.Add(p.first.Encode().ToString(), p.second);
1545 }
1546
1547 std::vector<std::string> sorted_keys;
1548 stl_wrappers::KVMap kvmap;
1549 Options options;
1550 options.compression = kNoCompression;
11fdf7f2 1551 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1552 table_options.block_restart_interval = 1;
1553 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1554
1555 const ImmutableCFOptions ioptions(options);
11fdf7f2 1556 const MutableCFOptions moptions(options);
7c673cae
FG
1557 std::unique_ptr<InternalKeyComparator> internal_cmp(
1558 new InternalKeyComparator(options.comparator));
11fdf7f2
TL
1559 c.Finish(options, ioptions, moptions, table_options, *internal_cmp,
1560 &sorted_keys, &kvmap);
7c673cae
FG
1561
1562 for (int j = 0; j < 2; ++j) {
1563 std::unique_ptr<InternalIterator> iter(
1564 c.GetTableReader()->NewRangeTombstoneIterator(ReadOptions()));
1565 if (j > 0) {
1566 // For second iteration, delete the table reader object and verify the
1567 // iterator can still access its metablock's range tombstones.
1568 c.ResetTableReader();
1569 }
1570 ASSERT_FALSE(iter->Valid());
1571 iter->SeekToFirst();
1572 ASSERT_TRUE(iter->Valid());
494da23a 1573 for (size_t i = 0; i < expected_tombstones.size(); i++) {
7c673cae
FG
1574 ASSERT_TRUE(iter->Valid());
1575 ParsedInternalKey parsed_key;
20effc67
TL
1576 ASSERT_OK(
1577 ParseInternalKey(iter->key(), &parsed_key, true /* log_err_key */));
7c673cae 1578 RangeTombstone t(parsed_key, iter->value());
494da23a
TL
1579 const auto& expected_t = expected_tombstones[i];
1580 ASSERT_EQ(t.start_key_, expected_t.start_key_);
1581 ASSERT_EQ(t.end_key_, expected_t.end_key_);
1582 ASSERT_EQ(t.seq_, expected_t.seq_);
7c673cae
FG
1583 iter->Next();
1584 }
1585 ASSERT_TRUE(!iter->Valid());
1586 }
1587}
1588
11fdf7f2 1589TEST_P(BlockBasedTableTest, FilterPolicyNameProperties) {
7c673cae
FG
1590 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1591 c.Add("a1", "val1");
1592 std::vector<std::string> keys;
1593 stl_wrappers::KVMap kvmap;
11fdf7f2 1594 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1595 table_options.filter_policy.reset(NewBloomFilterPolicy(10));
1596 Options options;
1597 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1598
1599 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
1600 const MutableCFOptions moptions(options);
1601 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
1602 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1603 auto& props = *c.GetTableReader()->GetTableProperties();
1604 ASSERT_EQ("rocksdb.BuiltinBloomFilter", props.filter_policy_name);
1605 c.ResetTableReader();
1606}
1607
1608//
1609// BlockBasedTableTest::PrefetchTest
1610//
1611void AssertKeysInCache(BlockBasedTable* table_reader,
1612 const std::vector<std::string>& keys_in_cache,
1613 const std::vector<std::string>& keys_not_in_cache,
1614 bool convert = false) {
1615 if (convert) {
1616 for (auto key : keys_in_cache) {
1617 InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
1618 ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
1619 }
1620 for (auto key : keys_not_in_cache) {
1621 InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
1622 ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
1623 }
1624 } else {
1625 for (auto key : keys_in_cache) {
1626 ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), key));
1627 }
1628 for (auto key : keys_not_in_cache) {
1629 ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), key));
1630 }
1631 }
1632}
1633
1634void PrefetchRange(TableConstructor* c, Options* opt,
1635 BlockBasedTableOptions* table_options, const char* key_begin,
1636 const char* key_end,
1637 const std::vector<std::string>& keys_in_cache,
1638 const std::vector<std::string>& keys_not_in_cache,
1639 const Status expected_status = Status::OK()) {
1640 // reset the cache and reopen the table
1641 table_options->block_cache = NewLRUCache(16 * 1024 * 1024, 4);
1642 opt->table_factory.reset(NewBlockBasedTableFactory(*table_options));
1643 const ImmutableCFOptions ioptions2(*opt);
11fdf7f2
TL
1644 const MutableCFOptions moptions(*opt);
1645 ASSERT_OK(c->Reopen(ioptions2, moptions));
7c673cae
FG
1646
1647 // prefetch
1648 auto* table_reader = dynamic_cast<BlockBasedTable*>(c->GetTableReader());
1649 Status s;
494da23a
TL
1650 std::unique_ptr<Slice> begin, end;
1651 std::unique_ptr<InternalKey> i_begin, i_end;
7c673cae
FG
1652 if (key_begin != nullptr) {
1653 if (c->ConvertToInternalKey()) {
1654 i_begin.reset(new InternalKey(key_begin, kMaxSequenceNumber, kTypeValue));
1655 begin.reset(new Slice(i_begin->Encode()));
1656 } else {
1657 begin.reset(new Slice(key_begin));
1658 }
1659 }
1660 if (key_end != nullptr) {
1661 if (c->ConvertToInternalKey()) {
1662 i_end.reset(new InternalKey(key_end, kMaxSequenceNumber, kTypeValue));
1663 end.reset(new Slice(i_end->Encode()));
1664 } else {
1665 end.reset(new Slice(key_end));
1666 }
1667 }
1668 s = table_reader->Prefetch(begin.get(), end.get());
1669
1670 ASSERT_TRUE(s.code() == expected_status.code());
1671
1672 // assert our expectation in cache warmup
1673 AssertKeysInCache(table_reader, keys_in_cache, keys_not_in_cache,
1674 c->ConvertToInternalKey());
1675 c->ResetTableReader();
1676}
1677
11fdf7f2 1678TEST_P(BlockBasedTableTest, PrefetchTest) {
7c673cae
FG
1679 // The purpose of this test is to test the prefetching operation built into
1680 // BlockBasedTable.
1681 Options opt;
494da23a 1682 std::unique_ptr<InternalKeyComparator> ikc;
7c673cae
FG
1683 ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
1684 opt.compression = kNoCompression;
11fdf7f2 1685 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1686 table_options.block_size = 1024;
1687 // big enough so we don't ever lose cached values.
1688 table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
1689 opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
1690
1691 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1692 c.Add("k01", "hello");
1693 c.Add("k02", "hello2");
1694 c.Add("k03", std::string(10000, 'x'));
1695 c.Add("k04", std::string(200000, 'x'));
1696 c.Add("k05", std::string(300000, 'x'));
1697 c.Add("k06", "hello3");
1698 c.Add("k07", std::string(100000, 'x'));
1699 std::vector<std::string> keys;
1700 stl_wrappers::KVMap kvmap;
1701 const ImmutableCFOptions ioptions(opt);
11fdf7f2
TL
1702 const MutableCFOptions moptions(opt);
1703 c.Finish(opt, ioptions, moptions, table_options, *ikc, &keys, &kvmap);
7c673cae
FG
1704 c.ResetTableReader();
1705
1706 // We get the following data spread :
1707 //
1708 // Data block Index
1709 // ========================
1710 // [ k01 k02 k03 ] k03
1711 // [ k04 ] k04
1712 // [ k05 ] k05
1713 // [ k06 k07 ] k07
1714
1715
1716 // Simple
1717 PrefetchRange(&c, &opt, &table_options,
1718 /*key_range=*/"k01", "k05",
1719 /*keys_in_cache=*/{"k01", "k02", "k03", "k04", "k05"},
1720 /*keys_not_in_cache=*/{"k06", "k07"});
1721 PrefetchRange(&c, &opt, &table_options, "k01", "k01", {"k01", "k02", "k03"},
1722 {"k04", "k05", "k06", "k07"});
1723 // odd
1724 PrefetchRange(&c, &opt, &table_options, "a", "z",
1725 {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
1726 PrefetchRange(&c, &opt, &table_options, "k00", "k00", {"k01", "k02", "k03"},
1727 {"k04", "k05", "k06", "k07"});
1728 // Edge cases
1729 PrefetchRange(&c, &opt, &table_options, "k00", "k06",
1730 {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
1731 PrefetchRange(&c, &opt, &table_options, "k00", "zzz",
1732 {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
1733 // null keys
1734 PrefetchRange(&c, &opt, &table_options, nullptr, nullptr,
1735 {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
1736 PrefetchRange(&c, &opt, &table_options, "k04", nullptr,
1737 {"k04", "k05", "k06", "k07"}, {"k01", "k02", "k03"});
1738 PrefetchRange(&c, &opt, &table_options, nullptr, "k05",
1739 {"k01", "k02", "k03", "k04", "k05"}, {"k06", "k07"});
1740 // invalid
1741 PrefetchRange(&c, &opt, &table_options, "k06", "k00", {}, {},
1742 Status::InvalidArgument(Slice("k06 "), Slice("k07")));
1743 c.ResetTableReader();
1744}
1745
11fdf7f2
TL
1746TEST_P(BlockBasedTableTest, TotalOrderSeekOnHashIndex) {
1747 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
f67539c2 1748 for (int i = 0; i <= 5; ++i) {
7c673cae
FG
1749 Options options;
1750 // Make each key/value an individual block
1751 table_options.block_size = 64;
1752 switch (i) {
1753 case 0:
1754 // Binary search index
1755 table_options.index_type = BlockBasedTableOptions::kBinarySearch;
1756 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1757 break;
1758 case 1:
1759 // Hash search index
1760 table_options.index_type = BlockBasedTableOptions::kHashSearch;
1761 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1762 options.prefix_extractor.reset(NewFixedPrefixTransform(4));
1763 break;
1764 case 2:
1765 // Hash search index with hash_index_allow_collision
1766 table_options.index_type = BlockBasedTableOptions::kHashSearch;
1767 table_options.hash_index_allow_collision = true;
1768 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1769 options.prefix_extractor.reset(NewFixedPrefixTransform(4));
1770 break;
1771 case 3:
1772 // Hash search index with filter policy
1773 table_options.index_type = BlockBasedTableOptions::kHashSearch;
1774 table_options.filter_policy.reset(NewBloomFilterPolicy(10));
1775 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1776 options.prefix_extractor.reset(NewFixedPrefixTransform(4));
1777 break;
1778 case 4:
f67539c2 1779 // Two-level index
7c673cae
FG
1780 table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
1781 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1782 break;
f67539c2
TL
1783 case 5:
1784 // Binary search with first key
1785 table_options.index_type =
1786 BlockBasedTableOptions::kBinarySearchWithFirstKey;
1787 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1788 break;
7c673cae
FG
1789 }
1790
1791 TableConstructor c(BytewiseComparator(),
1792 true /* convert_to_internal_key_ */);
1793 c.Add("aaaa1", std::string('a', 56));
1794 c.Add("bbaa1", std::string('a', 56));
1795 c.Add("cccc1", std::string('a', 56));
1796 c.Add("bbbb1", std::string('a', 56));
1797 c.Add("baaa1", std::string('a', 56));
1798 c.Add("abbb1", std::string('a', 56));
1799 c.Add("cccc2", std::string('a', 56));
1800 std::vector<std::string> keys;
1801 stl_wrappers::KVMap kvmap;
1802 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
1803 const MutableCFOptions moptions(options);
1804 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
1805 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1806 auto props = c.GetTableReader()->GetTableProperties();
1807 ASSERT_EQ(7u, props->num_data_blocks);
1808 auto* reader = c.GetTableReader();
1809 ReadOptions ro;
1810 ro.total_order_seek = true;
f67539c2
TL
1811 std::unique_ptr<InternalIterator> iter(reader->NewIterator(
1812 ro, moptions.prefix_extractor.get(), /*arena=*/nullptr,
1813 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
7c673cae
FG
1814
1815 iter->Seek(InternalKey("b", 0, kTypeValue).Encode());
1816 ASSERT_OK(iter->status());
1817 ASSERT_TRUE(iter->Valid());
1818 ASSERT_EQ("baaa1", ExtractUserKey(iter->key()).ToString());
1819 iter->Next();
1820 ASSERT_OK(iter->status());
1821 ASSERT_TRUE(iter->Valid());
1822 ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
1823
1824 iter->Seek(InternalKey("bb", 0, kTypeValue).Encode());
1825 ASSERT_OK(iter->status());
1826 ASSERT_TRUE(iter->Valid());
1827 ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
1828 iter->Next();
1829 ASSERT_OK(iter->status());
1830 ASSERT_TRUE(iter->Valid());
1831 ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
1832
1833 iter->Seek(InternalKey("bbb", 0, kTypeValue).Encode());
1834 ASSERT_OK(iter->status());
1835 ASSERT_TRUE(iter->Valid());
1836 ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
1837 iter->Next();
1838 ASSERT_OK(iter->status());
1839 ASSERT_TRUE(iter->Valid());
1840 ASSERT_EQ("cccc1", ExtractUserKey(iter->key()).ToString());
1841 }
1842}
1843
11fdf7f2
TL
1844TEST_P(BlockBasedTableTest, NoopTransformSeek) {
1845 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1846 table_options.filter_policy.reset(NewBloomFilterPolicy(10));
1847
1848 Options options;
1849 options.comparator = BytewiseComparator();
1850 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1851 options.prefix_extractor.reset(NewNoopTransform());
1852
1853 TableConstructor c(options.comparator);
1854 // To tickle the PrefixMayMatch bug it is important that the
1855 // user-key is a single byte so that the index key exactly matches
1856 // the user-key.
1857 InternalKey key("a", 1, kTypeValue);
1858 c.Add(key.Encode().ToString(), "b");
1859 std::vector<std::string> keys;
1860 stl_wrappers::KVMap kvmap;
1861 const ImmutableCFOptions ioptions(options);
11fdf7f2 1862 const MutableCFOptions moptions(options);
7c673cae 1863 const InternalKeyComparator internal_comparator(options.comparator);
11fdf7f2
TL
1864 c.Finish(options, ioptions, moptions, table_options, internal_comparator,
1865 &keys, &kvmap);
7c673cae
FG
1866
1867 auto* reader = c.GetTableReader();
1868 for (int i = 0; i < 2; ++i) {
1869 ReadOptions ro;
1870 ro.total_order_seek = (i == 0);
f67539c2
TL
1871 std::unique_ptr<InternalIterator> iter(reader->NewIterator(
1872 ro, moptions.prefix_extractor.get(), /*arena=*/nullptr,
1873 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
7c673cae
FG
1874
1875 iter->Seek(key.Encode());
1876 ASSERT_OK(iter->status());
1877 ASSERT_TRUE(iter->Valid());
1878 ASSERT_EQ("a", ExtractUserKey(iter->key()).ToString());
1879 }
1880}
1881
11fdf7f2 1882TEST_P(BlockBasedTableTest, SkipPrefixBloomFilter) {
7c673cae
FG
1883 // if DB is opened with a prefix extractor of a different name,
1884 // prefix bloom is skipped when read the file
11fdf7f2 1885 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
1886 table_options.filter_policy.reset(NewBloomFilterPolicy(2));
1887 table_options.whole_key_filtering = false;
1888
1889 Options options;
1890 options.comparator = BytewiseComparator();
1891 options.table_factory.reset(new BlockBasedTableFactory(table_options));
1892 options.prefix_extractor.reset(NewFixedPrefixTransform(1));
1893
1894 TableConstructor c(options.comparator);
1895 InternalKey key("abcdefghijk", 1, kTypeValue);
1896 c.Add(key.Encode().ToString(), "test");
1897 std::vector<std::string> keys;
1898 stl_wrappers::KVMap kvmap;
1899 const ImmutableCFOptions ioptions(options);
11fdf7f2 1900 const MutableCFOptions moptions(options);
7c673cae 1901 const InternalKeyComparator internal_comparator(options.comparator);
11fdf7f2
TL
1902 c.Finish(options, ioptions, moptions, table_options, internal_comparator,
1903 &keys, &kvmap);
1904 // TODO(Zhongyi): update test to use MutableCFOptions
7c673cae
FG
1905 options.prefix_extractor.reset(NewFixedPrefixTransform(9));
1906 const ImmutableCFOptions new_ioptions(options);
11fdf7f2 1907 const MutableCFOptions new_moptions(options);
20effc67 1908 ASSERT_OK(c.Reopen(new_ioptions, new_moptions));
7c673cae 1909 auto reader = c.GetTableReader();
20effc67 1910 ReadOptions read_options;
f67539c2 1911 std::unique_ptr<InternalIterator> db_iter(reader->NewIterator(
20effc67 1912 read_options, new_moptions.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2 1913 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
7c673cae
FG
1914
1915 // Test point lookup
1916 // only one kv
1917 for (auto& kv : kvmap) {
1918 db_iter->Seek(kv.first);
1919 ASSERT_TRUE(db_iter->Valid());
1920 ASSERT_OK(db_iter->status());
1921 ASSERT_EQ(db_iter->key(), kv.first);
1922 ASSERT_EQ(db_iter->value(), kv.second);
1923 }
1924}
1925
7c673cae 1926void AddInternalKey(TableConstructor* c, const std::string& prefix,
f67539c2 1927 std::string value = "v", int /*suffix_len*/ = 800) {
7c673cae 1928 static Random rnd(1023);
20effc67 1929 InternalKey k(prefix + rnd.RandomString(800), 0, kTypeValue);
f67539c2 1930 c->Add(k.Encode().ToString(), value);
7c673cae
FG
1931}
1932
1933void TableTest::IndexTest(BlockBasedTableOptions table_options) {
1934 TableConstructor c(BytewiseComparator());
1935
1936 // keys with prefix length 3, make sure the key/value is big enough to fill
1937 // one block
1938 AddInternalKey(&c, "0015");
1939 AddInternalKey(&c, "0035");
1940
1941 AddInternalKey(&c, "0054");
1942 AddInternalKey(&c, "0055");
1943
1944 AddInternalKey(&c, "0056");
1945 AddInternalKey(&c, "0057");
1946
1947 AddInternalKey(&c, "0058");
1948 AddInternalKey(&c, "0075");
1949
1950 AddInternalKey(&c, "0076");
1951 AddInternalKey(&c, "0095");
1952
1953 std::vector<std::string> keys;
1954 stl_wrappers::KVMap kvmap;
1955 Options options;
1956 options.prefix_extractor.reset(NewFixedPrefixTransform(3));
1957 table_options.block_size = 1700;
1958 table_options.block_cache = NewLRUCache(1024, 4);
1959 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1960
1961 std::unique_ptr<InternalKeyComparator> comparator(
1962 new InternalKeyComparator(BytewiseComparator()));
1963 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
1964 const MutableCFOptions moptions(options);
1965 c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
1966 &kvmap);
7c673cae
FG
1967 auto reader = c.GetTableReader();
1968
1969 auto props = reader->GetTableProperties();
1970 ASSERT_EQ(5u, props->num_data_blocks);
1971
11fdf7f2 1972 // TODO(Zhongyi): update test to use MutableCFOptions
20effc67 1973 ReadOptions read_options;
f67539c2 1974 std::unique_ptr<InternalIterator> index_iter(reader->NewIterator(
20effc67 1975 read_options, moptions.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2 1976 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
7c673cae
FG
1977
1978 // -- Find keys do not exist, but have common prefix.
1979 std::vector<std::string> prefixes = {"001", "003", "005", "007", "009"};
20effc67
TL
1980 std::vector<std::string> lower_bound = {
1981 keys[0], keys[1], keys[2], keys[7], keys[9],
1982 };
7c673cae
FG
1983
1984 // find the lower bound of the prefix
1985 for (size_t i = 0; i < prefixes.size(); ++i) {
1986 index_iter->Seek(InternalKey(prefixes[i], 0, kTypeValue).Encode());
1987 ASSERT_OK(index_iter->status());
1988 ASSERT_TRUE(index_iter->Valid());
1989
1990 // seek the first element in the block
1991 ASSERT_EQ(lower_bound[i], index_iter->key().ToString());
1992 ASSERT_EQ("v", index_iter->value().ToString());
1993 }
1994
1995 // find the upper bound of prefixes
1996 std::vector<std::string> upper_bound = {keys[1], keys[2], keys[7], keys[9], };
1997
1998 // find existing keys
1999 for (const auto& item : kvmap) {
2000 auto ukey = ExtractUserKey(item.first).ToString();
2001 index_iter->Seek(ukey);
2002
2003 // ASSERT_OK(regular_iter->status());
2004 ASSERT_OK(index_iter->status());
2005
2006 // ASSERT_TRUE(regular_iter->Valid());
2007 ASSERT_TRUE(index_iter->Valid());
2008
2009 ASSERT_EQ(item.first, index_iter->key().ToString());
2010 ASSERT_EQ(item.second, index_iter->value().ToString());
2011 }
2012
2013 for (size_t i = 0; i < prefixes.size(); ++i) {
2014 // the key is greater than any existing keys.
2015 auto key = prefixes[i] + "9";
2016 index_iter->Seek(InternalKey(key, 0, kTypeValue).Encode());
2017
f67539c2
TL
2018 ASSERT_TRUE(index_iter->status().ok() || index_iter->status().IsNotFound());
2019 ASSERT_TRUE(!index_iter->status().IsNotFound() || !index_iter->Valid());
7c673cae
FG
2020 if (i == prefixes.size() - 1) {
2021 // last key
2022 ASSERT_TRUE(!index_iter->Valid());
2023 } else {
2024 ASSERT_TRUE(index_iter->Valid());
2025 // seek the first element in the block
2026 ASSERT_EQ(upper_bound[i], index_iter->key().ToString());
2027 ASSERT_EQ("v", index_iter->value().ToString());
2028 }
2029 }
2030
2031 // find keys with prefix that don't match any of the existing prefixes.
2032 std::vector<std::string> non_exist_prefixes = {"002", "004", "006", "008"};
2033 for (const auto& prefix : non_exist_prefixes) {
2034 index_iter->Seek(InternalKey(prefix, 0, kTypeValue).Encode());
2035 // regular_iter->Seek(prefix);
2036
2037 ASSERT_OK(index_iter->status());
2038 // Seek to non-existing prefixes should yield either invalid, or a
2039 // key with prefix greater than the target.
2040 if (index_iter->Valid()) {
2041 Slice ukey = ExtractUserKey(index_iter->key());
2042 Slice ukey_prefix = options.prefix_extractor->Transform(ukey);
2043 ASSERT_TRUE(BytewiseComparator()->Compare(prefix, ukey_prefix) < 0);
2044 }
2045 }
f67539c2
TL
2046 for (const auto& prefix : non_exist_prefixes) {
2047 index_iter->SeekForPrev(InternalKey(prefix, 0, kTypeValue).Encode());
2048 // regular_iter->Seek(prefix);
2049
2050 ASSERT_OK(index_iter->status());
2051 // Seek to non-existing prefixes should yield either invalid, or a
2052 // key with prefix greater than the target.
2053 if (index_iter->Valid()) {
2054 Slice ukey = ExtractUserKey(index_iter->key());
2055 Slice ukey_prefix = options.prefix_extractor->Transform(ukey);
2056 ASSERT_TRUE(BytewiseComparator()->Compare(prefix, ukey_prefix) > 0);
2057 }
2058 }
20effc67
TL
2059
2060 {
2061 // Test reseek case. It should impact partitioned index more.
2062 ReadOptions ro;
2063 ro.total_order_seek = true;
2064 std::unique_ptr<InternalIterator> index_iter2(reader->NewIterator(
2065 ro, moptions.prefix_extractor.get(), /*arena=*/nullptr,
2066 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
2067
2068 // Things to cover in partitioned index:
2069 // 1. Both of Seek() and SeekToLast() has optimization to prevent
2070 // rereek leaf index block if it remains to the same one, and
2071 // they reuse the same variable.
2072 // 2. When Next() or Prev() is called, the block moves, so the
2073 // optimization should kick in only with the current one.
2074 index_iter2->Seek(InternalKey("0055", 0, kTypeValue).Encode());
2075 ASSERT_TRUE(index_iter2->Valid());
2076 ASSERT_EQ("0055", index_iter2->key().ToString().substr(0, 4));
2077
2078 index_iter2->SeekToLast();
2079 ASSERT_TRUE(index_iter2->Valid());
2080 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2081
2082 index_iter2->Seek(InternalKey("0055", 0, kTypeValue).Encode());
2083 ASSERT_TRUE(index_iter2->Valid());
2084 ASSERT_EQ("0055", index_iter2->key().ToString().substr(0, 4));
2085
2086 index_iter2->SeekToLast();
2087 ASSERT_TRUE(index_iter2->Valid());
2088 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2089 index_iter2->Prev();
2090 ASSERT_TRUE(index_iter2->Valid());
2091 index_iter2->Prev();
2092 ASSERT_TRUE(index_iter2->Valid());
2093 ASSERT_EQ("0075", index_iter2->key().ToString().substr(0, 4));
2094
2095 index_iter2->Seek(InternalKey("0095", 0, kTypeValue).Encode());
2096 ASSERT_TRUE(index_iter2->Valid());
2097 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2098 index_iter2->Prev();
2099 ASSERT_TRUE(index_iter2->Valid());
2100 index_iter2->Prev();
2101 ASSERT_TRUE(index_iter2->Valid());
2102 ASSERT_EQ("0075", index_iter2->key().ToString().substr(0, 4));
2103
2104 index_iter2->SeekToLast();
2105 ASSERT_TRUE(index_iter2->Valid());
2106 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2107
2108 index_iter2->Seek(InternalKey("0095", 0, kTypeValue).Encode());
2109 ASSERT_TRUE(index_iter2->Valid());
2110 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2111
2112 index_iter2->Prev();
2113 ASSERT_TRUE(index_iter2->Valid());
2114 index_iter2->Prev();
2115 ASSERT_TRUE(index_iter2->Valid());
2116 ASSERT_EQ("0075", index_iter2->key().ToString().substr(0, 4));
2117
2118 index_iter2->Seek(InternalKey("0075", 0, kTypeValue).Encode());
2119 ASSERT_TRUE(index_iter2->Valid());
2120 ASSERT_EQ("0075", index_iter2->key().ToString().substr(0, 4));
2121
2122 index_iter2->Next();
2123 ASSERT_TRUE(index_iter2->Valid());
2124 index_iter2->Next();
2125 ASSERT_TRUE(index_iter2->Valid());
2126 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2127
2128 index_iter2->SeekToLast();
2129 ASSERT_TRUE(index_iter2->Valid());
2130 ASSERT_EQ("0095", index_iter2->key().ToString().substr(0, 4));
2131 }
2132
7c673cae
FG
2133 c.ResetTableReader();
2134}
2135
11fdf7f2
TL
2136TEST_P(BlockBasedTableTest, BinaryIndexTest) {
2137 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
2138 table_options.index_type = BlockBasedTableOptions::kBinarySearch;
2139 IndexTest(table_options);
2140}
2141
11fdf7f2
TL
2142TEST_P(BlockBasedTableTest, HashIndexTest) {
2143 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
2144 table_options.index_type = BlockBasedTableOptions::kHashSearch;
2145 IndexTest(table_options);
2146}
2147
11fdf7f2 2148TEST_P(BlockBasedTableTest, PartitionIndexTest) {
7c673cae
FG
2149 const int max_index_keys = 5;
2150 const int est_max_index_key_value_size = 32;
2151 const int est_max_index_size = max_index_keys * est_max_index_key_value_size;
2152 for (int i = 1; i <= est_max_index_size + 1; i++) {
11fdf7f2 2153 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
2154 table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
2155 table_options.metadata_block_size = i;
2156 IndexTest(table_options);
2157 }
2158}
2159
f67539c2
TL
2160TEST_P(BlockBasedTableTest, IndexSeekOptimizationIncomplete) {
2161 std::unique_ptr<InternalKeyComparator> comparator(
2162 new InternalKeyComparator(BytewiseComparator()));
2163 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2164 Options options;
2165 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
2166 const ImmutableCFOptions ioptions(options);
2167 const MutableCFOptions moptions(options);
2168
2169 TableConstructor c(BytewiseComparator());
2170 AddInternalKey(&c, "pika");
2171
2172 std::vector<std::string> keys;
2173 stl_wrappers::KVMap kvmap;
2174 c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
2175 &kvmap);
2176 ASSERT_EQ(1, keys.size());
2177
2178 auto reader = c.GetTableReader();
2179 ReadOptions ropt;
2180 ropt.read_tier = ReadTier::kBlockCacheTier;
2181 std::unique_ptr<InternalIterator> iter(reader->NewIterator(
2182 ropt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
2183 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
2184
2185 auto ikey = [](Slice user_key) {
2186 return InternalKey(user_key, 0, kTypeValue).Encode().ToString();
2187 };
2188
2189 iter->Seek(ikey("pika"));
2190 ASSERT_FALSE(iter->Valid());
2191 ASSERT_TRUE(iter->status().IsIncomplete());
2192
2193 // This used to crash at some point.
2194 iter->Seek(ikey("pika"));
2195 ASSERT_FALSE(iter->Valid());
2196 ASSERT_TRUE(iter->status().IsIncomplete());
2197}
2198
2199TEST_P(BlockBasedTableTest, BinaryIndexWithFirstKey1) {
2200 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2201 table_options.index_type = BlockBasedTableOptions::kBinarySearchWithFirstKey;
2202 IndexTest(table_options);
2203}
2204
2205class CustomFlushBlockPolicy : public FlushBlockPolicyFactory,
2206 public FlushBlockPolicy {
2207 public:
2208 explicit CustomFlushBlockPolicy(std::vector<int> keys_per_block)
2209 : keys_per_block_(keys_per_block) {}
2210
20effc67
TL
2211 const char* Name() const override { return "CustomFlushBlockPolicy"; }
2212
f67539c2
TL
2213 FlushBlockPolicy* NewFlushBlockPolicy(const BlockBasedTableOptions&,
2214 const BlockBuilder&) const override {
2215 return new CustomFlushBlockPolicy(keys_per_block_);
2216 }
2217
2218 bool Update(const Slice&, const Slice&) override {
2219 if (keys_in_current_block_ >= keys_per_block_.at(current_block_idx_)) {
2220 ++current_block_idx_;
2221 keys_in_current_block_ = 1;
2222 return true;
2223 }
2224
2225 ++keys_in_current_block_;
2226 return false;
2227 }
2228
2229 std::vector<int> keys_per_block_;
2230
2231 int current_block_idx_ = 0;
2232 int keys_in_current_block_ = 0;
2233};
2234
2235TEST_P(BlockBasedTableTest, BinaryIndexWithFirstKey2) {
2236 for (int use_first_key = 0; use_first_key < 2; ++use_first_key) {
2237 SCOPED_TRACE("use_first_key = " + std::to_string(use_first_key));
2238 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2239 table_options.index_type =
2240 use_first_key ? BlockBasedTableOptions::kBinarySearchWithFirstKey
2241 : BlockBasedTableOptions::kBinarySearch;
2242 table_options.block_cache = NewLRUCache(10000); // fits all blocks
2243 table_options.index_shortening =
2244 BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
2245 table_options.flush_block_policy_factory =
2246 std::make_shared<CustomFlushBlockPolicy>(std::vector<int>{2, 1, 3, 2});
2247 Options options;
2248 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
2249 options.statistics = CreateDBStatistics();
2250 Statistics* stats = options.statistics.get();
2251 std::unique_ptr<InternalKeyComparator> comparator(
2252 new InternalKeyComparator(BytewiseComparator()));
2253 const ImmutableCFOptions ioptions(options);
2254 const MutableCFOptions moptions(options);
2255
2256 TableConstructor c(BytewiseComparator());
2257
2258 // Block 0.
2259 AddInternalKey(&c, "aaaa", "v0");
2260 AddInternalKey(&c, "aaac", "v1");
2261
2262 // Block 1.
2263 AddInternalKey(&c, "aaca", "v2");
2264
2265 // Block 2.
2266 AddInternalKey(&c, "caaa", "v3");
2267 AddInternalKey(&c, "caac", "v4");
2268 AddInternalKey(&c, "caae", "v5");
2269
2270 // Block 3.
2271 AddInternalKey(&c, "ccaa", "v6");
2272 AddInternalKey(&c, "ccac", "v7");
2273
2274 // Write the file.
2275 std::vector<std::string> keys;
2276 stl_wrappers::KVMap kvmap;
2277 c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
2278 &kvmap);
2279 ASSERT_EQ(8, keys.size());
2280
2281 auto reader = c.GetTableReader();
2282 auto props = reader->GetTableProperties();
2283 ASSERT_EQ(4u, props->num_data_blocks);
20effc67 2284 ReadOptions read_options;
f67539c2 2285 std::unique_ptr<InternalIterator> iter(reader->NewIterator(
20effc67
TL
2286 read_options, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
2287 /*skip_filters=*/false, TableReaderCaller::kUncategorized,
2288 /*compaction_readahead_size=*/0, /*allow_unprepared_value=*/true));
f67539c2
TL
2289
2290 // Shouldn't have read data blocks before iterator is seeked.
2291 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2292 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2293
2294 auto ikey = [](Slice user_key) {
2295 return InternalKey(user_key, 0, kTypeValue).Encode().ToString();
2296 };
2297
2298 // Seek to a key between blocks. If index contains first key, we shouldn't
2299 // read any data blocks until value is requested.
2300 iter->Seek(ikey("aaba"));
2301 ASSERT_TRUE(iter->Valid());
2302 EXPECT_EQ(keys[2], iter->key().ToString());
2303 EXPECT_EQ(use_first_key ? 0 : 1,
2304 stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
20effc67 2305 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2306 EXPECT_EQ("v2", iter->value().ToString());
2307 EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2308 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2309
2310 // Seek to the middle of a block. The block should be read right away.
2311 iter->Seek(ikey("caab"));
2312 ASSERT_TRUE(iter->Valid());
2313 EXPECT_EQ(keys[4], iter->key().ToString());
2314 EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2315 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
20effc67 2316 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2317 EXPECT_EQ("v4", iter->value().ToString());
2318 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2319
2320 // Seek to just before the same block and don't access value.
2321 // The iterator should keep pinning the block contents.
2322 iter->Seek(ikey("baaa"));
2323 ASSERT_TRUE(iter->Valid());
2324 EXPECT_EQ(keys[3], iter->key().ToString());
2325 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2326
2327 // Seek to the same block again to check that the block is still pinned.
2328 iter->Seek(ikey("caae"));
2329 ASSERT_TRUE(iter->Valid());
2330 EXPECT_EQ(keys[5], iter->key().ToString());
2331 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
20effc67 2332 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2333 EXPECT_EQ("v5", iter->value().ToString());
2334 EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2335 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2336
2337 // Step forward and fall through to the next block. Don't access value.
2338 iter->Next();
2339 ASSERT_TRUE(iter->Valid());
2340 EXPECT_EQ(keys[6], iter->key().ToString());
2341 EXPECT_EQ(use_first_key ? 2 : 3,
2342 stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2343 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2344
2345 // Step forward again. Block should be read.
2346 iter->Next();
2347 ASSERT_TRUE(iter->Valid());
2348 EXPECT_EQ(keys[7], iter->key().ToString());
2349 EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
20effc67 2350 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2351 EXPECT_EQ("v7", iter->value().ToString());
2352 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2353
2354 // Step forward and reach the end.
2355 iter->Next();
2356 EXPECT_FALSE(iter->Valid());
2357 EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2358 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2359
2360 // Seek to a single-key block and step forward without accessing value.
2361 iter->Seek(ikey("aaca"));
2362 ASSERT_TRUE(iter->Valid());
2363 EXPECT_EQ(keys[2], iter->key().ToString());
2364 EXPECT_EQ(use_first_key ? 0 : 1,
2365 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2366
2367 iter->Next();
2368 ASSERT_TRUE(iter->Valid());
2369 EXPECT_EQ(keys[3], iter->key().ToString());
2370 EXPECT_EQ(use_first_key ? 1 : 2,
2371 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
20effc67 2372 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2373 EXPECT_EQ("v3", iter->value().ToString());
2374 EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2375 EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2376
2377 // Seek between blocks and step back without accessing value.
2378 iter->Seek(ikey("aaca"));
2379 ASSERT_TRUE(iter->Valid());
2380 EXPECT_EQ(keys[2], iter->key().ToString());
2381 EXPECT_EQ(use_first_key ? 2 : 3,
2382 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2383 EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2384
2385 iter->Prev();
2386 ASSERT_TRUE(iter->Valid());
2387 EXPECT_EQ(keys[1], iter->key().ToString());
2388 EXPECT_EQ(use_first_key ? 2 : 3,
2389 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2390 // All blocks are in cache now, there'll be no more misses ever.
2391 EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
20effc67 2392 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2393 EXPECT_EQ("v1", iter->value().ToString());
2394
2395 // Next into the next block again.
2396 iter->Next();
2397 ASSERT_TRUE(iter->Valid());
2398 EXPECT_EQ(keys[2], iter->key().ToString());
2399 EXPECT_EQ(use_first_key ? 2 : 4,
2400 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2401
2402 // Seek to first and step back without accessing value.
2403 iter->SeekToFirst();
2404 ASSERT_TRUE(iter->Valid());
2405 EXPECT_EQ(keys[0], iter->key().ToString());
2406 EXPECT_EQ(use_first_key ? 2 : 5,
2407 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2408
2409 iter->Prev();
2410 EXPECT_FALSE(iter->Valid());
2411 EXPECT_EQ(use_first_key ? 2 : 5,
2412 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2413
2414 // Do some SeekForPrev() and SeekToLast() just to cover all methods.
2415 iter->SeekForPrev(ikey("caad"));
2416 ASSERT_TRUE(iter->Valid());
2417 EXPECT_EQ(keys[4], iter->key().ToString());
2418 EXPECT_EQ(use_first_key ? 3 : 6,
2419 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
20effc67 2420 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2421 EXPECT_EQ("v4", iter->value().ToString());
2422 EXPECT_EQ(use_first_key ? 3 : 6,
2423 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2424
2425 iter->SeekToLast();
2426 ASSERT_TRUE(iter->Valid());
2427 EXPECT_EQ(keys[7], iter->key().ToString());
2428 EXPECT_EQ(use_first_key ? 4 : 7,
2429 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
20effc67 2430 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2431 EXPECT_EQ("v7", iter->value().ToString());
2432 EXPECT_EQ(use_first_key ? 4 : 7,
2433 stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2434
2435 EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2436
2437 c.ResetTableReader();
2438 }
2439}
2440
2441TEST_P(BlockBasedTableTest, BinaryIndexWithFirstKeyGlobalSeqno) {
2442 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2443 table_options.index_type = BlockBasedTableOptions::kBinarySearchWithFirstKey;
2444 table_options.block_cache = NewLRUCache(10000);
2445 Options options;
2446 options.statistics = CreateDBStatistics();
2447 Statistics* stats = options.statistics.get();
2448 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
2449 std::unique_ptr<InternalKeyComparator> comparator(
2450 new InternalKeyComparator(BytewiseComparator()));
2451 const ImmutableCFOptions ioptions(options);
2452 const MutableCFOptions moptions(options);
2453
2454 TableConstructor c(BytewiseComparator(), /* convert_to_internal_key */ false,
2455 /* level */ -1, /* largest_seqno */ 42);
2456
2457 c.Add(InternalKey("b", 0, kTypeValue).Encode().ToString(), "x");
2458 c.Add(InternalKey("c", 0, kTypeValue).Encode().ToString(), "y");
2459
2460 std::vector<std::string> keys;
2461 stl_wrappers::KVMap kvmap;
2462 c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
2463 &kvmap);
2464 ASSERT_EQ(2, keys.size());
2465
2466 auto reader = c.GetTableReader();
2467 auto props = reader->GetTableProperties();
2468 ASSERT_EQ(1u, props->num_data_blocks);
20effc67 2469 ReadOptions read_options;
f67539c2 2470 std::unique_ptr<InternalIterator> iter(reader->NewIterator(
20effc67
TL
2471 read_options, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
2472 /*skip_filters=*/false, TableReaderCaller::kUncategorized,
2473 /*compaction_readahead_size=*/0, /*allow_unprepared_value=*/true));
f67539c2
TL
2474
2475 iter->Seek(InternalKey("a", 0, kTypeValue).Encode().ToString());
2476 ASSERT_TRUE(iter->Valid());
2477 EXPECT_EQ(InternalKey("b", 42, kTypeValue).Encode().ToString(),
2478 iter->key().ToString());
2479 EXPECT_NE(keys[0], iter->key().ToString());
2480 // Key should have been served from index, without reading data blocks.
2481 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2482
20effc67 2483 ASSERT_TRUE(iter->PrepareValue());
f67539c2
TL
2484 EXPECT_EQ("x", iter->value().ToString());
2485 EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
2486 EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
2487 EXPECT_EQ(InternalKey("b", 42, kTypeValue).Encode().ToString(),
2488 iter->key().ToString());
2489
2490 c.ResetTableReader();
2491}
2492
7c673cae
FG
2493// It's very hard to figure out the index block size of a block accurately.
2494// To make sure we get the index size, we just make sure as key number
2495// grows, the filter block size also grows.
11fdf7f2 2496TEST_P(BlockBasedTableTest, IndexSizeStat) {
7c673cae
FG
2497 uint64_t last_index_size = 0;
2498
2499 // we need to use random keys since the pure human readable texts
2500 // may be well compressed, resulting insignifcant change of index
2501 // block size.
2502 Random rnd(test::RandomSeed());
2503 std::vector<std::string> keys;
2504
2505 for (int i = 0; i < 100; ++i) {
20effc67 2506 keys.push_back(rnd.RandomString(10000));
7c673cae
FG
2507 }
2508
2509 // Each time we load one more key to the table. the table index block
2510 // size is expected to be larger than last time's.
2511 for (size_t i = 1; i < keys.size(); ++i) {
2512 TableConstructor c(BytewiseComparator(),
2513 true /* convert_to_internal_key_ */);
2514 for (size_t j = 0; j < i; ++j) {
2515 c.Add(keys[j], "val");
2516 }
2517
2518 std::vector<std::string> ks;
2519 stl_wrappers::KVMap kvmap;
2520 Options options;
2521 options.compression = kNoCompression;
11fdf7f2 2522 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
2523 table_options.block_restart_interval = 1;
2524 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
2525
2526 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
2527 const MutableCFOptions moptions(options);
2528 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
2529 GetPlainInternalComparator(options.comparator), &ks, &kvmap);
2530 auto index_size = c.GetTableReader()->GetTableProperties()->index_size;
2531 ASSERT_GT(index_size, last_index_size);
2532 last_index_size = index_size;
2533 c.ResetTableReader();
2534 }
2535}
2536
11fdf7f2 2537TEST_P(BlockBasedTableTest, NumBlockStat) {
7c673cae
FG
2538 Random rnd(test::RandomSeed());
2539 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
2540 Options options;
2541 options.compression = kNoCompression;
11fdf7f2 2542 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
2543 table_options.block_restart_interval = 1;
2544 table_options.block_size = 1000;
2545 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
2546
2547 for (int i = 0; i < 10; ++i) {
2548 // the key/val are slightly smaller than block size, so that each block
2549 // holds roughly one key/value pair.
20effc67 2550 c.Add(rnd.RandomString(900), "val");
7c673cae
FG
2551 }
2552
2553 std::vector<std::string> ks;
2554 stl_wrappers::KVMap kvmap;
2555 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
2556 const MutableCFOptions moptions(options);
2557 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
2558 GetPlainInternalComparator(options.comparator), &ks, &kvmap);
2559 ASSERT_EQ(kvmap.size(),
2560 c.GetTableReader()->GetTableProperties()->num_data_blocks);
2561 c.ResetTableReader();
2562}
2563
f67539c2
TL
2564TEST_P(BlockBasedTableTest, TracingGetTest) {
2565 TableConstructor c(BytewiseComparator());
2566 Options options;
2567 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2568 options.create_if_missing = true;
2569 table_options.block_cache = NewLRUCache(1024 * 1024, 0);
2570 table_options.cache_index_and_filter_blocks = true;
2571 table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
2572 options.table_factory.reset(new BlockBasedTableFactory(table_options));
2573 SetupTracingTest(&c);
2574 std::vector<std::string> keys;
2575 stl_wrappers::KVMap kvmap;
2576 ImmutableCFOptions ioptions(options);
2577 MutableCFOptions moptions(options);
2578 c.Finish(options, ioptions, moptions, table_options,
2579 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
2580 std::string user_key = "k01";
2581 InternalKey internal_key(user_key, 0, kTypeValue);
2582 std::string encoded_key = internal_key.Encode().ToString();
2583 for (uint32_t i = 1; i <= 2; i++) {
2584 PinnableSlice value;
2585 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
2586 GetContext::kNotFound, user_key, &value, nullptr,
2587 nullptr, true, nullptr, nullptr, nullptr, nullptr,
2588 nullptr, nullptr, /*tracing_get_id=*/i);
2589 get_perf_context()->Reset();
2590 ASSERT_OK(c.GetTableReader()->Get(ReadOptions(), encoded_key, &get_context,
2591 moptions.prefix_extractor.get()));
2592 ASSERT_EQ(get_context.State(), GetContext::kFound);
2593 ASSERT_EQ(value.ToString(), kDummyValue);
2594 }
2595
2596 // Verify traces.
2597 std::vector<BlockCacheTraceRecord> expected_records;
2598 // The first two records should be prefetching index and filter blocks.
2599 BlockCacheTraceRecord record;
2600 record.block_type = TraceType::kBlockTraceIndexBlock;
2601 record.caller = TableReaderCaller::kPrefetch;
2602 record.is_cache_hit = Boolean::kFalse;
2603 record.no_insert = Boolean::kFalse;
2604 expected_records.push_back(record);
2605 record.block_type = TraceType::kBlockTraceFilterBlock;
2606 expected_records.push_back(record);
2607 // Then we should have three records for one index, one filter, and one data
2608 // block access.
2609 record.get_id = 1;
2610 record.block_type = TraceType::kBlockTraceIndexBlock;
2611 record.caller = TableReaderCaller::kUserGet;
2612 record.get_from_user_specified_snapshot = Boolean::kFalse;
2613 record.referenced_key = encoded_key;
2614 record.referenced_key_exist_in_block = Boolean::kTrue;
2615 record.is_cache_hit = Boolean::kTrue;
2616 expected_records.push_back(record);
2617 record.block_type = TraceType::kBlockTraceFilterBlock;
2618 expected_records.push_back(record);
2619 record.is_cache_hit = Boolean::kFalse;
2620 record.block_type = TraceType::kBlockTraceDataBlock;
2621 expected_records.push_back(record);
2622 // The second get should all observe cache hits.
2623 record.is_cache_hit = Boolean::kTrue;
2624 record.get_id = 2;
2625 record.block_type = TraceType::kBlockTraceIndexBlock;
2626 record.caller = TableReaderCaller::kUserGet;
2627 record.get_from_user_specified_snapshot = Boolean::kFalse;
2628 record.referenced_key = encoded_key;
2629 expected_records.push_back(record);
2630 record.block_type = TraceType::kBlockTraceFilterBlock;
2631 expected_records.push_back(record);
2632 record.block_type = TraceType::kBlockTraceDataBlock;
2633 expected_records.push_back(record);
2634 VerifyBlockAccessTrace(&c, expected_records);
2635 c.ResetTableReader();
2636}
2637
2638TEST_P(BlockBasedTableTest, TracingApproximateOffsetOfTest) {
2639 TableConstructor c(BytewiseComparator());
2640 Options options;
2641 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2642 options.create_if_missing = true;
2643 table_options.block_cache = NewLRUCache(1024 * 1024, 0);
2644 table_options.cache_index_and_filter_blocks = true;
2645 table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
2646 options.table_factory.reset(new BlockBasedTableFactory(table_options));
2647 SetupTracingTest(&c);
2648 std::vector<std::string> keys;
2649 stl_wrappers::KVMap kvmap;
2650 ImmutableCFOptions ioptions(options);
2651 MutableCFOptions moptions(options);
2652 c.Finish(options, ioptions, moptions, table_options,
2653 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
2654 for (uint32_t i = 1; i <= 2; i++) {
2655 std::string user_key = "k01";
2656 InternalKey internal_key(user_key, 0, kTypeValue);
2657 std::string encoded_key = internal_key.Encode().ToString();
2658 c.GetTableReader()->ApproximateOffsetOf(
2659 encoded_key, TableReaderCaller::kUserApproximateSize);
2660 }
2661 // Verify traces.
2662 std::vector<BlockCacheTraceRecord> expected_records;
2663 // The first two records should be prefetching index and filter blocks.
2664 BlockCacheTraceRecord record;
2665 record.block_type = TraceType::kBlockTraceIndexBlock;
2666 record.caller = TableReaderCaller::kPrefetch;
2667 record.is_cache_hit = Boolean::kFalse;
2668 record.no_insert = Boolean::kFalse;
2669 expected_records.push_back(record);
2670 record.block_type = TraceType::kBlockTraceFilterBlock;
2671 expected_records.push_back(record);
2672 // Then we should have two records for only index blocks.
2673 record.block_type = TraceType::kBlockTraceIndexBlock;
2674 record.caller = TableReaderCaller::kUserApproximateSize;
2675 record.is_cache_hit = Boolean::kTrue;
2676 expected_records.push_back(record);
2677 expected_records.push_back(record);
2678 VerifyBlockAccessTrace(&c, expected_records);
2679 c.ResetTableReader();
2680}
2681
2682TEST_P(BlockBasedTableTest, TracingIterator) {
2683 TableConstructor c(BytewiseComparator());
2684 Options options;
2685 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
2686 options.create_if_missing = true;
2687 table_options.block_cache = NewLRUCache(1024 * 1024, 0);
2688 table_options.cache_index_and_filter_blocks = true;
2689 table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
2690 options.table_factory.reset(new BlockBasedTableFactory(table_options));
2691 SetupTracingTest(&c);
2692 std::vector<std::string> keys;
2693 stl_wrappers::KVMap kvmap;
2694 ImmutableCFOptions ioptions(options);
2695 MutableCFOptions moptions(options);
2696 c.Finish(options, ioptions, moptions, table_options,
2697 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
2698
2699 for (uint32_t i = 1; i <= 2; i++) {
20effc67 2700 ReadOptions read_options;
f67539c2 2701 std::unique_ptr<InternalIterator> iter(c.GetTableReader()->NewIterator(
20effc67 2702 read_options, moptions.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2
TL
2703 /*skip_filters=*/false, TableReaderCaller::kUserIterator));
2704 iter->SeekToFirst();
2705 while (iter->Valid()) {
2706 iter->key();
2707 iter->value();
2708 iter->Next();
2709 }
2710 ASSERT_OK(iter->status());
2711 iter.reset();
2712 }
2713
2714 // Verify traces.
2715 std::vector<BlockCacheTraceRecord> expected_records;
2716 // The first two records should be prefetching index and filter blocks.
2717 BlockCacheTraceRecord record;
2718 record.block_type = TraceType::kBlockTraceIndexBlock;
2719 record.caller = TableReaderCaller::kPrefetch;
2720 record.is_cache_hit = Boolean::kFalse;
2721 record.no_insert = Boolean::kFalse;
2722 expected_records.push_back(record);
2723 record.block_type = TraceType::kBlockTraceFilterBlock;
2724 expected_records.push_back(record);
2725 // Then we should have three records for index and two data block access.
2726 record.block_type = TraceType::kBlockTraceIndexBlock;
2727 record.caller = TableReaderCaller::kUserIterator;
2728 record.is_cache_hit = Boolean::kTrue;
2729 expected_records.push_back(record);
2730 record.block_type = TraceType::kBlockTraceDataBlock;
2731 record.is_cache_hit = Boolean::kFalse;
2732 expected_records.push_back(record);
2733 expected_records.push_back(record);
2734 // When we iterate this file for the second time, we should observe all cache
2735 // hits.
2736 record.block_type = TraceType::kBlockTraceIndexBlock;
2737 record.is_cache_hit = Boolean::kTrue;
2738 expected_records.push_back(record);
2739 record.block_type = TraceType::kBlockTraceDataBlock;
2740 expected_records.push_back(record);
2741 expected_records.push_back(record);
2742 VerifyBlockAccessTrace(&c, expected_records);
2743 c.ResetTableReader();
2744}
2745
7c673cae
FG
2746// A simple tool that takes the snapshot of block cache statistics.
2747class BlockCachePropertiesSnapshot {
2748 public:
2749 explicit BlockCachePropertiesSnapshot(Statistics* statistics) {
2750 block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_MISS);
2751 block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_HIT);
2752 index_block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_INDEX_MISS);
2753 index_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_INDEX_HIT);
2754 data_block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_DATA_MISS);
2755 data_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_DATA_HIT);
2756 filter_block_cache_miss =
2757 statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS);
2758 filter_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT);
2759 block_cache_bytes_read = statistics->getTickerCount(BLOCK_CACHE_BYTES_READ);
2760 block_cache_bytes_write =
2761 statistics->getTickerCount(BLOCK_CACHE_BYTES_WRITE);
2762 }
2763
2764 void AssertIndexBlockStat(int64_t expected_index_block_cache_miss,
2765 int64_t expected_index_block_cache_hit) {
2766 ASSERT_EQ(expected_index_block_cache_miss, index_block_cache_miss);
2767 ASSERT_EQ(expected_index_block_cache_hit, index_block_cache_hit);
2768 }
2769
2770 void AssertFilterBlockStat(int64_t expected_filter_block_cache_miss,
2771 int64_t expected_filter_block_cache_hit) {
2772 ASSERT_EQ(expected_filter_block_cache_miss, filter_block_cache_miss);
2773 ASSERT_EQ(expected_filter_block_cache_hit, filter_block_cache_hit);
2774 }
2775
2776 // Check if the fetched props matches the expected ones.
2777 // TODO(kailiu) Use this only when you disabled filter policy!
2778 void AssertEqual(int64_t expected_index_block_cache_miss,
2779 int64_t expected_index_block_cache_hit,
2780 int64_t expected_data_block_cache_miss,
2781 int64_t expected_data_block_cache_hit) const {
2782 ASSERT_EQ(expected_index_block_cache_miss, index_block_cache_miss);
2783 ASSERT_EQ(expected_index_block_cache_hit, index_block_cache_hit);
2784 ASSERT_EQ(expected_data_block_cache_miss, data_block_cache_miss);
2785 ASSERT_EQ(expected_data_block_cache_hit, data_block_cache_hit);
2786 ASSERT_EQ(expected_index_block_cache_miss + expected_data_block_cache_miss,
2787 block_cache_miss);
2788 ASSERT_EQ(expected_index_block_cache_hit + expected_data_block_cache_hit,
2789 block_cache_hit);
2790 }
2791
2792 int64_t GetCacheBytesRead() { return block_cache_bytes_read; }
2793
2794 int64_t GetCacheBytesWrite() { return block_cache_bytes_write; }
2795
2796 private:
2797 int64_t block_cache_miss = 0;
2798 int64_t block_cache_hit = 0;
2799 int64_t index_block_cache_miss = 0;
2800 int64_t index_block_cache_hit = 0;
2801 int64_t data_block_cache_miss = 0;
2802 int64_t data_block_cache_hit = 0;
2803 int64_t filter_block_cache_miss = 0;
2804 int64_t filter_block_cache_hit = 0;
2805 int64_t block_cache_bytes_read = 0;
2806 int64_t block_cache_bytes_write = 0;
2807};
2808
2809// Make sure, by default, index/filter blocks were pre-loaded (meaning we won't
2810// use block cache to store them).
11fdf7f2 2811TEST_P(BlockBasedTableTest, BlockCacheDisabledTest) {
7c673cae
FG
2812 Options options;
2813 options.create_if_missing = true;
2814 options.statistics = CreateDBStatistics();
11fdf7f2 2815 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
2816 table_options.block_cache = NewLRUCache(1024, 4);
2817 table_options.filter_policy.reset(NewBloomFilterPolicy(10));
2818 options.table_factory.reset(new BlockBasedTableFactory(table_options));
2819 std::vector<std::string> keys;
2820 stl_wrappers::KVMap kvmap;
2821
2822 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
2823 c.Add("key", "value");
2824 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
2825 const MutableCFOptions moptions(options);
2826 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
2827 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
2828
2829 // preloading filter/index blocks is enabled.
2830 auto reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
f67539c2
TL
2831 ASSERT_FALSE(reader->TEST_FilterBlockInCache());
2832 ASSERT_FALSE(reader->TEST_IndexBlockInCache());
7c673cae
FG
2833
2834 {
2835 // nothing happens in the beginning
2836 BlockCachePropertiesSnapshot props(options.statistics.get());
2837 props.AssertIndexBlockStat(0, 0);
2838 props.AssertFilterBlockStat(0, 0);
2839 }
2840
2841 {
2842 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
2843 GetContext::kNotFound, Slice(), nullptr, nullptr,
f67539c2 2844 nullptr, true, nullptr, nullptr);
7c673cae 2845 // a hack that just to trigger BlockBasedTable::GetFilter.
20effc67
TL
2846 ASSERT_OK(reader->Get(ReadOptions(), "non-exist-key", &get_context,
2847 moptions.prefix_extractor.get()));
7c673cae
FG
2848 BlockCachePropertiesSnapshot props(options.statistics.get());
2849 props.AssertIndexBlockStat(0, 0);
2850 props.AssertFilterBlockStat(0, 0);
2851 }
2852}
2853
2854// Due to the difficulities of the intersaction between statistics, this test
2855// only tests the case when "index block is put to block cache"
11fdf7f2 2856TEST_P(BlockBasedTableTest, FilterBlockInBlockCache) {
7c673cae
FG
2857 // -- Table construction
2858 Options options;
2859 options.create_if_missing = true;
2860 options.statistics = CreateDBStatistics();
2861
2862 // Enable the cache for index/filter blocks
11fdf7f2 2863 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
f67539c2
TL
2864 LRUCacheOptions co;
2865 co.capacity = 2048;
2866 co.num_shard_bits = 2;
2867 co.metadata_charge_policy = kDontChargeCacheMetadata;
2868 table_options.block_cache = NewLRUCache(co);
7c673cae
FG
2869 table_options.cache_index_and_filter_blocks = true;
2870 options.table_factory.reset(new BlockBasedTableFactory(table_options));
2871 std::vector<std::string> keys;
2872 stl_wrappers::KVMap kvmap;
2873
2874 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
2875 c.Add("key", "value");
2876 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
2877 const MutableCFOptions moptions(options);
2878 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
2879 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
2880 // preloading filter/index blocks is prohibited.
2881 auto* reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
f67539c2
TL
2882 ASSERT_FALSE(reader->TEST_FilterBlockInCache());
2883 ASSERT_TRUE(reader->TEST_IndexBlockInCache());
7c673cae
FG
2884
2885 // -- PART 1: Open with regular block cache.
2886 // Since block_cache is disabled, no cache activities will be involved.
494da23a 2887 std::unique_ptr<InternalIterator> iter;
7c673cae
FG
2888
2889 int64_t last_cache_bytes_read = 0;
2890 // At first, no block will be accessed.
2891 {
2892 BlockCachePropertiesSnapshot props(options.statistics.get());
2893 // index will be added to block cache.
2894 props.AssertEqual(1, // index block miss
2895 0, 0, 0);
2896 ASSERT_EQ(props.GetCacheBytesRead(), 0);
2897 ASSERT_EQ(props.GetCacheBytesWrite(),
f67539c2 2898 static_cast<int64_t>(table_options.block_cache->GetUsage()));
7c673cae
FG
2899 last_cache_bytes_read = props.GetCacheBytesRead();
2900 }
2901
2902 // Only index block will be accessed
2903 {
11fdf7f2 2904 iter.reset(c.NewIterator(moptions.prefix_extractor.get()));
7c673cae
FG
2905 BlockCachePropertiesSnapshot props(options.statistics.get());
2906 // NOTE: to help better highlight the "detla" of each ticker, I use
2907 // <last_value> + <added_value> to indicate the increment of changed
2908 // value; other numbers remain the same.
2909 props.AssertEqual(1, 0 + 1, // index block hit
2910 0, 0);
2911 // Cache hit, bytes read from cache should increase
2912 ASSERT_GT(props.GetCacheBytesRead(), last_cache_bytes_read);
2913 ASSERT_EQ(props.GetCacheBytesWrite(),
f67539c2 2914 static_cast<int64_t>(table_options.block_cache->GetUsage()));
7c673cae
FG
2915 last_cache_bytes_read = props.GetCacheBytesRead();
2916 }
2917
2918 // Only data block will be accessed
2919 {
2920 iter->SeekToFirst();
20effc67 2921 ASSERT_OK(iter->status());
7c673cae
FG
2922 BlockCachePropertiesSnapshot props(options.statistics.get());
2923 props.AssertEqual(1, 1, 0 + 1, // data block miss
2924 0);
2925 // Cache miss, Bytes read from cache should not change
2926 ASSERT_EQ(props.GetCacheBytesRead(), last_cache_bytes_read);
2927 ASSERT_EQ(props.GetCacheBytesWrite(),
f67539c2 2928 static_cast<int64_t>(table_options.block_cache->GetUsage()));
7c673cae
FG
2929 last_cache_bytes_read = props.GetCacheBytesRead();
2930 }
2931
2932 // Data block will be in cache
2933 {
11fdf7f2 2934 iter.reset(c.NewIterator(moptions.prefix_extractor.get()));
7c673cae 2935 iter->SeekToFirst();
20effc67 2936 ASSERT_OK(iter->status());
7c673cae
FG
2937 BlockCachePropertiesSnapshot props(options.statistics.get());
2938 props.AssertEqual(1, 1 + 1, /* index block hit */
2939 1, 0 + 1 /* data block hit */);
2940 // Cache hit, bytes read from cache should increase
2941 ASSERT_GT(props.GetCacheBytesRead(), last_cache_bytes_read);
2942 ASSERT_EQ(props.GetCacheBytesWrite(),
f67539c2 2943 static_cast<int64_t>(table_options.block_cache->GetUsage()));
7c673cae
FG
2944 }
2945 // release the iterator so that the block cache can reset correctly.
2946 iter.reset();
2947
2948 c.ResetTableReader();
2949
2950 // -- PART 2: Open with very small block cache
2951 // In this test, no block will ever get hit since the block cache is
2952 // too small to fit even one entry.
2953 table_options.block_cache = NewLRUCache(1, 4);
2954 options.statistics = CreateDBStatistics();
2955 options.table_factory.reset(new BlockBasedTableFactory(table_options));
2956 const ImmutableCFOptions ioptions2(options);
11fdf7f2 2957 const MutableCFOptions moptions2(options);
20effc67 2958 ASSERT_OK(c.Reopen(ioptions2, moptions2));
7c673cae
FG
2959 {
2960 BlockCachePropertiesSnapshot props(options.statistics.get());
2961 props.AssertEqual(1, // index block miss
2962 0, 0, 0);
2963 // Cache miss, Bytes read from cache should not change
2964 ASSERT_EQ(props.GetCacheBytesRead(), 0);
2965 }
2966
2967 {
2968 // Both index and data block get accessed.
2969 // It first cache index block then data block. But since the cache size
2970 // is only 1, index block will be purged after data block is inserted.
11fdf7f2 2971 iter.reset(c.NewIterator(moptions2.prefix_extractor.get()));
7c673cae
FG
2972 BlockCachePropertiesSnapshot props(options.statistics.get());
2973 props.AssertEqual(1 + 1, // index block miss
2974 0, 0, // data block miss
2975 0);
2976 // Cache hit, bytes read from cache should increase
2977 ASSERT_EQ(props.GetCacheBytesRead(), 0);
2978 }
2979
2980 {
2981 // SeekToFirst() accesses data block. With similar reason, we expect data
2982 // block's cache miss.
2983 iter->SeekToFirst();
20effc67 2984 ASSERT_OK(iter->status());
7c673cae
FG
2985 BlockCachePropertiesSnapshot props(options.statistics.get());
2986 props.AssertEqual(2, 0, 0 + 1, // data block miss
2987 0);
2988 // Cache miss, Bytes read from cache should not change
2989 ASSERT_EQ(props.GetCacheBytesRead(), 0);
2990 }
2991 iter.reset();
2992 c.ResetTableReader();
2993
2994 // -- PART 3: Open table with bloom filter enabled but not in SST file
2995 table_options.block_cache = NewLRUCache(4096, 4);
2996 table_options.cache_index_and_filter_blocks = false;
2997 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
2998
2999 TableConstructor c3(BytewiseComparator());
3000 std::string user_key = "k01";
3001 InternalKey internal_key(user_key, 0, kTypeValue);
3002 c3.Add(internal_key.Encode().ToString(), "hello");
3003 ImmutableCFOptions ioptions3(options);
11fdf7f2 3004 MutableCFOptions moptions3(options);
7c673cae 3005 // Generate table without filter policy
11fdf7f2 3006 c3.Finish(options, ioptions3, moptions3, table_options,
7c673cae
FG
3007 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
3008 c3.ResetTableReader();
3009
3010 // Open table with filter policy
3011 table_options.filter_policy.reset(NewBloomFilterPolicy(1));
3012 options.table_factory.reset(new BlockBasedTableFactory(table_options));
3013 options.statistics = CreateDBStatistics();
3014 ImmutableCFOptions ioptions4(options);
11fdf7f2
TL
3015 MutableCFOptions moptions4(options);
3016 ASSERT_OK(c3.Reopen(ioptions4, moptions4));
7c673cae 3017 reader = dynamic_cast<BlockBasedTable*>(c3.GetTableReader());
f67539c2 3018 ASSERT_FALSE(reader->TEST_FilterBlockInCache());
7c673cae
FG
3019 PinnableSlice value;
3020 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
3021 GetContext::kNotFound, user_key, &value, nullptr,
f67539c2 3022 nullptr, true, nullptr, nullptr);
11fdf7f2
TL
3023 ASSERT_OK(reader->Get(ReadOptions(), internal_key.Encode(), &get_context,
3024 moptions4.prefix_extractor.get()));
7c673cae
FG
3025 ASSERT_STREQ(value.data(), "hello");
3026 BlockCachePropertiesSnapshot props(options.statistics.get());
3027 props.AssertFilterBlockStat(0, 0);
3028 c3.ResetTableReader();
3029}
3030
3031void ValidateBlockSizeDeviation(int value, int expected) {
3032 BlockBasedTableOptions table_options;
3033 table_options.block_size_deviation = value;
3034 BlockBasedTableFactory* factory = new BlockBasedTableFactory(table_options);
3035
3036 const BlockBasedTableOptions* normalized_table_options =
20effc67 3037 factory->GetOptions<BlockBasedTableOptions>();
7c673cae
FG
3038 ASSERT_EQ(normalized_table_options->block_size_deviation, expected);
3039
3040 delete factory;
3041}
3042
3043void ValidateBlockRestartInterval(int value, int expected) {
3044 BlockBasedTableOptions table_options;
3045 table_options.block_restart_interval = value;
3046 BlockBasedTableFactory* factory = new BlockBasedTableFactory(table_options);
3047
3048 const BlockBasedTableOptions* normalized_table_options =
20effc67 3049 factory->GetOptions<BlockBasedTableOptions>();
7c673cae
FG
3050 ASSERT_EQ(normalized_table_options->block_restart_interval, expected);
3051
3052 delete factory;
3053}
3054
11fdf7f2 3055TEST_P(BlockBasedTableTest, InvalidOptions) {
7c673cae
FG
3056 // invalid values for block_size_deviation (<0 or >100) are silently set to 0
3057 ValidateBlockSizeDeviation(-10, 0);
3058 ValidateBlockSizeDeviation(-1, 0);
3059 ValidateBlockSizeDeviation(0, 0);
3060 ValidateBlockSizeDeviation(1, 1);
3061 ValidateBlockSizeDeviation(99, 99);
3062 ValidateBlockSizeDeviation(100, 100);
3063 ValidateBlockSizeDeviation(101, 0);
3064 ValidateBlockSizeDeviation(1000, 0);
3065
3066 // invalid values for block_restart_interval (<1) are silently set to 1
3067 ValidateBlockRestartInterval(-10, 1);
3068 ValidateBlockRestartInterval(-1, 1);
3069 ValidateBlockRestartInterval(0, 1);
3070 ValidateBlockRestartInterval(1, 1);
3071 ValidateBlockRestartInterval(2, 2);
3072 ValidateBlockRestartInterval(1000, 1000);
3073}
3074
11fdf7f2 3075TEST_P(BlockBasedTableTest, BlockReadCountTest) {
7c673cae
FG
3076 // bloom_filter_type = 0 -- block-based filter
3077 // bloom_filter_type = 0 -- full filter
3078 for (int bloom_filter_type = 0; bloom_filter_type < 2; ++bloom_filter_type) {
3079 for (int index_and_filter_in_cache = 0; index_and_filter_in_cache < 2;
3080 ++index_and_filter_in_cache) {
3081 Options options;
3082 options.create_if_missing = true;
3083
11fdf7f2 3084 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
3085 table_options.block_cache = NewLRUCache(1, 0);
3086 table_options.cache_index_and_filter_blocks = index_and_filter_in_cache;
3087 table_options.filter_policy.reset(
3088 NewBloomFilterPolicy(10, bloom_filter_type == 0));
3089 options.table_factory.reset(new BlockBasedTableFactory(table_options));
3090 std::vector<std::string> keys;
3091 stl_wrappers::KVMap kvmap;
3092
3093 TableConstructor c(BytewiseComparator());
3094 std::string user_key = "k04";
3095 InternalKey internal_key(user_key, 0, kTypeValue);
3096 std::string encoded_key = internal_key.Encode().ToString();
3097 c.Add(encoded_key, "hello");
3098 ImmutableCFOptions ioptions(options);
11fdf7f2 3099 MutableCFOptions moptions(options);
7c673cae 3100 // Generate table with filter policy
11fdf7f2 3101 c.Finish(options, ioptions, moptions, table_options,
7c673cae
FG
3102 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
3103 auto reader = c.GetTableReader();
3104 PinnableSlice value;
f67539c2
TL
3105 {
3106 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
3107 GetContext::kNotFound, user_key, &value, nullptr,
3108 nullptr, true, nullptr, nullptr);
3109 get_perf_context()->Reset();
3110 ASSERT_OK(reader->Get(ReadOptions(), encoded_key, &get_context,
3111 moptions.prefix_extractor.get()));
3112 if (index_and_filter_in_cache) {
3113 // data, index and filter block
3114 ASSERT_EQ(get_perf_context()->block_read_count, 3);
3115 ASSERT_EQ(get_perf_context()->index_block_read_count, 1);
3116 ASSERT_EQ(get_perf_context()->filter_block_read_count, 1);
3117 } else {
3118 // just the data block
3119 ASSERT_EQ(get_perf_context()->block_read_count, 1);
3120 }
3121 ASSERT_EQ(get_context.State(), GetContext::kFound);
3122 ASSERT_STREQ(value.data(), "hello");
7c673cae 3123 }
7c673cae
FG
3124
3125 // Get non-existing key
3126 user_key = "does-not-exist";
3127 internal_key = InternalKey(user_key, 0, kTypeValue);
3128 encoded_key = internal_key.Encode().ToString();
3129
3130 value.Reset();
f67539c2
TL
3131 {
3132 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
7c673cae 3133 GetContext::kNotFound, user_key, &value, nullptr,
f67539c2
TL
3134 nullptr, true, nullptr, nullptr);
3135 get_perf_context()->Reset();
3136 ASSERT_OK(reader->Get(ReadOptions(), encoded_key, &get_context,
3137 moptions.prefix_extractor.get()));
3138 ASSERT_EQ(get_context.State(), GetContext::kNotFound);
3139 }
7c673cae
FG
3140
3141 if (index_and_filter_in_cache) {
3142 if (bloom_filter_type == 0) {
3143 // with block-based, we read index and then the filter
11fdf7f2 3144 ASSERT_EQ(get_perf_context()->block_read_count, 2);
f67539c2
TL
3145 ASSERT_EQ(get_perf_context()->index_block_read_count, 1);
3146 ASSERT_EQ(get_perf_context()->filter_block_read_count, 1);
7c673cae
FG
3147 } else {
3148 // with full-filter, we read filter first and then we stop
11fdf7f2 3149 ASSERT_EQ(get_perf_context()->block_read_count, 1);
f67539c2 3150 ASSERT_EQ(get_perf_context()->filter_block_read_count, 1);
7c673cae
FG
3151 }
3152 } else {
3153 // filter is already in memory and it figures out that the key doesn't
3154 // exist
11fdf7f2 3155 ASSERT_EQ(get_perf_context()->block_read_count, 0);
7c673cae
FG
3156 }
3157 }
3158 }
3159}
3160
11fdf7f2 3161TEST_P(BlockBasedTableTest, BlockCacheLeak) {
7c673cae
FG
3162 // Check that when we reopen a table we don't lose access to blocks already
3163 // in the cache. This test checks whether the Table actually makes use of the
3164 // unique ID from the file.
3165
3166 Options opt;
494da23a 3167 std::unique_ptr<InternalKeyComparator> ikc;
7c673cae
FG
3168 ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
3169 opt.compression = kNoCompression;
11fdf7f2 3170 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
7c673cae
FG
3171 table_options.block_size = 1024;
3172 // big enough so we don't ever lose cached values.
3173 table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
3174 opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
3175
3176 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
3177 c.Add("k01", "hello");
3178 c.Add("k02", "hello2");
3179 c.Add("k03", std::string(10000, 'x'));
3180 c.Add("k04", std::string(200000, 'x'));
3181 c.Add("k05", std::string(300000, 'x'));
3182 c.Add("k06", "hello3");
3183 c.Add("k07", std::string(100000, 'x'));
3184 std::vector<std::string> keys;
3185 stl_wrappers::KVMap kvmap;
3186 const ImmutableCFOptions ioptions(opt);
11fdf7f2
TL
3187 const MutableCFOptions moptions(opt);
3188 c.Finish(opt, ioptions, moptions, table_options, *ikc, &keys, &kvmap);
7c673cae 3189
494da23a 3190 std::unique_ptr<InternalIterator> iter(
11fdf7f2 3191 c.NewIterator(moptions.prefix_extractor.get()));
7c673cae
FG
3192 iter->SeekToFirst();
3193 while (iter->Valid()) {
3194 iter->key();
3195 iter->value();
3196 iter->Next();
3197 }
3198 ASSERT_OK(iter->status());
11fdf7f2 3199 iter.reset();
7c673cae
FG
3200
3201 const ImmutableCFOptions ioptions1(opt);
11fdf7f2
TL
3202 const MutableCFOptions moptions1(opt);
3203 ASSERT_OK(c.Reopen(ioptions1, moptions1));
7c673cae
FG
3204 auto table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
3205 for (const std::string& key : keys) {
11fdf7f2
TL
3206 InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
3207 ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
7c673cae
FG
3208 }
3209 c.ResetTableReader();
3210
3211 // rerun with different block cache
3212 table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
3213 opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
3214 const ImmutableCFOptions ioptions2(opt);
11fdf7f2
TL
3215 const MutableCFOptions moptions2(opt);
3216 ASSERT_OK(c.Reopen(ioptions2, moptions2));
7c673cae
FG
3217 table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
3218 for (const std::string& key : keys) {
11fdf7f2
TL
3219 InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
3220 ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
7c673cae
FG
3221 }
3222 c.ResetTableReader();
3223}
3224
494da23a
TL
3225namespace {
3226class CustomMemoryAllocator : public MemoryAllocator {
3227 public:
3228 const char* Name() const override { return "CustomMemoryAllocator"; }
3229
3230 void* Allocate(size_t size) override {
3231 ++numAllocations;
3232 auto ptr = new char[size + 16];
3233 memcpy(ptr, "memory_allocator_", 16); // mangle first 16 bytes
3234 return reinterpret_cast<void*>(ptr + 16);
3235 }
3236 void Deallocate(void* p) override {
3237 ++numDeallocations;
3238 char* ptr = reinterpret_cast<char*>(p) - 16;
3239 delete[] ptr;
3240 }
3241
3242 std::atomic<int> numAllocations;
3243 std::atomic<int> numDeallocations;
3244};
3245} // namespace
3246
3247TEST_P(BlockBasedTableTest, MemoryAllocator) {
3248 auto custom_memory_allocator = std::make_shared<CustomMemoryAllocator>();
3249 {
3250 Options opt;
3251 std::unique_ptr<InternalKeyComparator> ikc;
3252 ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
3253 opt.compression = kNoCompression;
3254 BlockBasedTableOptions table_options;
3255 table_options.block_size = 1024;
3256 LRUCacheOptions lruOptions;
3257 lruOptions.memory_allocator = custom_memory_allocator;
3258 lruOptions.capacity = 16 * 1024 * 1024;
3259 lruOptions.num_shard_bits = 4;
3260 table_options.block_cache = NewLRUCache(std::move(lruOptions));
3261 opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
3262
3263 TableConstructor c(BytewiseComparator(),
3264 true /* convert_to_internal_key_ */);
3265 c.Add("k01", "hello");
3266 c.Add("k02", "hello2");
3267 c.Add("k03", std::string(10000, 'x'));
3268 c.Add("k04", std::string(200000, 'x'));
3269 c.Add("k05", std::string(300000, 'x'));
3270 c.Add("k06", "hello3");
3271 c.Add("k07", std::string(100000, 'x'));
3272 std::vector<std::string> keys;
3273 stl_wrappers::KVMap kvmap;
3274 const ImmutableCFOptions ioptions(opt);
3275 const MutableCFOptions moptions(opt);
3276 c.Finish(opt, ioptions, moptions, table_options, *ikc, &keys, &kvmap);
3277
3278 std::unique_ptr<InternalIterator> iter(
3279 c.NewIterator(moptions.prefix_extractor.get()));
3280 iter->SeekToFirst();
3281 while (iter->Valid()) {
3282 iter->key();
3283 iter->value();
3284 iter->Next();
3285 }
3286 ASSERT_OK(iter->status());
3287 }
3288
3289 // out of scope, block cache should have been deleted, all allocations
3290 // deallocated
3291 EXPECT_EQ(custom_memory_allocator->numAllocations.load(),
3292 custom_memory_allocator->numDeallocations.load());
3293 // make sure that allocations actually happened through the cache allocator
3294 EXPECT_GT(custom_memory_allocator->numAllocations.load(), 0);
3295}
3296
f67539c2
TL
3297// Test the file checksum of block based table
3298TEST_P(BlockBasedTableTest, NoFileChecksum) {
7c673cae 3299 Options options;
f67539c2
TL
3300 ImmutableCFOptions ioptions(options);
3301 MutableCFOptions moptions(options);
11fdf7f2 3302 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
f67539c2
TL
3303 std::unique_ptr<InternalKeyComparator> comparator(
3304 new InternalKeyComparator(BytewiseComparator()));
f67539c2
TL
3305 int level = 0;
3306 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
3307 int_tbl_prop_collector_factories;
f67539c2 3308 std::string column_family_name;
7c673cae 3309
f67539c2
TL
3310 FileChecksumTestHelper f(true);
3311 f.CreateWriteableFile();
3312 std::unique_ptr<TableBuilder> builder;
3313 builder.reset(ioptions.table_factory->NewTableBuilder(
3314 TableBuilderOptions(ioptions, moptions, *comparator,
3315 &int_tbl_prop_collector_factories,
3316 options.compression, options.sample_for_compression,
3317 options.compression_opts, false /* skip_filters */,
3318 column_family_name, level),
3319 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
3320 f.GetFileWriter()));
20effc67 3321 ASSERT_OK(f.ResetTableBuilder(std::move(builder)));
f67539c2
TL
3322 f.AddKVtoKVMap(1000);
3323 f.WriteKVAndFlushTable();
20effc67
TL
3324 ASSERT_STREQ(f.GetFileChecksumFuncName(), kUnknownFileChecksumFuncName);
3325 ASSERT_STREQ(f.GetFileChecksum().c_str(), kUnknownFileChecksum);
f67539c2 3326}
7c673cae 3327
20effc67
TL
3328TEST_P(BlockBasedTableTest, Crc32cFileChecksum) {
3329 FileChecksumGenCrc32cFactory* file_checksum_gen_factory =
3330 new FileChecksumGenCrc32cFactory();
f67539c2 3331 Options options;
20effc67 3332 options.file_checksum_gen_factory.reset(file_checksum_gen_factory);
f67539c2
TL
3333 ImmutableCFOptions ioptions(options);
3334 MutableCFOptions moptions(options);
3335 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
3336 std::unique_ptr<InternalKeyComparator> comparator(
3337 new InternalKeyComparator(BytewiseComparator()));
f67539c2
TL
3338 int level = 0;
3339 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
3340 int_tbl_prop_collector_factories;
f67539c2
TL
3341 std::string column_family_name;
3342
20effc67
TL
3343 FileChecksumGenContext gen_context;
3344 gen_context.file_name = "db/tmp";
3345 std::unique_ptr<FileChecksumGenerator> checksum_crc32c_gen1 =
3346 options.file_checksum_gen_factory->CreateFileChecksumGenerator(
3347 gen_context);
f67539c2
TL
3348 FileChecksumTestHelper f(true);
3349 f.CreateWriteableFile();
20effc67 3350 f.SetFileChecksumGenerator(checksum_crc32c_gen1.release());
f67539c2
TL
3351 std::unique_ptr<TableBuilder> builder;
3352 builder.reset(ioptions.table_factory->NewTableBuilder(
3353 TableBuilderOptions(ioptions, moptions, *comparator,
3354 &int_tbl_prop_collector_factories,
3355 options.compression, options.sample_for_compression,
3356 options.compression_opts, false /* skip_filters */,
3357 column_family_name, level),
3358 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
3359 f.GetFileWriter()));
20effc67 3360 ASSERT_OK(f.ResetTableBuilder(std::move(builder)));
f67539c2
TL
3361 f.AddKVtoKVMap(1000);
3362 f.WriteKVAndFlushTable();
3363 ASSERT_STREQ(f.GetFileChecksumFuncName(), "FileChecksumCrc32c");
20effc67
TL
3364
3365 std::unique_ptr<FileChecksumGenerator> checksum_crc32c_gen2 =
3366 options.file_checksum_gen_factory->CreateFileChecksumGenerator(
3367 gen_context);
f67539c2 3368 std::string checksum;
20effc67 3369 ASSERT_OK(f.CalculateFileChecksum(checksum_crc32c_gen2.get(), &checksum));
f67539c2 3370 ASSERT_STREQ(f.GetFileChecksum().c_str(), checksum.c_str());
20effc67
TL
3371
3372 // Unit test the generator itself for schema stability
3373 std::unique_ptr<FileChecksumGenerator> checksum_crc32c_gen3 =
3374 options.file_checksum_gen_factory->CreateFileChecksumGenerator(
3375 gen_context);
3376 const char data[] = "here is some data";
3377 checksum_crc32c_gen3->Update(data, sizeof(data));
3378 checksum_crc32c_gen3->Finalize();
3379 checksum = checksum_crc32c_gen3->GetChecksum();
3380 ASSERT_STREQ(checksum.c_str(), "\345\245\277\110");
7c673cae
FG
3381}
3382
3383// Plain table is not supported in ROCKSDB_LITE
3384#ifndef ROCKSDB_LITE
3385TEST_F(PlainTableTest, BasicPlainTableProperties) {
3386 PlainTableOptions plain_table_options;
3387 plain_table_options.user_key_len = 8;
3388 plain_table_options.bloom_bits_per_key = 8;
3389 plain_table_options.hash_table_ratio = 0;
3390
3391 PlainTableFactory factory(plain_table_options);
3392 test::StringSink sink;
494da23a 3393 std::unique_ptr<WritableFileWriter> file_writer(
11fdf7f2 3394 test::GetWritableFileWriter(new test::StringSink(), "" /* don't care */));
7c673cae
FG
3395 Options options;
3396 const ImmutableCFOptions ioptions(options);
11fdf7f2 3397 const MutableCFOptions moptions(options);
7c673cae
FG
3398 InternalKeyComparator ikc(options.comparator);
3399 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
3400 int_tbl_prop_collector_factories;
3401 std::string column_family_name;
3402 int unknown_level = -1;
3403 std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
11fdf7f2
TL
3404 TableBuilderOptions(
3405 ioptions, moptions, ikc, &int_tbl_prop_collector_factories,
494da23a 3406 kNoCompression, 0 /* sample_for_compression */, CompressionOptions(),
11fdf7f2 3407 false /* skip_filters */, column_family_name, unknown_level),
7c673cae
FG
3408 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
3409 file_writer.get()));
3410
3411 for (char c = 'a'; c <= 'z'; ++c) {
3412 std::string key(8, c);
3413 key.append("\1 "); // PlainTable expects internal key structure
3414 std::string value(28, c + 42);
3415 builder->Add(key, value);
3416 }
3417 ASSERT_OK(builder->Finish());
3418 file_writer->Flush();
3419
3420 test::StringSink* ss =
f67539c2 3421 ROCKSDB_NAMESPACE::test::GetStringSinkFromLegacyWriter(file_writer.get());
494da23a 3422 std::unique_ptr<RandomAccessFileReader> file_reader(
7c673cae
FG
3423 test::GetRandomAccessFileReader(
3424 new test::StringSource(ss->contents(), 72242, true)));
3425
3426 TableProperties* props = nullptr;
3427 auto s = ReadTableProperties(file_reader.get(), ss->contents().size(),
3428 kPlainTableMagicNumber, ioptions,
11fdf7f2 3429 &props, true /* compression_type_missing */);
7c673cae
FG
3430 std::unique_ptr<TableProperties> props_guard(props);
3431 ASSERT_OK(s);
3432
3433 ASSERT_EQ(0ul, props->index_size);
3434 ASSERT_EQ(0ul, props->filter_size);
3435 ASSERT_EQ(16ul * 26, props->raw_key_size);
3436 ASSERT_EQ(28ul * 26, props->raw_value_size);
3437 ASSERT_EQ(26ul, props->num_entries);
3438 ASSERT_EQ(1ul, props->num_data_blocks);
3439}
f67539c2
TL
3440
3441TEST_F(PlainTableTest, NoFileChecksum) {
3442 PlainTableOptions plain_table_options;
3443 plain_table_options.user_key_len = 20;
3444 plain_table_options.bloom_bits_per_key = 8;
3445 plain_table_options.hash_table_ratio = 0;
3446 PlainTableFactory factory(plain_table_options);
3447
3448 Options options;
3449 const ImmutableCFOptions ioptions(options);
3450 const MutableCFOptions moptions(options);
3451 InternalKeyComparator ikc(options.comparator);
3452 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
3453 int_tbl_prop_collector_factories;
3454 std::string column_family_name;
3455 int unknown_level = -1;
3456 FileChecksumTestHelper f(true);
3457 f.CreateWriteableFile();
3458
3459 std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
3460 TableBuilderOptions(
3461 ioptions, moptions, ikc, &int_tbl_prop_collector_factories,
3462 kNoCompression, 0 /* sample_for_compression */, CompressionOptions(),
3463 false /* skip_filters */, column_family_name, unknown_level),
3464 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
3465 f.GetFileWriter()));
20effc67 3466 ASSERT_OK(f.ResetTableBuilder(std::move(builder)));
f67539c2
TL
3467 f.AddKVtoKVMap(1000);
3468 f.WriteKVAndFlushTable();
20effc67
TL
3469 ASSERT_STREQ(f.GetFileChecksumFuncName(), kUnknownFileChecksumFuncName);
3470 EXPECT_EQ(f.GetFileChecksum(), kUnknownFileChecksum);
f67539c2
TL
3471}
3472
20effc67 3473TEST_F(PlainTableTest, Crc32cFileChecksum) {
f67539c2
TL
3474 PlainTableOptions plain_table_options;
3475 plain_table_options.user_key_len = 20;
3476 plain_table_options.bloom_bits_per_key = 8;
3477 plain_table_options.hash_table_ratio = 0;
3478 PlainTableFactory factory(plain_table_options);
3479
20effc67
TL
3480 FileChecksumGenCrc32cFactory* file_checksum_gen_factory =
3481 new FileChecksumGenCrc32cFactory();
f67539c2 3482 Options options;
20effc67 3483 options.file_checksum_gen_factory.reset(file_checksum_gen_factory);
f67539c2
TL
3484 const ImmutableCFOptions ioptions(options);
3485 const MutableCFOptions moptions(options);
3486 InternalKeyComparator ikc(options.comparator);
3487 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
3488 int_tbl_prop_collector_factories;
3489 std::string column_family_name;
3490 int unknown_level = -1;
20effc67
TL
3491
3492 FileChecksumGenContext gen_context;
3493 gen_context.file_name = "db/tmp";
3494 std::unique_ptr<FileChecksumGenerator> checksum_crc32c_gen1 =
3495 options.file_checksum_gen_factory->CreateFileChecksumGenerator(
3496 gen_context);
f67539c2
TL
3497 FileChecksumTestHelper f(true);
3498 f.CreateWriteableFile();
20effc67 3499 f.SetFileChecksumGenerator(checksum_crc32c_gen1.release());
f67539c2
TL
3500
3501 std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
3502 TableBuilderOptions(
3503 ioptions, moptions, ikc, &int_tbl_prop_collector_factories,
3504 kNoCompression, 0 /* sample_for_compression */, CompressionOptions(),
3505 false /* skip_filters */, column_family_name, unknown_level),
3506 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
3507 f.GetFileWriter()));
20effc67 3508 ASSERT_OK(f.ResetTableBuilder(std::move(builder)));
f67539c2
TL
3509 f.AddKVtoKVMap(1000);
3510 f.WriteKVAndFlushTable();
3511 ASSERT_STREQ(f.GetFileChecksumFuncName(), "FileChecksumCrc32c");
20effc67
TL
3512
3513 std::unique_ptr<FileChecksumGenerator> checksum_crc32c_gen2 =
3514 options.file_checksum_gen_factory->CreateFileChecksumGenerator(
3515 gen_context);
f67539c2 3516 std::string checksum;
20effc67 3517 ASSERT_OK(f.CalculateFileChecksum(checksum_crc32c_gen2.get(), &checksum));
f67539c2
TL
3518 EXPECT_STREQ(f.GetFileChecksum().c_str(), checksum.c_str());
3519}
3520
7c673cae
FG
3521#endif // !ROCKSDB_LITE
3522
3523TEST_F(GeneralTableTest, ApproximateOffsetOfPlain) {
3524 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
3525 c.Add("k01", "hello");
3526 c.Add("k02", "hello2");
3527 c.Add("k03", std::string(10000, 'x'));
3528 c.Add("k04", std::string(200000, 'x'));
3529 c.Add("k05", std::string(300000, 'x'));
3530 c.Add("k06", "hello3");
3531 c.Add("k07", std::string(100000, 'x'));
3532 std::vector<std::string> keys;
3533 stl_wrappers::KVMap kvmap;
3534 Options options;
20effc67 3535 options.db_host_id = "";
7c673cae
FG
3536 test::PlainInternalKeyComparator internal_comparator(options.comparator);
3537 options.compression = kNoCompression;
3538 BlockBasedTableOptions table_options;
3539 table_options.block_size = 1024;
3540 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
3541 const MutableCFOptions moptions(options);
3542 c.Finish(options, ioptions, moptions, table_options, internal_comparator,
7c673cae
FG
3543 &keys, &kvmap);
3544
3545 ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
3546 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
3547 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0));
3548 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
3549 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0));
3550 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000));
3551 // k04 and k05 will be in two consecutive blocks, the index is
3552 // an arbitrary slice between k04 and k05, either before or after k04a
3553 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 10000, 211000));
3554 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000));
3555 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000));
3556 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000));
3557 ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000));
3558 c.ResetTableReader();
3559}
3560
3561static void DoCompressionTest(CompressionType comp) {
3562 Random rnd(301);
3563 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
3564 std::string tmp;
3565 c.Add("k01", "hello");
3566 c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
3567 c.Add("k03", "hello3");
3568 c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
3569 std::vector<std::string> keys;
3570 stl_wrappers::KVMap kvmap;
3571 Options options;
3572 test::PlainInternalKeyComparator ikc(options.comparator);
3573 options.compression = comp;
3574 BlockBasedTableOptions table_options;
3575 table_options.block_size = 1024;
3576 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
3577 const MutableCFOptions moptions(options);
3578 c.Finish(options, ioptions, moptions, table_options, ikc, &keys, &kvmap);
7c673cae 3579
20effc67
TL
3580 ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
3581 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
3582 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
3583 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3500));
3584 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3500));
3585 ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 7000));
7c673cae
FG
3586 c.ResetTableReader();
3587}
3588
3589TEST_F(GeneralTableTest, ApproximateOffsetOfCompressed) {
3590 std::vector<CompressionType> compression_state;
3591 if (!Snappy_Supported()) {
3592 fprintf(stderr, "skipping snappy compression tests\n");
3593 } else {
3594 compression_state.push_back(kSnappyCompression);
3595 }
3596
3597 if (!Zlib_Supported()) {
3598 fprintf(stderr, "skipping zlib compression tests\n");
3599 } else {
3600 compression_state.push_back(kZlibCompression);
3601 }
3602
3603 // TODO(kailiu) DoCompressionTest() doesn't work with BZip2.
3604 /*
3605 if (!BZip2_Supported()) {
3606 fprintf(stderr, "skipping bzip2 compression tests\n");
3607 } else {
3608 compression_state.push_back(kBZip2Compression);
3609 }
3610 */
3611
3612 if (!LZ4_Supported()) {
3613 fprintf(stderr, "skipping lz4 and lz4hc compression tests\n");
3614 } else {
3615 compression_state.push_back(kLZ4Compression);
3616 compression_state.push_back(kLZ4HCCompression);
3617 }
3618
3619 if (!XPRESS_Supported()) {
3620 fprintf(stderr, "skipping xpress and xpress compression tests\n");
3621 }
3622 else {
3623 compression_state.push_back(kXpressCompression);
3624 }
3625
3626 for (auto state : compression_state) {
3627 DoCompressionTest(state);
3628 }
3629}
3630
494da23a 3631#ifndef ROCKSDB_VALGRIND_RUN
20effc67
TL
3632TEST_P(ParameterizedHarnessTest, RandomizedHarnessTest) {
3633 Random rnd(test::RandomSeed() + 5);
3634 for (int num_entries = 0; num_entries < 2000;
3635 num_entries += (num_entries < 50 ? 1 : 200)) {
3636 for (int e = 0; e < num_entries; e++) {
3637 Add(test::RandomKey(&rnd, rnd.Skewed(4)),
3638 rnd.RandomString(rnd.Skewed(5)));
3639 }
3640 Test(&rnd);
3641 }
7c673cae
FG
3642}
3643
3644#ifndef ROCKSDB_LITE
20effc67 3645TEST_F(DBHarnessTest, RandomizedLongDB) {
7c673cae 3646 Random rnd(test::RandomSeed());
7c673cae
FG
3647 int num_entries = 100000;
3648 for (int e = 0; e < num_entries; e++) {
3649 std::string v;
20effc67 3650 Add(test::RandomKey(&rnd, rnd.Skewed(4)), rnd.RandomString(rnd.Skewed(5)));
7c673cae
FG
3651 }
3652 Test(&rnd);
3653
3654 // We must have created enough data to force merging
3655 int files = 0;
3656 for (int level = 0; level < db()->NumberLevels(); level++) {
3657 std::string value;
3658 char name[100];
3659 snprintf(name, sizeof(name), "rocksdb.num-files-at-level%d", level);
3660 ASSERT_TRUE(db()->GetProperty(name, &value));
3661 files += atoi(value.c_str());
3662 }
3663 ASSERT_GT(files, 0);
3664}
3665#endif // ROCKSDB_LITE
494da23a 3666#endif // ROCKSDB_VALGRIND_RUN
7c673cae 3667
20effc67
TL
3668class MemTableTest : public testing::Test {
3669 public:
3670 MemTableTest() {
3671 InternalKeyComparator cmp(BytewiseComparator());
3672 auto table_factory = std::make_shared<SkipListFactory>();
3673 options_.memtable_factory = table_factory;
3674 ImmutableCFOptions ioptions(options_);
3675 wb_ = new WriteBufferManager(options_.db_write_buffer_size);
3676 memtable_ = new MemTable(cmp, ioptions, MutableCFOptions(options_), wb_,
3677 kMaxSequenceNumber, 0 /* column_family_id */);
3678 memtable_->Ref();
3679 }
3680
3681 ~MemTableTest() {
3682 delete memtable_->Unref();
3683 delete wb_;
3684 }
3685
3686 MemTable* GetMemTable() { return memtable_; }
3687
3688 private:
3689 MemTable* memtable_;
3690 Options options_;
3691 WriteBufferManager* wb_;
3692};
7c673cae
FG
3693
3694TEST_F(MemTableTest, Simple) {
7c673cae
FG
3695 WriteBatch batch;
3696 WriteBatchInternal::SetSequence(&batch, 100);
20effc67
TL
3697 ASSERT_OK(batch.Put(std::string("k1"), std::string("v1")));
3698 ASSERT_OK(batch.Put(std::string("k2"), std::string("v2")));
3699 ASSERT_OK(batch.Put(std::string("k3"), std::string("v3")));
3700 ASSERT_OK(batch.Put(std::string("largekey"), std::string("vlarge")));
3701 ASSERT_OK(batch.DeleteRange(std::string("chi"), std::string("xigua")));
3702 ASSERT_OK(batch.DeleteRange(std::string("begin"), std::string("end")));
3703 ColumnFamilyMemTablesDefault cf_mems_default(GetMemTable());
7c673cae 3704 ASSERT_TRUE(
f67539c2
TL
3705 WriteBatchInternal::InsertInto(&batch, &cf_mems_default, nullptr, nullptr)
3706 .ok());
7c673cae
FG
3707
3708 for (int i = 0; i < 2; ++i) {
3709 Arena arena;
3710 ScopedArenaIterator arena_iter_guard;
3711 std::unique_ptr<InternalIterator> iter_guard;
3712 InternalIterator* iter;
3713 if (i == 0) {
20effc67 3714 iter = GetMemTable()->NewIterator(ReadOptions(), &arena);
7c673cae
FG
3715 arena_iter_guard.set(iter);
3716 } else {
20effc67 3717 iter = GetMemTable()->NewRangeTombstoneIterator(
494da23a 3718 ReadOptions(), kMaxSequenceNumber /* read_seq */);
7c673cae
FG
3719 iter_guard.reset(iter);
3720 }
3721 if (iter == nullptr) {
3722 continue;
3723 }
3724 iter->SeekToFirst();
3725 while (iter->Valid()) {
3726 fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(),
3727 iter->value().ToString().c_str());
3728 iter->Next();
3729 }
3730 }
7c673cae
FG
3731}
3732
3733// Test the empty key
20effc67
TL
3734TEST_P(ParameterizedHarnessTest, SimpleEmptyKey) {
3735 Random rnd(test::RandomSeed() + 1);
3736 Add("", "v");
3737 Test(&rnd);
7c673cae
FG
3738}
3739
20effc67
TL
3740TEST_P(ParameterizedHarnessTest, SimpleSingle) {
3741 Random rnd(test::RandomSeed() + 2);
3742 Add("abc", "v");
3743 Test(&rnd);
7c673cae
FG
3744}
3745
20effc67
TL
3746TEST_P(ParameterizedHarnessTest, SimpleMulti) {
3747 Random rnd(test::RandomSeed() + 3);
3748 Add("abc", "v");
3749 Add("abcd", "v");
3750 Add("ac", "v2");
3751 Test(&rnd);
7c673cae
FG
3752}
3753
20effc67
TL
3754TEST_P(ParameterizedHarnessTest, SimpleSpecialKey) {
3755 Random rnd(test::RandomSeed() + 4);
3756 Add("\xff\xff", "v3");
3757 Test(&rnd);
7c673cae
FG
3758}
3759
20effc67 3760TEST(TableTest, FooterTests) {
7c673cae
FG
3761 {
3762 // upconvert legacy block based
3763 std::string encoded;
3764 Footer footer(kLegacyBlockBasedTableMagicNumber, 0);
3765 BlockHandle meta_index(10, 5), index(20, 15);
3766 footer.set_metaindex_handle(meta_index);
3767 footer.set_index_handle(index);
3768 footer.EncodeTo(&encoded);
3769 Footer decoded_footer;
3770 Slice encoded_slice(encoded);
20effc67 3771 ASSERT_OK(decoded_footer.DecodeFrom(&encoded_slice));
7c673cae
FG
3772 ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
3773 ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
3774 ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
3775 ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
3776 ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
3777 ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
3778 ASSERT_EQ(decoded_footer.version(), 0U);
3779 }
3780 {
3781 // xxhash block based
3782 std::string encoded;
3783 Footer footer(kBlockBasedTableMagicNumber, 1);
3784 BlockHandle meta_index(10, 5), index(20, 15);
3785 footer.set_metaindex_handle(meta_index);
3786 footer.set_index_handle(index);
3787 footer.set_checksum(kxxHash);
3788 footer.EncodeTo(&encoded);
3789 Footer decoded_footer;
3790 Slice encoded_slice(encoded);
20effc67 3791 ASSERT_OK(decoded_footer.DecodeFrom(&encoded_slice));
7c673cae
FG
3792 ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
3793 ASSERT_EQ(decoded_footer.checksum(), kxxHash);
3794 ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
3795 ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
3796 ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
3797 ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
3798 ASSERT_EQ(decoded_footer.version(), 1U);
3799 }
494da23a
TL
3800 {
3801 // xxhash64 block based
3802 std::string encoded;
3803 Footer footer(kBlockBasedTableMagicNumber, 1);
3804 BlockHandle meta_index(10, 5), index(20, 15);
3805 footer.set_metaindex_handle(meta_index);
3806 footer.set_index_handle(index);
3807 footer.set_checksum(kxxHash64);
3808 footer.EncodeTo(&encoded);
3809 Footer decoded_footer;
3810 Slice encoded_slice(encoded);
20effc67 3811 ASSERT_OK(decoded_footer.DecodeFrom(&encoded_slice));
494da23a
TL
3812 ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
3813 ASSERT_EQ(decoded_footer.checksum(), kxxHash64);
3814 ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
3815 ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
3816 ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
3817 ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
3818 ASSERT_EQ(decoded_footer.version(), 1U);
3819 }
7c673cae
FG
3820// Plain table is not supported in ROCKSDB_LITE
3821#ifndef ROCKSDB_LITE
3822 {
3823 // upconvert legacy plain table
3824 std::string encoded;
3825 Footer footer(kLegacyPlainTableMagicNumber, 0);
3826 BlockHandle meta_index(10, 5), index(20, 15);
3827 footer.set_metaindex_handle(meta_index);
3828 footer.set_index_handle(index);
3829 footer.EncodeTo(&encoded);
3830 Footer decoded_footer;
3831 Slice encoded_slice(encoded);
20effc67 3832 ASSERT_OK(decoded_footer.DecodeFrom(&encoded_slice));
7c673cae
FG
3833 ASSERT_EQ(decoded_footer.table_magic_number(), kPlainTableMagicNumber);
3834 ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
3835 ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
3836 ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
3837 ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
3838 ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
3839 ASSERT_EQ(decoded_footer.version(), 0U);
3840 }
3841 {
3842 // xxhash block based
3843 std::string encoded;
3844 Footer footer(kPlainTableMagicNumber, 1);
3845 BlockHandle meta_index(10, 5), index(20, 15);
3846 footer.set_metaindex_handle(meta_index);
3847 footer.set_index_handle(index);
3848 footer.set_checksum(kxxHash);
3849 footer.EncodeTo(&encoded);
3850 Footer decoded_footer;
3851 Slice encoded_slice(encoded);
20effc67 3852 ASSERT_OK(decoded_footer.DecodeFrom(&encoded_slice));
7c673cae
FG
3853 ASSERT_EQ(decoded_footer.table_magic_number(), kPlainTableMagicNumber);
3854 ASSERT_EQ(decoded_footer.checksum(), kxxHash);
3855 ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
3856 ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
3857 ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
3858 ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
3859 ASSERT_EQ(decoded_footer.version(), 1U);
3860 }
3861#endif // !ROCKSDB_LITE
3862 {
3863 // version == 2
3864 std::string encoded;
3865 Footer footer(kBlockBasedTableMagicNumber, 2);
3866 BlockHandle meta_index(10, 5), index(20, 15);
3867 footer.set_metaindex_handle(meta_index);
3868 footer.set_index_handle(index);
3869 footer.EncodeTo(&encoded);
3870 Footer decoded_footer;
3871 Slice encoded_slice(encoded);
20effc67 3872 ASSERT_OK(decoded_footer.DecodeFrom(&encoded_slice));
7c673cae
FG
3873 ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
3874 ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
3875 ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
3876 ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
3877 ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
3878 ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
3879 ASSERT_EQ(decoded_footer.version(), 2U);
3880 }
3881}
3882
3883class IndexBlockRestartIntervalTest
11fdf7f2
TL
3884 : public TableTest,
3885 public ::testing::WithParamInterface<std::pair<int, bool>> {
7c673cae 3886 public:
11fdf7f2
TL
3887 static std::vector<std::pair<int, bool>> GetRestartValues() {
3888 return {{-1, false}, {0, false}, {1, false}, {8, false},
3889 {16, false}, {32, false}, {-1, true}, {0, true},
3890 {1, true}, {8, true}, {16, true}, {32, true}};
3891 }
7c673cae
FG
3892};
3893
3894INSTANTIATE_TEST_CASE_P(
3895 IndexBlockRestartIntervalTest, IndexBlockRestartIntervalTest,
3896 ::testing::ValuesIn(IndexBlockRestartIntervalTest::GetRestartValues()));
3897
3898TEST_P(IndexBlockRestartIntervalTest, IndexBlockRestartInterval) {
3899 const int kKeysInTable = 10000;
3900 const int kKeySize = 100;
3901 const int kValSize = 500;
3902
11fdf7f2
TL
3903 const int index_block_restart_interval = std::get<0>(GetParam());
3904 const bool value_delta_encoding = std::get<1>(GetParam());
7c673cae
FG
3905
3906 Options options;
3907 BlockBasedTableOptions table_options;
3908 table_options.block_size = 64; // small block size to get big index block
3909 table_options.index_block_restart_interval = index_block_restart_interval;
11fdf7f2
TL
3910 if (value_delta_encoding) {
3911 table_options.format_version = 4;
3912 }
7c673cae
FG
3913 options.table_factory.reset(new BlockBasedTableFactory(table_options));
3914
3915 TableConstructor c(BytewiseComparator());
3916 static Random rnd(301);
3917 for (int i = 0; i < kKeysInTable; i++) {
20effc67
TL
3918 InternalKey k(rnd.RandomString(kKeySize), 0, kTypeValue);
3919 c.Add(k.Encode().ToString(), rnd.RandomString(kValSize));
7c673cae
FG
3920 }
3921
3922 std::vector<std::string> keys;
3923 stl_wrappers::KVMap kvmap;
3924 std::unique_ptr<InternalKeyComparator> comparator(
3925 new InternalKeyComparator(BytewiseComparator()));
3926 const ImmutableCFOptions ioptions(options);
11fdf7f2
TL
3927 const MutableCFOptions moptions(options);
3928 c.Finish(options, ioptions, moptions, table_options, *comparator, &keys,
3929 &kvmap);
7c673cae
FG
3930 auto reader = c.GetTableReader();
3931
20effc67 3932 ReadOptions read_options;
f67539c2 3933 std::unique_ptr<InternalIterator> db_iter(reader->NewIterator(
20effc67 3934 read_options, moptions.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2 3935 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
7c673cae
FG
3936
3937 // Test point lookup
3938 for (auto& kv : kvmap) {
3939 db_iter->Seek(kv.first);
3940
3941 ASSERT_TRUE(db_iter->Valid());
3942 ASSERT_OK(db_iter->status());
3943 ASSERT_EQ(db_iter->key(), kv.first);
3944 ASSERT_EQ(db_iter->value(), kv.second);
3945 }
3946
3947 // Test iterating
3948 auto kv_iter = kvmap.begin();
3949 for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
3950 ASSERT_EQ(db_iter->key(), kv_iter->first);
3951 ASSERT_EQ(db_iter->value(), kv_iter->second);
3952 kv_iter++;
3953 }
3954 ASSERT_EQ(kv_iter, kvmap.end());
3955 c.ResetTableReader();
3956}
3957
3958class PrefixTest : public testing::Test {
3959 public:
3960 PrefixTest() : testing::Test() {}
494da23a 3961 ~PrefixTest() override {}
7c673cae
FG
3962};
3963
3964namespace {
3965// A simple PrefixExtractor that only works for test PrefixAndWholeKeyTest
f67539c2 3966class TestPrefixExtractor : public ROCKSDB_NAMESPACE::SliceTransform {
7c673cae
FG
3967 public:
3968 ~TestPrefixExtractor() override{};
3969 const char* Name() const override { return "TestPrefixExtractor"; }
3970
f67539c2
TL
3971 ROCKSDB_NAMESPACE::Slice Transform(
3972 const ROCKSDB_NAMESPACE::Slice& src) const override {
7c673cae 3973 assert(IsValid(src));
f67539c2 3974 return ROCKSDB_NAMESPACE::Slice(src.data(), 3);
7c673cae
FG
3975 }
3976
f67539c2 3977 bool InDomain(const ROCKSDB_NAMESPACE::Slice& src) const override {
7c673cae
FG
3978 assert(IsValid(src));
3979 return true;
3980 }
3981
f67539c2
TL
3982 bool InRange(const ROCKSDB_NAMESPACE::Slice& /*dst*/) const override {
3983 return true;
3984 }
7c673cae 3985
f67539c2 3986 bool IsValid(const ROCKSDB_NAMESPACE::Slice& src) const {
7c673cae
FG
3987 if (src.size() != 4) {
3988 return false;
3989 }
3990 if (src[0] != '[') {
3991 return false;
3992 }
3993 if (src[1] < '0' || src[1] > '9') {
3994 return false;
3995 }
3996 if (src[2] != ']') {
3997 return false;
3998 }
3999 if (src[3] < '0' || src[3] > '9') {
4000 return false;
4001 }
4002 return true;
4003 }
4004};
4005} // namespace
4006
4007TEST_F(PrefixTest, PrefixAndWholeKeyTest) {
f67539c2
TL
4008 ROCKSDB_NAMESPACE::Options options;
4009 options.compaction_style = ROCKSDB_NAMESPACE::kCompactionStyleUniversal;
7c673cae
FG
4010 options.num_levels = 20;
4011 options.create_if_missing = true;
4012 options.optimize_filters_for_hits = false;
4013 options.target_file_size_base = 268435456;
4014 options.prefix_extractor = std::make_shared<TestPrefixExtractor>();
f67539c2
TL
4015 ROCKSDB_NAMESPACE::BlockBasedTableOptions bbto;
4016 bbto.filter_policy.reset(ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10));
7c673cae
FG
4017 bbto.block_size = 262144;
4018 bbto.whole_key_filtering = true;
4019
11fdf7f2 4020 const std::string kDBPath = test::PerThreadDBPath("table_prefix_test");
7c673cae
FG
4021 options.table_factory.reset(NewBlockBasedTableFactory(bbto));
4022 DestroyDB(kDBPath, options);
f67539c2
TL
4023 ROCKSDB_NAMESPACE::DB* db;
4024 ASSERT_OK(ROCKSDB_NAMESPACE::DB::Open(options, kDBPath, &db));
7c673cae
FG
4025
4026 // Create a bunch of keys with 10 filters.
4027 for (int i = 0; i < 10; i++) {
4028 std::string prefix = "[" + std::to_string(i) + "]";
4029 for (int j = 0; j < 10; j++) {
4030 std::string key = prefix + std::to_string(j);
20effc67 4031 ASSERT_OK(db->Put(ROCKSDB_NAMESPACE::WriteOptions(), key, "1"));
7c673cae
FG
4032 }
4033 }
4034
4035 // Trigger compaction.
20effc67 4036 ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, nullptr));
7c673cae
FG
4037 delete db;
4038 // In the second round, turn whole_key_filtering off and expect
4039 // rocksdb still works.
4040}
4041
11fdf7f2
TL
4042/*
4043 * Disable TableWithGlobalSeqno since RocksDB does not store global_seqno in
4044 * the SST file any more. Instead, RocksDB deduces global_seqno from the
4045 * MANIFEST while reading from an SST. Therefore, it's not possible to test the
4046 * functionality of global_seqno in a single, isolated unit test without the
4047 * involvement of Version, VersionSet, etc.
4048 */
4049TEST_P(BlockBasedTableTest, DISABLED_TableWithGlobalSeqno) {
4050 BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
7c673cae 4051 test::StringSink* sink = new test::StringSink();
494da23a 4052 std::unique_ptr<WritableFileWriter> file_writer(
11fdf7f2 4053 test::GetWritableFileWriter(sink, "" /* don't care */));
7c673cae
FG
4054 Options options;
4055 options.table_factory.reset(NewBlockBasedTableFactory(bbto));
4056 const ImmutableCFOptions ioptions(options);
11fdf7f2 4057 const MutableCFOptions moptions(options);
7c673cae
FG
4058 InternalKeyComparator ikc(options.comparator);
4059 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
4060 int_tbl_prop_collector_factories;
4061 int_tbl_prop_collector_factories.emplace_back(
4062 new SstFileWriterPropertiesCollectorFactory(2 /* version */,
4063 0 /* global_seqno*/));
4064 std::string column_family_name;
4065 std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
11fdf7f2
TL
4066 TableBuilderOptions(ioptions, moptions, ikc,
4067 &int_tbl_prop_collector_factories, kNoCompression,
494da23a 4068 0 /* sample_for_compression */, CompressionOptions(),
7c673cae
FG
4069 false /* skip_filters */, column_family_name, -1),
4070 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
4071 file_writer.get()));
4072
4073 for (char c = 'a'; c <= 'z'; ++c) {
4074 std::string key(8, c);
4075 std::string value = key;
4076 InternalKey ik(key, 0, kTypeValue);
4077
4078 builder->Add(ik.Encode(), value);
4079 }
4080 ASSERT_OK(builder->Finish());
4081 file_writer->Flush();
4082
4083 test::RandomRWStringSink ss_rw(sink);
4084 uint32_t version;
4085 uint64_t global_seqno;
4086 uint64_t global_seqno_offset;
4087
4088 // Helper function to get version, global_seqno, global_seqno_offset
4089 std::function<void()> GetVersionAndGlobalSeqno = [&]() {
494da23a 4090 std::unique_ptr<RandomAccessFileReader> file_reader(
7c673cae
FG
4091 test::GetRandomAccessFileReader(
4092 new test::StringSource(ss_rw.contents(), 73342, true)));
4093
4094 TableProperties* props = nullptr;
4095 ASSERT_OK(ReadTableProperties(file_reader.get(), ss_rw.contents().size(),
4096 kBlockBasedTableMagicNumber, ioptions,
11fdf7f2 4097 &props, true /* compression_type_missing */));
7c673cae
FG
4098
4099 UserCollectedProperties user_props = props->user_collected_properties;
4100 version = DecodeFixed32(
4101 user_props[ExternalSstFilePropertyNames::kVersion].c_str());
4102 global_seqno = DecodeFixed64(
4103 user_props[ExternalSstFilePropertyNames::kGlobalSeqno].c_str());
4104 global_seqno_offset =
4105 props->properties_offsets[ExternalSstFilePropertyNames::kGlobalSeqno];
4106
4107 delete props;
4108 };
4109
4110 // Helper function to update the value of the global seqno in the file
4111 std::function<void(uint64_t)> SetGlobalSeqno = [&](uint64_t val) {
4112 std::string new_global_seqno;
4113 PutFixed64(&new_global_seqno, val);
4114
4115 ASSERT_OK(ss_rw.Write(global_seqno_offset, new_global_seqno));
4116 };
4117
4118 // Helper function to get the contents of the table InternalIterator
494da23a 4119 std::unique_ptr<TableReader> table_reader;
20effc67 4120 const ReadOptions read_options;
7c673cae 4121 std::function<InternalIterator*()> GetTableInternalIter = [&]() {
494da23a 4122 std::unique_ptr<RandomAccessFileReader> file_reader(
7c673cae
FG
4123 test::GetRandomAccessFileReader(
4124 new test::StringSource(ss_rw.contents(), 73342, true)));
4125
4126 options.table_factory->NewTableReader(
11fdf7f2
TL
4127 TableReaderOptions(ioptions, moptions.prefix_extractor.get(),
4128 EnvOptions(), ikc),
4129 std::move(file_reader), ss_rw.contents().size(), &table_reader);
7c673cae 4130
f67539c2 4131 return table_reader->NewIterator(
20effc67 4132 read_options, moptions.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2 4133 /*skip_filters=*/false, TableReaderCaller::kUncategorized);
7c673cae
FG
4134 };
4135
4136 GetVersionAndGlobalSeqno();
f67539c2
TL
4137 ASSERT_EQ(2u, version);
4138 ASSERT_EQ(0u, global_seqno);
7c673cae
FG
4139
4140 InternalIterator* iter = GetTableInternalIter();
4141 char current_c = 'a';
4142 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
4143 ParsedInternalKey pik;
20effc67 4144 ASSERT_OK(ParseInternalKey(iter->key(), &pik, true /* log_err_key */));
7c673cae
FG
4145
4146 ASSERT_EQ(pik.type, ValueType::kTypeValue);
4147 ASSERT_EQ(pik.sequence, 0);
4148 ASSERT_EQ(pik.user_key, iter->value());
4149 ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
4150 current_c++;
4151 }
4152 ASSERT_EQ(current_c, 'z' + 1);
4153 delete iter;
4154
4155 // Update global sequence number to 10
4156 SetGlobalSeqno(10);
4157 GetVersionAndGlobalSeqno();
f67539c2
TL
4158 ASSERT_EQ(2u, version);
4159 ASSERT_EQ(10u, global_seqno);
7c673cae
FG
4160
4161 iter = GetTableInternalIter();
4162 current_c = 'a';
4163 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
4164 ParsedInternalKey pik;
20effc67 4165 ASSERT_OK(ParseInternalKey(iter->key(), &pik, true /* log_err_key */));
7c673cae
FG
4166
4167 ASSERT_EQ(pik.type, ValueType::kTypeValue);
4168 ASSERT_EQ(pik.sequence, 10);
4169 ASSERT_EQ(pik.user_key, iter->value());
4170 ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
4171 current_c++;
4172 }
4173 ASSERT_EQ(current_c, 'z' + 1);
4174
4175 // Verify Seek
4176 for (char c = 'a'; c <= 'z'; c++) {
4177 std::string k = std::string(8, c);
4178 InternalKey ik(k, 10, kValueTypeForSeek);
4179 iter->Seek(ik.Encode());
4180 ASSERT_TRUE(iter->Valid());
4181
4182 ParsedInternalKey pik;
20effc67 4183 ASSERT_OK(ParseInternalKey(iter->key(), &pik, true /* log_err_key */));
7c673cae
FG
4184
4185 ASSERT_EQ(pik.type, ValueType::kTypeValue);
4186 ASSERT_EQ(pik.sequence, 10);
4187 ASSERT_EQ(pik.user_key.ToString(), k);
4188 ASSERT_EQ(iter->value().ToString(), k);
4189 }
4190 delete iter;
4191
4192 // Update global sequence number to 3
4193 SetGlobalSeqno(3);
4194 GetVersionAndGlobalSeqno();
f67539c2
TL
4195 ASSERT_EQ(2u, version);
4196 ASSERT_EQ(3u, global_seqno);
7c673cae
FG
4197
4198 iter = GetTableInternalIter();
4199 current_c = 'a';
4200 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
4201 ParsedInternalKey pik;
20effc67 4202 ASSERT_OK(ParseInternalKey(iter->key(), &pik, true /* log_err_key */));
7c673cae
FG
4203
4204 ASSERT_EQ(pik.type, ValueType::kTypeValue);
4205 ASSERT_EQ(pik.sequence, 3);
4206 ASSERT_EQ(pik.user_key, iter->value());
4207 ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
4208 current_c++;
4209 }
4210 ASSERT_EQ(current_c, 'z' + 1);
4211
4212 // Verify Seek
4213 for (char c = 'a'; c <= 'z'; c++) {
4214 std::string k = std::string(8, c);
4215 // seqno=4 is less than 3 so we still should get our key
4216 InternalKey ik(k, 4, kValueTypeForSeek);
4217 iter->Seek(ik.Encode());
4218 ASSERT_TRUE(iter->Valid());
4219
4220 ParsedInternalKey pik;
20effc67 4221 ASSERT_OK(ParseInternalKey(iter->key(), &pik, true /* log_err_key */));
7c673cae
FG
4222
4223 ASSERT_EQ(pik.type, ValueType::kTypeValue);
4224 ASSERT_EQ(pik.sequence, 3);
4225 ASSERT_EQ(pik.user_key.ToString(), k);
4226 ASSERT_EQ(iter->value().ToString(), k);
4227 }
4228
4229 delete iter;
4230}
4231
11fdf7f2
TL
4232TEST_P(BlockBasedTableTest, BlockAlignTest) {
4233 BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
4234 bbto.block_align = true;
4235 test::StringSink* sink = new test::StringSink();
494da23a 4236 std::unique_ptr<WritableFileWriter> file_writer(
11fdf7f2
TL
4237 test::GetWritableFileWriter(sink, "" /* don't care */));
4238 Options options;
4239 options.compression = kNoCompression;
4240 options.table_factory.reset(NewBlockBasedTableFactory(bbto));
4241 const ImmutableCFOptions ioptions(options);
4242 const MutableCFOptions moptions(options);
4243 InternalKeyComparator ikc(options.comparator);
4244 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
4245 int_tbl_prop_collector_factories;
4246 std::string column_family_name;
4247 std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
4248 TableBuilderOptions(ioptions, moptions, ikc,
4249 &int_tbl_prop_collector_factories, kNoCompression,
494da23a 4250 0 /* sample_for_compression */, CompressionOptions(),
11fdf7f2
TL
4251 false /* skip_filters */, column_family_name, -1),
4252 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
4253 file_writer.get()));
4254
4255 for (int i = 1; i <= 10000; ++i) {
4256 std::ostringstream ostr;
4257 ostr << std::setfill('0') << std::setw(5) << i;
4258 std::string key = ostr.str();
4259 std::string value = "val";
4260 InternalKey ik(key, 0, kTypeValue);
4261
4262 builder->Add(ik.Encode(), value);
4263 }
4264 ASSERT_OK(builder->Finish());
4265 file_writer->Flush();
4266
4267 test::RandomRWStringSink ss_rw(sink);
494da23a 4268 std::unique_ptr<RandomAccessFileReader> file_reader(
11fdf7f2
TL
4269 test::GetRandomAccessFileReader(
4270 new test::StringSource(ss_rw.contents(), 73342, true)));
4271
4272 // Helper function to get version, global_seqno, global_seqno_offset
4273 std::function<void()> VerifyBlockAlignment = [&]() {
4274 TableProperties* props = nullptr;
4275 ASSERT_OK(ReadTableProperties(file_reader.get(), ss_rw.contents().size(),
4276 kBlockBasedTableMagicNumber, ioptions,
4277 &props, true /* compression_type_missing */));
4278
4279 uint64_t data_block_size = props->data_size / props->num_data_blocks;
4280 ASSERT_EQ(data_block_size, 4096);
4281 ASSERT_EQ(props->data_size, data_block_size * props->num_data_blocks);
4282 delete props;
4283 };
4284
4285 VerifyBlockAlignment();
4286
4287 // The below block of code verifies that we can read back the keys. Set
4288 // block_align to false when creating the reader to ensure we can flip between
4289 // the two modes without any issues
4290 std::unique_ptr<TableReader> table_reader;
4291 bbto.block_align = false;
4292 Options options2;
4293 options2.table_factory.reset(NewBlockBasedTableFactory(bbto));
4294 ImmutableCFOptions ioptions2(options2);
4295 const MutableCFOptions moptions2(options2);
4296
4297 ASSERT_OK(ioptions.table_factory->NewTableReader(
4298 TableReaderOptions(ioptions2, moptions2.prefix_extractor.get(),
4299 EnvOptions(),
4300 GetPlainInternalComparator(options2.comparator)),
4301 std::move(file_reader), ss_rw.contents().size(), &table_reader));
4302
20effc67 4303 ReadOptions read_options;
11fdf7f2 4304 std::unique_ptr<InternalIterator> db_iter(table_reader->NewIterator(
20effc67 4305 read_options, moptions2.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2 4306 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
11fdf7f2
TL
4307
4308 int expected_key = 1;
4309 for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
4310 std::ostringstream ostr;
4311 ostr << std::setfill('0') << std::setw(5) << expected_key++;
4312 std::string key = ostr.str();
4313 std::string value = "val";
4314
4315 ASSERT_OK(db_iter->status());
4316 ASSERT_EQ(ExtractUserKey(db_iter->key()).ToString(), key);
4317 ASSERT_EQ(db_iter->value().ToString(), value);
4318 }
4319 expected_key--;
4320 ASSERT_EQ(expected_key, 10000);
4321 table_reader.reset();
4322}
4323
4324TEST_P(BlockBasedTableTest, PropertiesBlockRestartPointTest) {
4325 BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
4326 bbto.block_align = true;
4327 test::StringSink* sink = new test::StringSink();
494da23a 4328 std::unique_ptr<WritableFileWriter> file_writer(
11fdf7f2
TL
4329 test::GetWritableFileWriter(sink, "" /* don't care */));
4330
4331 Options options;
4332 options.compression = kNoCompression;
4333 options.table_factory.reset(NewBlockBasedTableFactory(bbto));
4334
4335 const ImmutableCFOptions ioptions(options);
4336 const MutableCFOptions moptions(options);
4337 InternalKeyComparator ikc(options.comparator);
4338 std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
4339 int_tbl_prop_collector_factories;
4340 std::string column_family_name;
4341
4342 std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
4343 TableBuilderOptions(ioptions, moptions, ikc,
4344 &int_tbl_prop_collector_factories, kNoCompression,
494da23a 4345 0 /* sample_for_compression */, CompressionOptions(),
11fdf7f2
TL
4346 false /* skip_filters */, column_family_name, -1),
4347 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
4348 file_writer.get()));
4349
4350 for (int i = 1; i <= 10000; ++i) {
4351 std::ostringstream ostr;
4352 ostr << std::setfill('0') << std::setw(5) << i;
4353 std::string key = ostr.str();
4354 std::string value = "val";
4355 InternalKey ik(key, 0, kTypeValue);
4356
4357 builder->Add(ik.Encode(), value);
4358 }
4359 ASSERT_OK(builder->Finish());
4360 file_writer->Flush();
4361
4362 test::RandomRWStringSink ss_rw(sink);
494da23a 4363 std::unique_ptr<RandomAccessFileReader> file_reader(
11fdf7f2
TL
4364 test::GetRandomAccessFileReader(
4365 new test::StringSource(ss_rw.contents(), 73342, true)));
4366
4367 {
4368 RandomAccessFileReader* file = file_reader.get();
4369 uint64_t file_size = ss_rw.contents().size();
4370
4371 Footer footer;
20effc67
TL
4372 IOOptions opts;
4373 ASSERT_OK(ReadFooterFromFile(opts, file, nullptr /* prefetch_buffer */,
4374 file_size, &footer,
4375 kBlockBasedTableMagicNumber));
11fdf7f2 4376
f67539c2 4377 auto BlockFetchHelper = [&](const BlockHandle& handle, BlockType block_type,
11fdf7f2
TL
4378 BlockContents* contents) {
4379 ReadOptions read_options;
4380 read_options.verify_checksums = false;
11fdf7f2
TL
4381 PersistentCacheOptions cache_options;
4382
494da23a
TL
4383 BlockFetcher block_fetcher(
4384 file, nullptr /* prefetch_buffer */, footer, read_options, handle,
4385 contents, ioptions, false /* decompress */,
f67539c2
TL
4386 false /*maybe_compressed*/, block_type,
4387 UncompressionDict::GetEmptyDict(), cache_options);
11fdf7f2
TL
4388
4389 ASSERT_OK(block_fetcher.ReadBlockContents());
4390 };
4391
4392 // -- Read metaindex block
4393 auto metaindex_handle = footer.metaindex_handle();
4394 BlockContents metaindex_contents;
4395
f67539c2
TL
4396 BlockFetchHelper(metaindex_handle, BlockType::kMetaIndex,
4397 &metaindex_contents);
20effc67 4398 Block metaindex_block(std::move(metaindex_contents));
11fdf7f2 4399
f67539c2 4400 std::unique_ptr<InternalIterator> meta_iter(metaindex_block.NewDataIterator(
20effc67 4401 BytewiseComparator(), kDisableGlobalSequenceNumber));
11fdf7f2
TL
4402 bool found_properties_block = true;
4403 ASSERT_OK(SeekToPropertiesBlock(meta_iter.get(), &found_properties_block));
4404 ASSERT_TRUE(found_properties_block);
4405
4406 // -- Read properties block
4407 Slice v = meta_iter->value();
4408 BlockHandle properties_handle;
4409 ASSERT_OK(properties_handle.DecodeFrom(&v));
4410 BlockContents properties_contents;
4411
f67539c2
TL
4412 BlockFetchHelper(properties_handle, BlockType::kProperties,
4413 &properties_contents);
20effc67 4414 Block properties_block(std::move(properties_contents));
11fdf7f2 4415
f67539c2 4416 ASSERT_EQ(properties_block.NumRestarts(), 1u);
11fdf7f2
TL
4417 }
4418}
4419
4420TEST_P(BlockBasedTableTest, PropertiesMetaBlockLast) {
4421 // The properties meta-block should come at the end since we always need to
4422 // read it when opening a file, unlike index/filter/other meta-blocks, which
4423 // are sometimes read depending on the user's configuration. This ordering
4424 // allows us to do a small readahead on the end of the file to read properties
4425 // and meta-index blocks with one I/O.
4426 TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
4427 c.Add("a1", "val1");
4428 c.Add("b2", "val2");
4429 c.Add("c3", "val3");
4430 c.Add("d4", "val4");
4431 c.Add("e5", "val5");
4432 c.Add("f6", "val6");
4433 c.Add("g7", "val7");
4434 c.Add("h8", "val8");
4435 c.Add("j9", "val9");
4436
4437 // write an SST file
4438 Options options;
4439 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
4440 table_options.filter_policy.reset(NewBloomFilterPolicy(
4441 8 /* bits_per_key */, false /* use_block_based_filter */));
4442 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
4443 ImmutableCFOptions ioptions(options);
4444 MutableCFOptions moptions(options);
4445 std::vector<std::string> keys;
4446 stl_wrappers::KVMap kvmap;
4447 c.Finish(options, ioptions, moptions, table_options,
4448 GetPlainInternalComparator(options.comparator), &keys, &kvmap);
4449
4450 // get file reader
4451 test::StringSink* table_sink = c.TEST_GetSink();
4452 std::unique_ptr<RandomAccessFileReader> table_reader{
4453 test::GetRandomAccessFileReader(
4454 new test::StringSource(table_sink->contents(), 0 /* unique_id */,
4455 false /* allow_mmap_reads */))};
4456 size_t table_size = table_sink->contents().size();
4457
4458 // read footer
4459 Footer footer;
20effc67
TL
4460 IOOptions opts;
4461 ASSERT_OK(ReadFooterFromFile(opts, table_reader.get(),
11fdf7f2
TL
4462 nullptr /* prefetch_buffer */, table_size,
4463 &footer, kBlockBasedTableMagicNumber));
4464
4465 // read metaindex
4466 auto metaindex_handle = footer.metaindex_handle();
4467 BlockContents metaindex_contents;
11fdf7f2
TL
4468 PersistentCacheOptions pcache_opts;
4469 BlockFetcher block_fetcher(
4470 table_reader.get(), nullptr /* prefetch_buffer */, footer, ReadOptions(),
4471 metaindex_handle, &metaindex_contents, ioptions, false /* decompress */,
f67539c2
TL
4472 false /*maybe_compressed*/, BlockType::kMetaIndex,
4473 UncompressionDict::GetEmptyDict(), pcache_opts,
4474 nullptr /*memory_allocator*/);
11fdf7f2 4475 ASSERT_OK(block_fetcher.ReadBlockContents());
20effc67 4476 Block metaindex_block(std::move(metaindex_contents));
11fdf7f2
TL
4477
4478 // verify properties block comes last
4479 std::unique_ptr<InternalIterator> metaindex_iter{
20effc67
TL
4480 metaindex_block.NewDataIterator(options.comparator,
4481 kDisableGlobalSequenceNumber)};
11fdf7f2
TL
4482 uint64_t max_offset = 0;
4483 std::string key_at_max_offset;
4484 for (metaindex_iter->SeekToFirst(); metaindex_iter->Valid();
4485 metaindex_iter->Next()) {
4486 BlockHandle handle;
4487 Slice value = metaindex_iter->value();
4488 ASSERT_OK(handle.DecodeFrom(&value));
4489 if (handle.offset() > max_offset) {
4490 max_offset = handle.offset();
4491 key_at_max_offset = metaindex_iter->key().ToString();
4492 }
4493 }
4494 ASSERT_EQ(kPropertiesBlock, key_at_max_offset);
4495 // index handle is stored in footer rather than metaindex block, so need
4496 // separate logic to verify it comes before properties block.
4497 ASSERT_GT(max_offset, footer.index_handle().offset());
4498 c.ResetTableReader();
4499}
4500
4501TEST_P(BlockBasedTableTest, BadOptions) {
f67539c2 4502 ROCKSDB_NAMESPACE::Options options;
11fdf7f2
TL
4503 options.compression = kNoCompression;
4504 BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
4505 bbto.block_size = 4000;
4506 bbto.block_align = true;
4507
4508 const std::string kDBPath =
4509 test::PerThreadDBPath("block_based_table_bad_options_test");
4510 options.table_factory.reset(NewBlockBasedTableFactory(bbto));
4511 DestroyDB(kDBPath, options);
f67539c2
TL
4512 ROCKSDB_NAMESPACE::DB* db;
4513 ASSERT_NOK(ROCKSDB_NAMESPACE::DB::Open(options, kDBPath, &db));
11fdf7f2
TL
4514
4515 bbto.block_size = 4096;
4516 options.compression = kSnappyCompression;
4517 options.table_factory.reset(NewBlockBasedTableFactory(bbto));
f67539c2 4518 ASSERT_NOK(ROCKSDB_NAMESPACE::DB::Open(options, kDBPath, &db));
11fdf7f2
TL
4519}
4520
4521TEST_F(BBTTailPrefetchTest, TestTailPrefetchStats) {
4522 TailPrefetchStats tpstats;
4523 ASSERT_EQ(0, tpstats.GetSuggestedPrefetchSize());
4524 tpstats.RecordEffectiveSize(size_t{1000});
4525 tpstats.RecordEffectiveSize(size_t{1005});
4526 tpstats.RecordEffectiveSize(size_t{1002});
4527 ASSERT_EQ(1005, tpstats.GetSuggestedPrefetchSize());
4528
4529 // One single super large value shouldn't influence much
4530 tpstats.RecordEffectiveSize(size_t{1002000});
4531 tpstats.RecordEffectiveSize(size_t{999});
4532 ASSERT_LE(1005, tpstats.GetSuggestedPrefetchSize());
4533 ASSERT_GT(1200, tpstats.GetSuggestedPrefetchSize());
4534
4535 // Only history of 32 is kept
4536 for (int i = 0; i < 32; i++) {
4537 tpstats.RecordEffectiveSize(size_t{100});
4538 }
4539 ASSERT_EQ(100, tpstats.GetSuggestedPrefetchSize());
4540
4541 // 16 large values and 16 small values. The result should be closer
4542 // to the small value as the algorithm.
4543 for (int i = 0; i < 16; i++) {
4544 tpstats.RecordEffectiveSize(size_t{1000});
4545 }
4546 tpstats.RecordEffectiveSize(size_t{10});
4547 tpstats.RecordEffectiveSize(size_t{20});
4548 for (int i = 0; i < 6; i++) {
4549 tpstats.RecordEffectiveSize(size_t{100});
4550 }
4551 ASSERT_LE(80, tpstats.GetSuggestedPrefetchSize());
4552 ASSERT_GT(200, tpstats.GetSuggestedPrefetchSize());
4553}
4554
4555TEST_F(BBTTailPrefetchTest, FilePrefetchBufferMinOffset) {
4556 TailPrefetchStats tpstats;
4557 FilePrefetchBuffer buffer(nullptr, 0, 0, false, true);
20effc67
TL
4558 IOOptions opts;
4559 buffer.TryReadFromCache(opts, 500, 10, nullptr);
4560 buffer.TryReadFromCache(opts, 480, 10, nullptr);
4561 buffer.TryReadFromCache(opts, 490, 10, nullptr);
11fdf7f2
TL
4562 ASSERT_EQ(480, buffer.min_offset_read());
4563}
4564
4565TEST_P(BlockBasedTableTest, DataBlockHashIndex) {
4566 const int kNumKeys = 500;
4567 const int kKeySize = 8;
4568 const int kValSize = 40;
4569
4570 BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
4571 table_options.data_block_index_type =
4572 BlockBasedTableOptions::kDataBlockBinaryAndHash;
4573
4574 Options options;
4575 options.comparator = BytewiseComparator();
4576
4577 options.table_factory.reset(new BlockBasedTableFactory(table_options));
4578
4579 TableConstructor c(options.comparator);
4580
4581 static Random rnd(1048);
4582 for (int i = 0; i < kNumKeys; i++) {
4583 // padding one "0" to mark existent keys.
20effc67 4584 std::string random_key(rnd.RandomString(kKeySize - 1) + "1");
11fdf7f2 4585 InternalKey k(random_key, 0, kTypeValue);
20effc67 4586 c.Add(k.Encode().ToString(), rnd.RandomString(kValSize));
11fdf7f2
TL
4587 }
4588
4589 std::vector<std::string> keys;
4590 stl_wrappers::KVMap kvmap;
4591 const ImmutableCFOptions ioptions(options);
4592 const MutableCFOptions moptions(options);
4593 const InternalKeyComparator internal_comparator(options.comparator);
4594 c.Finish(options, ioptions, moptions, table_options, internal_comparator,
4595 &keys, &kvmap);
4596
4597 auto reader = c.GetTableReader();
4598
4599 std::unique_ptr<InternalIterator> seek_iter;
20effc67 4600 ReadOptions read_options;
f67539c2 4601 seek_iter.reset(reader->NewIterator(
20effc67 4602 read_options, moptions.prefix_extractor.get(), /*arena=*/nullptr,
f67539c2 4603 /*skip_filters=*/false, TableReaderCaller::kUncategorized));
11fdf7f2
TL
4604 for (int i = 0; i < 2; ++i) {
4605 ReadOptions ro;
4606 // for every kv, we seek using two method: Get() and Seek()
4607 // Get() will use the SuffixIndexHash in Block. For non-existent key it
4608 // will invalidate the iterator
4609 // Seek() will use the default BinarySeek() in Block. So for non-existent
4610 // key it will land at the closest key that is large than target.
4611
4612 // Search for existent keys
4613 for (auto& kv : kvmap) {
4614 if (i == 0) {
4615 // Search using Seek()
4616 seek_iter->Seek(kv.first);
4617 ASSERT_OK(seek_iter->status());
4618 ASSERT_TRUE(seek_iter->Valid());
4619 ASSERT_EQ(seek_iter->key(), kv.first);
4620 ASSERT_EQ(seek_iter->value(), kv.second);
4621 } else {
4622 // Search using Get()
4623 PinnableSlice value;
4624 std::string user_key = ExtractUserKey(kv.first).ToString();
4625 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
4626 GetContext::kNotFound, user_key, &value, nullptr,
f67539c2 4627 nullptr, true, nullptr, nullptr);
11fdf7f2
TL
4628 ASSERT_OK(reader->Get(ro, kv.first, &get_context,
4629 moptions.prefix_extractor.get()));
4630 ASSERT_EQ(get_context.State(), GetContext::kFound);
4631 ASSERT_EQ(value, Slice(kv.second));
4632 value.Reset();
4633 }
4634 }
4635
4636 // Search for non-existent keys
4637 for (auto& kv : kvmap) {
4638 std::string user_key = ExtractUserKey(kv.first).ToString();
4639 user_key.back() = '0'; // make it non-existent key
4640 InternalKey internal_key(user_key, 0, kTypeValue);
4641 std::string encoded_key = internal_key.Encode().ToString();
4642 if (i == 0) { // Search using Seek()
4643 seek_iter->Seek(encoded_key);
4644 ASSERT_OK(seek_iter->status());
4645 if (seek_iter->Valid()) {
4646 ASSERT_TRUE(BytewiseComparator()->Compare(
4647 user_key, ExtractUserKey(seek_iter->key())) < 0);
4648 }
4649 } else { // Search using Get()
4650 PinnableSlice value;
4651 GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
4652 GetContext::kNotFound, user_key, &value, nullptr,
f67539c2 4653 nullptr, true, nullptr, nullptr);
11fdf7f2
TL
4654 ASSERT_OK(reader->Get(ro, encoded_key, &get_context,
4655 moptions.prefix_extractor.get()));
4656 ASSERT_EQ(get_context.State(), GetContext::kNotFound);
4657 value.Reset();
4658 }
4659 }
4660 }
4661}
4662
f67539c2
TL
4663// BlockBasedTableIterator should invalidate itself and return
4664// OutOfBound()=true immediately after Seek(), to allow LevelIterator
4665// filter out corresponding level.
4666TEST_P(BlockBasedTableTest, OutOfBoundOnSeek) {
4667 TableConstructor c(BytewiseComparator(), true /*convert_to_internal_key*/);
4668 c.Add("foo", "v1");
4669 std::vector<std::string> keys;
4670 stl_wrappers::KVMap kvmap;
4671 Options options;
4672 BlockBasedTableOptions table_opt(GetBlockBasedTableOptions());
4673 options.table_factory.reset(NewBlockBasedTableFactory(table_opt));
4674 const ImmutableCFOptions ioptions(options);
4675 const MutableCFOptions moptions(options);
4676 c.Finish(options, ioptions, moptions, table_opt,
4677 GetPlainInternalComparator(BytewiseComparator()), &keys, &kvmap);
4678 auto* reader = c.GetTableReader();
4679 ReadOptions read_opt;
4680 std::string upper_bound = "bar";
4681 Slice upper_bound_slice(upper_bound);
4682 read_opt.iterate_upper_bound = &upper_bound_slice;
4683 std::unique_ptr<InternalIterator> iter;
4684 iter.reset(new KeyConvertingIterator(reader->NewIterator(
4685 read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
4686 /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
4687 iter->SeekToFirst();
4688 ASSERT_FALSE(iter->Valid());
20effc67
TL
4689 ASSERT_OK(iter->status());
4690 ASSERT_TRUE(iter->UpperBoundCheckResult() == IterBoundCheck::kOutOfBound);
f67539c2
TL
4691 iter.reset(new KeyConvertingIterator(reader->NewIterator(
4692 read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
4693 /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
4694 iter->Seek("foo");
4695 ASSERT_FALSE(iter->Valid());
20effc67
TL
4696 ASSERT_OK(iter->status());
4697 ASSERT_TRUE(iter->UpperBoundCheckResult() == IterBoundCheck::kOutOfBound);
f67539c2
TL
4698}
4699
4700// BlockBasedTableIterator should invalidate itself and return
4701// OutOfBound()=true after Next(), if it finds current index key is no smaller
4702// than upper bound, unless it is pointing to the last data block.
4703TEST_P(BlockBasedTableTest, OutOfBoundOnNext) {
4704 TableConstructor c(BytewiseComparator(), true /*convert_to_internal_key*/);
4705 c.Add("bar", "v");
4706 c.Add("foo", "v");
4707 std::vector<std::string> keys;
4708 stl_wrappers::KVMap kvmap;
4709 Options options;
4710 BlockBasedTableOptions table_opt(GetBlockBasedTableOptions());
4711 table_opt.flush_block_policy_factory =
4712 std::make_shared<FlushBlockEveryKeyPolicyFactory>();
4713 options.table_factory.reset(NewBlockBasedTableFactory(table_opt));
4714 const ImmutableCFOptions ioptions(options);
4715 const MutableCFOptions moptions(options);
4716 c.Finish(options, ioptions, moptions, table_opt,
4717 GetPlainInternalComparator(BytewiseComparator()), &keys, &kvmap);
4718 auto* reader = c.GetTableReader();
4719 ReadOptions read_opt;
4720 std::string ub1 = "bar_after";
4721 Slice ub_slice1(ub1);
4722 read_opt.iterate_upper_bound = &ub_slice1;
4723 std::unique_ptr<InternalIterator> iter;
4724 iter.reset(new KeyConvertingIterator(reader->NewIterator(
4725 read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
4726 /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
4727 iter->Seek("bar");
4728 ASSERT_TRUE(iter->Valid());
4729 ASSERT_EQ("bar", iter->key());
4730 iter->Next();
4731 ASSERT_FALSE(iter->Valid());
20effc67 4732 ASSERT_TRUE(iter->UpperBoundCheckResult() == IterBoundCheck::kOutOfBound);
f67539c2
TL
4733 std::string ub2 = "foo_after";
4734 Slice ub_slice2(ub2);
4735 read_opt.iterate_upper_bound = &ub_slice2;
4736 iter.reset(new KeyConvertingIterator(reader->NewIterator(
4737 read_opt, /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
4738 /*skip_filters=*/false, TableReaderCaller::kUncategorized)));
4739 iter->Seek("foo");
4740 ASSERT_TRUE(iter->Valid());
4741 ASSERT_EQ("foo", iter->key());
4742 iter->Next();
4743 ASSERT_FALSE(iter->Valid());
20effc67 4744 ASSERT_FALSE(iter->UpperBoundCheckResult() == IterBoundCheck::kOutOfBound);
f67539c2
TL
4745}
4746
4747} // namespace ROCKSDB_NAMESPACE
7c673cae
FG
4748
4749int main(int argc, char** argv) {
4750 ::testing::InitGoogleTest(&argc, argv);
4751 return RUN_ALL_TESTS();
4752}