table_test.cc 112.3 KB
Newer Older
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
5
//
J
jorlow@chromium.org 已提交
6 7 8
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
9 10

#include <stdio.h>
11

K
Kai Liu 已提交
12
#include <algorithm>
13
#include <iostream>
J
jorlow@chromium.org 已提交
14
#include <map>
J
Jim Paton 已提交
15
#include <memory>
16
#include <string>
K
Kai Liu 已提交
17 18
#include <vector>

M
Maysam Yabandeh 已提交
19
#include "cache/lru_cache.h"
J
jorlow@chromium.org 已提交
20 21 22
#include "db/dbformat.h"
#include "db/memtable.h"
#include "db/write_batch_internal.h"
23
#include "memtable/stl_wrappers.h"
24
#include "monitoring/statistics.h"
D
Dmitri Smirnov 已提交
25
#include "port/port.h"
K
Kai Liu 已提交
26
#include "rocksdb/cache.h"
27 28 29 30
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/iterator.h"
#include "rocksdb/memtablerep.h"
I
Igor Canadi 已提交
31
#include "rocksdb/perf_context.h"
32 33
#include "rocksdb/slice_transform.h"
#include "rocksdb/statistics.h"
34
#include "rocksdb/write_buffer_manager.h"
K
kailiu 已提交
35
#include "table/block.h"
K
Kai Liu 已提交
36
#include "table/block_based_table_builder.h"
37
#include "table/block_based_table_factory.h"
K
Kai Liu 已提交
38
#include "table/block_based_table_reader.h"
J
jorlow@chromium.org 已提交
39 40
#include "table/block_builder.h"
#include "table/format.h"
41
#include "table/get_context.h"
S
sdong 已提交
42
#include "table/internal_iterator.h"
K
kailiu 已提交
43 44
#include "table/meta_blocks.h"
#include "table/plain_table_factory.h"
S
sdong 已提交
45
#include "table/scoped_arena_iterator.h"
46
#include "table/sst_file_writer_collectors.h"
I
Igor Canadi 已提交
47
#include "util/compression.h"
J
jorlow@chromium.org 已提交
48
#include "util/random.h"
49
#include "util/string_util.h"
50
#include "util/sync_point.h"
J
jorlow@chromium.org 已提交
51 52
#include "util/testharness.h"
#include "util/testutil.h"
53
#include "utilities/merge_operators.h"
54

55
namespace rocksdb {
J
jorlow@chromium.org 已提交
56

I
xxHash  
Igor Canadi 已提交
57 58 59 60 61
extern const uint64_t kLegacyBlockBasedTableMagicNumber;
extern const uint64_t kLegacyPlainTableMagicNumber;
extern const uint64_t kBlockBasedTableMagicNumber;
extern const uint64_t kPlainTableMagicNumber;

62
namespace {
K
Kai Liu 已提交
63

64 65 66 67 68
// DummyPropertiesCollector used to test BlockBasedTableProperties
class DummyPropertiesCollector : public TablePropertiesCollector {
 public:
  const char* Name() const { return ""; }

69
  Status Finish(UserCollectedProperties* properties) { return Status::OK(); }
70

71
  Status Add(const Slice& user_key, const Slice& value) { return Status::OK(); }
72 73 74 75 76 77 78 79 80 81

  virtual UserCollectedProperties GetReadableProperties() const {
    return UserCollectedProperties{};
  }
};

class DummyPropertiesCollectorFactory1
    : public TablePropertiesCollectorFactory {
 public:
  virtual TablePropertiesCollector* CreateTablePropertiesCollector(
82
      TablePropertiesCollectorFactory::Context context) {
83 84 85 86 87 88 89 90 91
    return new DummyPropertiesCollector();
  }
  const char* Name() const { return "DummyPropertiesCollector1"; }
};

class DummyPropertiesCollectorFactory2
    : public TablePropertiesCollectorFactory {
 public:
  virtual TablePropertiesCollector* CreateTablePropertiesCollector(
92
      TablePropertiesCollectorFactory::Context context) {
93 94 95 96 97
    return new DummyPropertiesCollector();
  }
  const char* Name() const { return "DummyPropertiesCollector2"; }
};

J
jorlow@chromium.org 已提交
98 99
// Return reverse of "key".
// Used to test non-lexicographic comparators.
K
Kai Liu 已提交
100 101 102
std::string Reverse(const Slice& key) {
  auto rev = key.ToString();
  std::reverse(rev.begin(), rev.end());
J
jorlow@chromium.org 已提交
103 104 105 106 107
  return rev;
}

class ReverseKeyComparator : public Comparator {
 public:
I
Igor Sugak 已提交
108
  virtual const char* Name() const override {
109
    return "rocksdb.ReverseBytewiseComparator";
J
jorlow@chromium.org 已提交
110 111
  }

I
Igor Sugak 已提交
112
  virtual int Compare(const Slice& a, const Slice& b) const override {
J
jorlow@chromium.org 已提交
113 114 115
    return BytewiseComparator()->Compare(Reverse(a), Reverse(b));
  }

I
Igor Sugak 已提交
116 117
  virtual void FindShortestSeparator(std::string* start,
                                     const Slice& limit) const override {
J
jorlow@chromium.org 已提交
118 119 120 121 122 123
    std::string s = Reverse(*start);
    std::string l = Reverse(limit);
    BytewiseComparator()->FindShortestSeparator(&s, l);
    *start = Reverse(s);
  }

I
Igor Sugak 已提交
124
  virtual void FindShortSuccessor(std::string* key) const override {
J
jorlow@chromium.org 已提交
125 126 127 128 129 130
    std::string s = Reverse(*key);
    BytewiseComparator()->FindShortSuccessor(&s);
    *key = Reverse(s);
  }
};

K
Kai Liu 已提交
131 132 133
ReverseKeyComparator reverse_key_comparator;

void Increment(const Comparator* cmp, std::string* key) {
J
jorlow@chromium.org 已提交
134 135 136 137 138 139 140 141 142 143
  if (cmp == BytewiseComparator()) {
    key->push_back('\0');
  } else {
    assert(cmp == &reverse_key_comparator);
    std::string rev = Reverse(*key);
    rev.push_back('\0');
    *key = Reverse(rev);
  }
}

H
Hans Wennborg 已提交
144
}  // namespace
J
jorlow@chromium.org 已提交
145 146 147 148 149

// Helper class for tests to unify the interface between
// BlockBuilder/TableBuilder and Block/Table.
class Constructor {
 public:
150 151
  explicit Constructor(const Comparator* cmp)
      : data_(stl_wrappers::LessOfComparator(cmp)) {}
J
jorlow@chromium.org 已提交
152 153 154 155 156 157 158 159 160
  virtual ~Constructor() { }

  void Add(const std::string& key, const Slice& value) {
    data_[key] = value.ToString();
  }

  // Finish constructing the data structure with all the keys that have
  // been added so far.  Returns the keys in sorted order in "*keys"
  // and stores the key/value pairs in "*kvmap"
161
  void Finish(const Options& options, const ImmutableCFOptions& ioptions,
162
              const BlockBasedTableOptions& table_options,
163
              const InternalKeyComparator& internal_comparator,
164
              std::vector<std::string>* keys, stl_wrappers::KVMap* kvmap) {
165
    last_internal_key_ = &internal_comparator;
J
jorlow@chromium.org 已提交
166 167
    *kvmap = data_;
    keys->clear();
168 169
    for (const auto& kv : data_) {
      keys->push_back(kv.first);
J
jorlow@chromium.org 已提交
170 171
    }
    data_.clear();
L
Lei Jin 已提交
172 173
    Status s = FinishImpl(options, ioptions, table_options,
                          internal_comparator, *kvmap);
J
jorlow@chromium.org 已提交
174 175 176 177
    ASSERT_TRUE(s.ok()) << s.ToString();
  }

  // Construct the data structure from the data in "data"
178
  virtual Status FinishImpl(const Options& options,
L
Lei Jin 已提交
179
                            const ImmutableCFOptions& ioptions,
180
                            const BlockBasedTableOptions& table_options,
181
                            const InternalKeyComparator& internal_comparator,
182
                            const stl_wrappers::KVMap& data) = 0;
J
jorlow@chromium.org 已提交
183

S
sdong 已提交
184
  virtual InternalIterator* NewIterator() const = 0;
J
jorlow@chromium.org 已提交
185

186
  virtual const stl_wrappers::KVMap& data() { return data_; }
J
jorlow@chromium.org 已提交
187

188 189
  virtual bool IsArenaMode() const { return false; }

A
Abhishek Kona 已提交
190
  virtual DB* db() const { return nullptr; }  // Overridden in DBConstructor
J
jorlow@chromium.org 已提交
191

192 193
  virtual bool AnywayDeleteIterator() const { return false; }

194 195 196
 protected:
  const InternalKeyComparator* last_internal_key_;

J
jorlow@chromium.org 已提交
197
 private:
198
  stl_wrappers::KVMap data_;
J
jorlow@chromium.org 已提交
199 200 201 202 203 204 205
};

class BlockConstructor: public Constructor {
 public:
  explicit BlockConstructor(const Comparator* cmp)
      : Constructor(cmp),
        comparator_(cmp),
A
Abhishek Kona 已提交
206
        block_(nullptr) { }
J
jorlow@chromium.org 已提交
207 208 209
  ~BlockConstructor() {
    delete block_;
  }
210 211 212 213 214
  virtual Status FinishImpl(const Options& options,
                            const ImmutableCFOptions& ioptions,
                            const BlockBasedTableOptions& table_options,
                            const InternalKeyComparator& internal_comparator,
                            const stl_wrappers::KVMap& kv_map) override {
J
jorlow@chromium.org 已提交
215
    delete block_;
A
Abhishek Kona 已提交
216
    block_ = nullptr;
I
Igor Canadi 已提交
217
    BlockBuilder builder(table_options.block_restart_interval);
J
jorlow@chromium.org 已提交
218

I
Igor Canadi 已提交
219 220
    for (const auto kv : kv_map) {
      builder.Add(kv.first, kv.second);
J
jorlow@chromium.org 已提交
221 222
    }
    // Open the block
S
Sanjay Ghemawat 已提交
223 224 225 226
    data_ = builder.Finish().ToString();
    BlockContents contents;
    contents.data = data_;
    contents.cachable = false;
227
    block_ = new Block(std::move(contents), kDisableGlobalSequenceNumber);
J
jorlow@chromium.org 已提交
228 229
    return Status::OK();
  }
S
sdong 已提交
230
  virtual InternalIterator* NewIterator() const override {
J
jorlow@chromium.org 已提交
231 232 233 234 235
    return block_->NewIterator(comparator_);
  }

 private:
  const Comparator* comparator_;
S
Sanjay Ghemawat 已提交
236
  std::string data_;
J
jorlow@chromium.org 已提交
237 238 239 240 241
  Block* block_;

  BlockConstructor();
};

242
// A helper class that converts internal format keys into user keys
S
sdong 已提交
243
class KeyConvertingIterator : public InternalIterator {
J
jorlow@chromium.org 已提交
244
 public:
S
sdong 已提交
245 246
  explicit KeyConvertingIterator(InternalIterator* iter,
                                 bool arena_mode = false)
247 248 249
      : iter_(iter), arena_mode_(arena_mode) {}
  virtual ~KeyConvertingIterator() {
    if (arena_mode_) {
S
sdong 已提交
250
      iter_->~InternalIterator();
251 252 253 254
    } else {
      delete iter_;
    }
  }
I
Igor Sugak 已提交
255 256
  virtual bool Valid() const override { return iter_->Valid(); }
  virtual void Seek(const Slice& target) override {
257 258 259 260 261
    ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
    std::string encoded;
    AppendInternalKey(&encoded, ikey);
    iter_->Seek(encoded);
  }
A
Aaron Gao 已提交
262 263 264 265 266 267
  virtual void SeekForPrev(const Slice& target) override {
    ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
    std::string encoded;
    AppendInternalKey(&encoded, ikey);
    iter_->SeekForPrev(encoded);
  }
I
Igor Sugak 已提交
268 269 270 271
  virtual void SeekToFirst() override { iter_->SeekToFirst(); }
  virtual void SeekToLast() override { iter_->SeekToLast(); }
  virtual void Next() override { iter_->Next(); }
  virtual void Prev() override { iter_->Prev(); }
272

I
Igor Sugak 已提交
273
  virtual Slice key() const override {
274
    assert(Valid());
I
Igor Canadi 已提交
275 276
    ParsedInternalKey parsed_key;
    if (!ParseInternalKey(iter_->key(), &parsed_key)) {
277 278 279
      status_ = Status::Corruption("malformed internal key");
      return Slice("corrupted key");
    }
I
Igor Canadi 已提交
280
    return parsed_key.user_key;
J
jorlow@chromium.org 已提交
281
  }
282

I
Igor Sugak 已提交
283 284
  virtual Slice value() const override { return iter_->value(); }
  virtual Status status() const override {
285 286 287 288 289
    return status_.ok() ? iter_->status() : status_;
  }

 private:
  mutable Status status_;
S
sdong 已提交
290
  InternalIterator* iter_;
291
  bool arena_mode_;
292 293 294 295 296 297 298 299

  // No copying allowed
  KeyConvertingIterator(const KeyConvertingIterator&);
  void operator=(const KeyConvertingIterator&);
};

class TableConstructor: public Constructor {
 public:
K
kailiu 已提交
300
  explicit TableConstructor(const Comparator* cmp,
301
                            bool convert_to_internal_key = false)
302
      : Constructor(cmp),
303
        convert_to_internal_key_(convert_to_internal_key) {}
K
kailiu 已提交
304
  ~TableConstructor() { Reset(); }
305

306
  virtual Status FinishImpl(const Options& options,
L
Lei Jin 已提交
307
                            const ImmutableCFOptions& ioptions,
308
                            const BlockBasedTableOptions& table_options,
309
                            const InternalKeyComparator& internal_comparator,
310
                            const stl_wrappers::KVMap& kv_map) override {
J
jorlow@chromium.org 已提交
311
    Reset();
312
    soptions.use_mmap_reads = ioptions.allow_mmap_reads;
A
Andres Notzli 已提交
313
    file_writer_.reset(test::GetWritableFileWriter(new test::StringSink()));
314
    unique_ptr<TableBuilder> builder;
315 316
    std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
        int_tbl_prop_collector_factories;
317
    std::string column_family_name;
318
    int unknown_level = -1;
L
Lei Jin 已提交
319
    builder.reset(ioptions.table_factory->NewTableBuilder(
320 321 322 323
        TableBuilderOptions(ioptions, internal_comparator,
                            &int_tbl_prop_collector_factories,
                            options.compression, CompressionOptions(),
                            nullptr /* compression_dict */,
324 325
                            false /* skip_filters */, column_family_name,
                            unknown_level),
326
        TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
327
        file_writer_.get()));
J
jorlow@chromium.org 已提交
328

I
Igor Canadi 已提交
329
    for (const auto kv : kv_map) {
330
      if (convert_to_internal_key_) {
I
Igor Canadi 已提交
331
        ParsedInternalKey ikey(kv.first, kMaxSequenceNumber, kTypeValue);
332 333
        std::string encoded;
        AppendInternalKey(&encoded, ikey);
I
Igor Canadi 已提交
334
        builder->Add(encoded, kv.second);
335
      } else {
I
Igor Canadi 已提交
336
        builder->Add(kv.first, kv.second);
337
      }
338
      EXPECT_TRUE(builder->status().ok());
J
jorlow@chromium.org 已提交
339
    }
340
    Status s = builder->Finish();
341
    file_writer_->Flush();
342
    EXPECT_TRUE(s.ok()) << s.ToString();
J
jorlow@chromium.org 已提交
343

344
    EXPECT_EQ(GetSink()->contents().size(), builder->FileSize());
J
jorlow@chromium.org 已提交
345 346

    // Open the table
347
    uniq_id_ = cur_uniq_id_++;
A
Andres Notzli 已提交
348
    file_reader_.reset(test::GetRandomAccessFileReader(new test::StringSource(
349
        GetSink()->contents(), uniq_id_, ioptions.allow_mmap_reads)));
L
Lei Jin 已提交
350
    return ioptions.table_factory->NewTableReader(
351 352
        TableReaderOptions(ioptions, soptions, internal_comparator),
        std::move(file_reader_), GetSink()->contents().size(), &table_reader_);
J
jorlow@chromium.org 已提交
353 354
  }

S
sdong 已提交
355
  virtual InternalIterator* NewIterator() const override {
356
    ReadOptions ro;
S
sdong 已提交
357
    InternalIterator* iter = table_reader_->NewIterator(ro);
358 359 360 361 362
    if (convert_to_internal_key_) {
      return new KeyConvertingIterator(iter);
    } else {
      return iter;
    }
J
jorlow@chromium.org 已提交
363 364 365
  }

  uint64_t ApproximateOffsetOf(const Slice& key) const {
366 367 368 369 370
    if (convert_to_internal_key_) {
      InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
      const Slice skey = ikey.Encode();
      return table_reader_->ApproximateOffsetOf(skey);
    }
S
Siying Dong 已提交
371
    return table_reader_->ApproximateOffsetOf(key);
J
jorlow@chromium.org 已提交
372 373
  }

L
Lei Jin 已提交
374
  virtual Status Reopen(const ImmutableCFOptions& ioptions) {
A
Andres Notzli 已提交
375
    file_reader_.reset(test::GetRandomAccessFileReader(new test::StringSource(
376
        GetSink()->contents(), uniq_id_, ioptions.allow_mmap_reads)));
L
Lei Jin 已提交
377
    return ioptions.table_factory->NewTableReader(
378 379
        TableReaderOptions(ioptions, soptions, *last_internal_key_),
        std::move(file_reader_), GetSink()->contents().size(), &table_reader_);
380 381
  }

382
  virtual TableReader* GetTableReader() {
S
Siying Dong 已提交
383
    return table_reader_.get();
384 385
  }

386 387 388 389
  virtual bool AnywayDeleteIterator() const override {
    return convert_to_internal_key_;
  }

390 391
  void ResetTableReader() { table_reader_.reset(); }

392 393
  bool ConvertToInternalKey() { return convert_to_internal_key_; }

J
jorlow@chromium.org 已提交
394 395
 private:
  void Reset() {
396
    uniq_id_ = 0;
S
Siying Dong 已提交
397
    table_reader_.reset();
398 399 400 401
    file_writer_.reset();
    file_reader_.reset();
  }

A
Andres Notzli 已提交
402 403
  test::StringSink* GetSink() {
    return static_cast<test::StringSink*>(file_writer_->writable_file());
J
jorlow@chromium.org 已提交
404 405
  }

406
  uint64_t uniq_id_;
407 408
  unique_ptr<WritableFileWriter> file_writer_;
  unique_ptr<RandomAccessFileReader> file_reader_;
S
Siying Dong 已提交
409
  unique_ptr<TableReader> table_reader_;
410
  bool convert_to_internal_key_;
J
jorlow@chromium.org 已提交
411

412
  TableConstructor();
413 414

  static uint64_t cur_uniq_id_;
415
  EnvOptions soptions;
J
jorlow@chromium.org 已提交
416
};
417
uint64_t TableConstructor::cur_uniq_id_ = 1;
J
jorlow@chromium.org 已提交
418 419 420

class MemTableConstructor: public Constructor {
 public:
421
  explicit MemTableConstructor(const Comparator* cmp, WriteBufferManager* wb)
J
jorlow@chromium.org 已提交
422
      : Constructor(cmp),
J
Jim Paton 已提交
423
        internal_comparator_(cmp),
424
        write_buffer_manager_(wb),
J
Jim Paton 已提交
425
        table_factory_(new SkipListFactory) {
426 427
    options_.memtable_factory = table_factory_;
    ImmutableCFOptions ioptions(options_);
Y
Yi Wu 已提交
428 429
    memtable_ =
        new MemTable(internal_comparator_, ioptions, MutableCFOptions(options_),
430
                     wb, kMaxSequenceNumber, 0 /* column_family_id */);
431
    memtable_->Ref();
J
jorlow@chromium.org 已提交
432 433
  }
  ~MemTableConstructor() {
434
    delete memtable_->Unref();
J
jorlow@chromium.org 已提交
435
  }
436 437 438 439
  virtual Status FinishImpl(const Options&, const ImmutableCFOptions& ioptions,
                            const BlockBasedTableOptions& table_options,
                            const InternalKeyComparator& internal_comparator,
                            const stl_wrappers::KVMap& kv_map) override {
440
    delete memtable_->Unref();
441
    ImmutableCFOptions mem_ioptions(ioptions);
442
    memtable_ = new MemTable(internal_comparator_, mem_ioptions,
Y
Yi Wu 已提交
443
                             MutableCFOptions(options_), write_buffer_manager_,
444
                             kMaxSequenceNumber, 0 /* column_family_id */);
445
    memtable_->Ref();
J
jorlow@chromium.org 已提交
446
    int seq = 1;
I
Igor Canadi 已提交
447 448
    for (const auto kv : kv_map) {
      memtable_->Add(seq, kTypeValue, kv.first, kv.second);
J
jorlow@chromium.org 已提交
449 450 451 452
      seq++;
    }
    return Status::OK();
  }
S
sdong 已提交
453
  virtual InternalIterator* NewIterator() const override {
454 455
    return new KeyConvertingIterator(
        memtable_->NewIterator(ReadOptions(), &arena_), true);
J
jorlow@chromium.org 已提交
456 457
  }

458 459 460 461
  virtual bool AnywayDeleteIterator() const override { return true; }

  virtual bool IsArenaMode() const override { return true; }

J
jorlow@chromium.org 已提交
462
 private:
463
  mutable Arena arena_;
J
jorlow@chromium.org 已提交
464
  InternalKeyComparator internal_comparator_;
465
  Options options_;
466
  WriteBufferManager* write_buffer_manager_;
J
jorlow@chromium.org 已提交
467
  MemTable* memtable_;
J
Jim Paton 已提交
468
  std::shared_ptr<SkipListFactory> table_factory_;
J
jorlow@chromium.org 已提交
469 470
};

S
sdong 已提交
471 472 473 474 475
class InternalIteratorFromIterator : public InternalIterator {
 public:
  explicit InternalIteratorFromIterator(Iterator* it) : it_(it) {}
  virtual bool Valid() const override { return it_->Valid(); }
  virtual void Seek(const Slice& target) override { it_->Seek(target); }
A
Aaron Gao 已提交
476 477 478
  virtual void SeekForPrev(const Slice& target) override {
    it_->SeekForPrev(target);
  }
S
sdong 已提交
479 480 481 482 483 484 485 486 487 488 489 490
  virtual void SeekToFirst() override { it_->SeekToFirst(); }
  virtual void SeekToLast() override { it_->SeekToLast(); }
  virtual void Next() override { it_->Next(); }
  virtual void Prev() override { it_->Prev(); }
  Slice key() const override { return it_->key(); }
  Slice value() const override { return it_->value(); }
  virtual Status status() const override { return it_->status(); }

 private:
  unique_ptr<Iterator> it_;
};

J
jorlow@chromium.org 已提交
491 492 493 494 495
class DBConstructor: public Constructor {
 public:
  explicit DBConstructor(const Comparator* cmp)
      : Constructor(cmp),
        comparator_(cmp) {
A
Abhishek Kona 已提交
496
    db_ = nullptr;
J
jorlow@chromium.org 已提交
497 498 499 500 501
    NewDB();
  }
  ~DBConstructor() {
    delete db_;
  }
502 503 504 505 506
  virtual Status FinishImpl(const Options& options,
                            const ImmutableCFOptions& ioptions,
                            const BlockBasedTableOptions& table_options,
                            const InternalKeyComparator& internal_comparator,
                            const stl_wrappers::KVMap& kv_map) override {
J
jorlow@chromium.org 已提交
507
    delete db_;
A
Abhishek Kona 已提交
508
    db_ = nullptr;
J
jorlow@chromium.org 已提交
509
    NewDB();
I
Igor Canadi 已提交
510
    for (const auto kv : kv_map) {
J
jorlow@chromium.org 已提交
511
      WriteBatch batch;
I
Igor Canadi 已提交
512
      batch.Put(kv.first, kv.second);
513
      EXPECT_TRUE(db_->Write(WriteOptions(), &batch).ok());
J
jorlow@chromium.org 已提交
514 515 516
    }
    return Status::OK();
  }
S
sdong 已提交
517 518 519

  virtual InternalIterator* NewIterator() const override {
    return new InternalIteratorFromIterator(db_->NewIterator(ReadOptions()));
J
jorlow@chromium.org 已提交
520 521
  }

I
Igor Sugak 已提交
522
  virtual DB* db() const override { return db_; }
J
jorlow@chromium.org 已提交
523

J
jorlow@chromium.org 已提交
524 525 526 527
 private:
  void NewDB() {
    std::string name = test::TmpDir() + "/table_testdb";

528
    Options options;
J
jorlow@chromium.org 已提交
529 530 531 532 533 534
    options.comparator = comparator_;
    Status status = DestroyDB(name, options);
    ASSERT_TRUE(status.ok()) << status.ToString();

    options.create_if_missing = true;
    options.error_if_exists = true;
J
jorlow@chromium.org 已提交
535
    options.write_buffer_size = 10000;  // Something small to force merging
J
jorlow@chromium.org 已提交
536 537 538 539 540 541 542 543 544
    status = DB::Open(options, name, &db_);
    ASSERT_TRUE(status.ok()) << status.ToString();
  }

  const Comparator* comparator_;
  DB* db_;
};

enum TestType {
545
  BLOCK_BASED_TABLE_TEST,
546
#ifndef ROCKSDB_LITE
547 548
  PLAIN_TABLE_SEMI_FIXED_PREFIX,
  PLAIN_TABLE_FULL_STR_PREFIX,
549
  PLAIN_TABLE_TOTAL_ORDER,
550
#endif  // !ROCKSDB_LITE
J
jorlow@chromium.org 已提交
551 552
  BLOCK_TEST,
  MEMTABLE_TEST,
553
  DB_TEST
J
jorlow@chromium.org 已提交
554 555 556 557 558 559
};

struct TestArgs {
  TestType type;
  bool reverse_compare;
  int restart_interval;
H
heyongqiang 已提交
560
  CompressionType compression;
561
  uint32_t format_version;
562
  bool use_mmap;
J
jorlow@chromium.org 已提交
563 564
};

565
static std::vector<TestArgs> GenerateArgList() {
K
Kai Liu 已提交
566 567
  std::vector<TestArgs> test_args;
  std::vector<TestType> test_types = {
568 569 570 571 572 573 574 575
      BLOCK_BASED_TABLE_TEST,
#ifndef ROCKSDB_LITE
      PLAIN_TABLE_SEMI_FIXED_PREFIX,
      PLAIN_TABLE_FULL_STR_PREFIX,
      PLAIN_TABLE_TOTAL_ORDER,
#endif  // !ROCKSDB_LITE
      BLOCK_TEST,
      MEMTABLE_TEST, DB_TEST};
K
Kai Liu 已提交
576 577
  std::vector<bool> reverse_compare_types = {false, true};
  std::vector<int> restart_intervals = {16, 1, 1024};
H
heyongqiang 已提交
578 579

  // Only add compression if it is supported
580 581
  std::vector<std::pair<CompressionType, bool>> compression_types;
  compression_types.emplace_back(kNoCompression, false);
I
Igor Canadi 已提交
582
  if (Snappy_Supported()) {
583
    compression_types.emplace_back(kSnappyCompression, false);
K
Kai Liu 已提交
584
  }
I
Igor Canadi 已提交
585
  if (Zlib_Supported()) {
586 587
    compression_types.emplace_back(kZlibCompression, false);
    compression_types.emplace_back(kZlibCompression, true);
K
Kai Liu 已提交
588
  }
I
Igor Canadi 已提交
589
  if (BZip2_Supported()) {
590 591
    compression_types.emplace_back(kBZip2Compression, false);
    compression_types.emplace_back(kBZip2Compression, true);
K
Kai Liu 已提交
592
  }
I
Igor Canadi 已提交
593
  if (LZ4_Supported()) {
594 595 596 597
    compression_types.emplace_back(kLZ4Compression, false);
    compression_types.emplace_back(kLZ4Compression, true);
    compression_types.emplace_back(kLZ4HCCompression, false);
    compression_types.emplace_back(kLZ4HCCompression, true);
A
Albert Strasheim 已提交
598
  }
599 600 601 602
  if (XPRESS_Supported()) {
    compression_types.emplace_back(kXpressCompression, false);
    compression_types.emplace_back(kXpressCompression, true);
  }
603
  if (ZSTD_Supported()) {
S
sdong 已提交
604 605
    compression_types.emplace_back(kZSTD, false);
    compression_types.emplace_back(kZSTD, true);
606
  }
H
heyongqiang 已提交
607

K
Kai Liu 已提交
608 609
  for (auto test_type : test_types) {
    for (auto reverse_compare : reverse_compare_types) {
610
#ifndef ROCKSDB_LITE
K
Kai Liu 已提交
611
      if (test_type == PLAIN_TABLE_SEMI_FIXED_PREFIX ||
612 613
          test_type == PLAIN_TABLE_FULL_STR_PREFIX ||
          test_type == PLAIN_TABLE_TOTAL_ORDER) {
614 615
        // Plain table doesn't use restart index or compression.
        TestArgs one_arg;
K
Kai Liu 已提交
616 617 618
        one_arg.type = test_type;
        one_arg.reverse_compare = reverse_compare;
        one_arg.restart_interval = restart_intervals[0];
619
        one_arg.compression = compression_types[0].first;
620 621 622
        one_arg.use_mmap = true;
        test_args.push_back(one_arg);
        one_arg.use_mmap = false;
K
Kai Liu 已提交
623
        test_args.push_back(one_arg);
624 625
        continue;
      }
626
#endif  // !ROCKSDB_LITE
H
heyongqiang 已提交
627

K
Kai Liu 已提交
628 629
      for (auto restart_interval : restart_intervals) {
        for (auto compression_type : compression_types) {
630
          TestArgs one_arg;
K
Kai Liu 已提交
631 632 633
          one_arg.type = test_type;
          one_arg.reverse_compare = reverse_compare;
          one_arg.restart_interval = restart_interval;
634 635
          one_arg.compression = compression_type.first;
          one_arg.format_version = compression_type.second ? 2 : 1;
636
          one_arg.use_mmap = false;
K
Kai Liu 已提交
637
          test_args.push_back(one_arg);
638
        }
K
Kai Liu 已提交
639
      }
640
    }
K
Kai Liu 已提交
641 642
  }
  return test_args;
H
heyongqiang 已提交
643
}
J
jorlow@chromium.org 已提交
644

645 646 647 648 649 650 651 652 653 654 655 656 657
// In order to make all tests run for plain table format, including
// those operating on empty keys, create a new prefix transformer which
// return fixed prefix if the slice is not shorter than the prefix length,
// and the full slice if it is shorter.
class FixedOrLessPrefixTransform : public SliceTransform {
 private:
  const size_t prefix_len_;

 public:
  explicit FixedOrLessPrefixTransform(size_t prefix_len) :
      prefix_len_(prefix_len) {
  }

I
Igor Sugak 已提交
658
  virtual const char* Name() const override { return "rocksdb.FixedPrefix"; }
659

I
Igor Sugak 已提交
660
  virtual Slice Transform(const Slice& src) const override {
661 662 663 664 665 666 667
    assert(InDomain(src));
    if (src.size() < prefix_len_) {
      return src;
    }
    return Slice(src.data(), prefix_len_);
  }

668
  virtual bool InDomain(const Slice& src) const override { return true; }
669

I
Igor Sugak 已提交
670
  virtual bool InRange(const Slice& dst) const override {
671 672 673 674
    return (dst.size() <= prefix_len_);
  }
};

I
Igor Sugak 已提交
675
class HarnessTest : public testing::Test {
J
jorlow@chromium.org 已提交
676
 public:
677
  HarnessTest()
678 679 680
      : ioptions_(options_),
        constructor_(nullptr),
        write_buffer_(options_.db_write_buffer_size) {}
J
jorlow@chromium.org 已提交
681 682 683

  void Init(const TestArgs& args) {
    delete constructor_;
A
Abhishek Kona 已提交
684
    constructor_ = nullptr;
685
    options_ = Options();
H
heyongqiang 已提交
686
    options_.compression = args.compression;
J
jorlow@chromium.org 已提交
687 688 689 690 691
    // Use shorter block size for tests to exercise block boundary
    // conditions more.
    if (args.reverse_compare) {
      options_.comparator = &reverse_key_comparator;
    }
692 693 694 695

    internal_comparator_.reset(
        new test::PlainInternalKeyComparator(options_.comparator));

696 697
    support_prev_ = true;
    only_support_prefix_seek_ = false;
698
    options_.allow_mmap_reads = args.use_mmap;
J
jorlow@chromium.org 已提交
699
    switch (args.type) {
700
      case BLOCK_BASED_TABLE_TEST:
701
        table_options_.flush_block_policy_factory.reset(
702
            new FlushBlockBySizePolicyFactory());
703 704
        table_options_.block_size = 256;
        table_options_.block_restart_interval = args.restart_interval;
705
        table_options_.index_block_restart_interval = args.restart_interval;
706
        table_options_.format_version = args.format_version;
707 708
        options_.table_factory.reset(
            new BlockBasedTableFactory(table_options_));
709 710 711 712
        constructor_ = new TableConstructor(
            options_.comparator, true /* convert_to_internal_key_ */);
        internal_comparator_.reset(
            new InternalKeyComparator(options_.comparator));
713
        break;
714 715
// Plain table is not supported in ROCKSDB_LITE
#ifndef ROCKSDB_LITE
716 717 718
      case PLAIN_TABLE_SEMI_FIXED_PREFIX:
        support_prev_ = false;
        only_support_prefix_seek_ = true;
719
        options_.prefix_extractor.reset(new FixedOrLessPrefixTransform(2));
720
        options_.table_factory.reset(NewPlainTableFactory());
721 722
        constructor_ = new TableConstructor(
            options_.comparator, true /* convert_to_internal_key_ */);
723 724
        internal_comparator_.reset(
            new InternalKeyComparator(options_.comparator));
725 726 727 728
        break;
      case PLAIN_TABLE_FULL_STR_PREFIX:
        support_prev_ = false;
        only_support_prefix_seek_ = true;
729
        options_.prefix_extractor.reset(NewNoopTransform());
730
        options_.table_factory.reset(NewPlainTableFactory());
731 732
        constructor_ = new TableConstructor(
            options_.comparator, true /* convert_to_internal_key_ */);
733 734 735 736 737 738 739
        internal_comparator_.reset(
            new InternalKeyComparator(options_.comparator));
        break;
      case PLAIN_TABLE_TOTAL_ORDER:
        support_prev_ = false;
        only_support_prefix_seek_ = false;
        options_.prefix_extractor = nullptr;
S
Stanislau Hlebik 已提交
740 741 742 743 744 745 746 747 748 749

        {
          PlainTableOptions plain_table_options;
          plain_table_options.user_key_len = kPlainTableVariableLength;
          plain_table_options.bloom_bits_per_key = 0;
          plain_table_options.hash_table_ratio = 0;

          options_.table_factory.reset(
              NewPlainTableFactory(plain_table_options));
        }
750 751
        constructor_ = new TableConstructor(
            options_.comparator, true /* convert_to_internal_key_ */);
752 753
        internal_comparator_.reset(
            new InternalKeyComparator(options_.comparator));
J
jorlow@chromium.org 已提交
754
        break;
755
#endif  // !ROCKSDB_LITE
J
jorlow@chromium.org 已提交
756
      case BLOCK_TEST:
757 758 759
        table_options_.block_size = 256;
        options_.table_factory.reset(
            new BlockBasedTableFactory(table_options_));
J
jorlow@chromium.org 已提交
760 761 762
        constructor_ = new BlockConstructor(options_.comparator);
        break;
      case MEMTABLE_TEST:
763 764 765
        table_options_.block_size = 256;
        options_.table_factory.reset(
            new BlockBasedTableFactory(table_options_));
766 767
        constructor_ = new MemTableConstructor(options_.comparator,
                                               &write_buffer_);
J
jorlow@chromium.org 已提交
768 769
        break;
      case DB_TEST:
770 771 772
        table_options_.block_size = 256;
        options_.table_factory.reset(
            new BlockBasedTableFactory(table_options_));
J
jorlow@chromium.org 已提交
773 774 775
        constructor_ = new DBConstructor(options_.comparator);
        break;
    }
L
Lei Jin 已提交
776
    ioptions_ = ImmutableCFOptions(options_);
J
jorlow@chromium.org 已提交
777 778
  }

779
  ~HarnessTest() { delete constructor_; }
J
jorlow@chromium.org 已提交
780 781 782 783 784 785 786

  void Add(const std::string& key, const std::string& value) {
    constructor_->Add(key, value);
  }

  void Test(Random* rnd) {
    std::vector<std::string> keys;
787
    stl_wrappers::KVMap data;
L
Lei Jin 已提交
788 789
    constructor_->Finish(options_, ioptions_, table_options_,
                         *internal_comparator_, &keys, &data);
J
jorlow@chromium.org 已提交
790 791

    TestForwardScan(keys, data);
792 793 794
    if (support_prev_) {
      TestBackwardScan(keys, data);
    }
J
jorlow@chromium.org 已提交
795 796 797
    TestRandomAccess(rnd, keys, data);
  }

798
  void TestForwardScan(const std::vector<std::string>& keys,
799
                       const stl_wrappers::KVMap& data) {
S
sdong 已提交
800
    InternalIterator* iter = constructor_->NewIterator();
J
jorlow@chromium.org 已提交
801 802
    ASSERT_TRUE(!iter->Valid());
    iter->SeekToFirst();
803 804
    for (stl_wrappers::KVMap::const_iterator model_iter = data.begin();
         model_iter != data.end(); ++model_iter) {
J
jorlow@chromium.org 已提交
805 806 807 808
      ASSERT_EQ(ToString(data, model_iter), ToString(iter));
      iter->Next();
    }
    ASSERT_TRUE(!iter->Valid());
809
    if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
S
sdong 已提交
810
      iter->~InternalIterator();
811 812 813
    } else {
      delete iter;
    }
J
jorlow@chromium.org 已提交
814 815
  }

816
  void TestBackwardScan(const std::vector<std::string>& keys,
817
                        const stl_wrappers::KVMap& data) {
S
sdong 已提交
818
    InternalIterator* iter = constructor_->NewIterator();
J
jorlow@chromium.org 已提交
819 820
    ASSERT_TRUE(!iter->Valid());
    iter->SeekToLast();
821 822
    for (stl_wrappers::KVMap::const_reverse_iterator model_iter = data.rbegin();
         model_iter != data.rend(); ++model_iter) {
J
jorlow@chromium.org 已提交
823 824 825 826
      ASSERT_EQ(ToString(data, model_iter), ToString(iter));
      iter->Prev();
    }
    ASSERT_TRUE(!iter->Valid());
827
    if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
S
sdong 已提交
828
      iter->~InternalIterator();
829 830 831
    } else {
      delete iter;
    }
J
jorlow@chromium.org 已提交
832 833
  }

834 835
  void TestRandomAccess(Random* rnd, const std::vector<std::string>& keys,
                        const stl_wrappers::KVMap& data) {
J
jorlow@chromium.org 已提交
836
    static const bool kVerbose = false;
S
sdong 已提交
837
    InternalIterator* iter = constructor_->NewIterator();
J
jorlow@chromium.org 已提交
838
    ASSERT_TRUE(!iter->Valid());
839
    stl_wrappers::KVMap::const_iterator model_iter = data.begin();
J
jorlow@chromium.org 已提交
840 841
    if (kVerbose) fprintf(stderr, "---\n");
    for (int i = 0; i < 200; i++) {
842
      const int toss = rnd->Uniform(support_prev_ ? 5 : 3);
J
jorlow@chromium.org 已提交
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
      switch (toss) {
        case 0: {
          if (iter->Valid()) {
            if (kVerbose) fprintf(stderr, "Next\n");
            iter->Next();
            ++model_iter;
            ASSERT_EQ(ToString(data, model_iter), ToString(iter));
          }
          break;
        }

        case 1: {
          if (kVerbose) fprintf(stderr, "SeekToFirst\n");
          iter->SeekToFirst();
          model_iter = data.begin();
          ASSERT_EQ(ToString(data, model_iter), ToString(iter));
          break;
        }

        case 2: {
          std::string key = PickRandomKey(rnd, keys);
          model_iter = data.lower_bound(key);
          if (kVerbose) fprintf(stderr, "Seek '%s'\n",
                                EscapeString(key).c_str());
          iter->Seek(Slice(key));
          ASSERT_EQ(ToString(data, model_iter), ToString(iter));
          break;
        }

        case 3: {
          if (iter->Valid()) {
            if (kVerbose) fprintf(stderr, "Prev\n");
            iter->Prev();
            if (model_iter == data.begin()) {
              model_iter = data.end();   // Wrap around to invalid value
            } else {
              --model_iter;
            }
            ASSERT_EQ(ToString(data, model_iter), ToString(iter));
          }
          break;
        }

        case 4: {
          if (kVerbose) fprintf(stderr, "SeekToLast\n");
          iter->SeekToLast();
          if (keys.empty()) {
            model_iter = data.end();
          } else {
            std::string last = data.rbegin()->first;
            model_iter = data.lower_bound(last);
          }
          ASSERT_EQ(ToString(data, model_iter), ToString(iter));
          break;
        }
      }
    }
900
    if (constructor_->IsArenaMode() && !constructor_->AnywayDeleteIterator()) {
S
sdong 已提交
901
      iter->~InternalIterator();
902 903 904
    } else {
      delete iter;
    }
J
jorlow@chromium.org 已提交
905 906
  }

907 908
  std::string ToString(const stl_wrappers::KVMap& data,
                       const stl_wrappers::KVMap::const_iterator& it) {
J
jorlow@chromium.org 已提交
909 910 911 912 913 914 915
    if (it == data.end()) {
      return "END";
    } else {
      return "'" + it->first + "->" + it->second + "'";
    }
  }

916 917
  std::string ToString(const stl_wrappers::KVMap& data,
                       const stl_wrappers::KVMap::const_reverse_iterator& it) {
J
jorlow@chromium.org 已提交
918 919 920 921 922 923 924
    if (it == data.rend()) {
      return "END";
    } else {
      return "'" + it->first + "->" + it->second + "'";
    }
  }

S
sdong 已提交
925
  std::string ToString(const InternalIterator* it) {
J
jorlow@chromium.org 已提交
926 927 928 929 930 931 932 933 934 935 936
    if (!it->Valid()) {
      return "END";
    } else {
      return "'" + it->key().ToString() + "->" + it->value().ToString() + "'";
    }
  }

  std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) {
    if (keys.empty()) {
      return "foo";
    } else {
937
      const int index = rnd->Uniform(static_cast<int>(keys.size()));
J
jorlow@chromium.org 已提交
938
      std::string result = keys[index];
939
      switch (rnd->Uniform(support_prev_ ? 3 : 1)) {
J
jorlow@chromium.org 已提交
940 941 942 943 944
        case 0:
          // Return an existing key
          break;
        case 1: {
          // Attempt to return something smaller than an existing key
945 946 947 948 949
          if (result.size() > 0 && result[result.size() - 1] > '\0'
              && (!only_support_prefix_seek_
                  || options_.prefix_extractor->Transform(result).size()
                  < result.size())) {
            result[result.size() - 1]--;
J
jorlow@chromium.org 已提交
950 951
          }
          break;
952
      }
J
jorlow@chromium.org 已提交
953 954 955 956 957 958 959 960 961 962
        case 2: {
          // Return something larger than an existing key
          Increment(options_.comparator, &result);
          break;
        }
      }
      return result;
    }
  }

A
Abhishek Kona 已提交
963
  // Returns nullptr if not running against a DB
J
jorlow@chromium.org 已提交
964 965
  DB* db() const { return constructor_->db(); }

966 967 968 969 970 971
  void RandomizedHarnessTest(size_t part, size_t total) {
    std::vector<TestArgs> args = GenerateArgList();
    assert(part);
    assert(part <= total);
    size_t start_i = (part - 1) * args.size() / total;
    size_t end_i = part * args.size() / total;
F
Fosco Marotto 已提交
972
    for (unsigned int i = static_cast<unsigned int>(start_i); i < end_i; i++) {
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
      Init(args[i]);
      Random rnd(test::RandomSeed() + 5);
      for (int num_entries = 0; num_entries < 2000;
           num_entries += (num_entries < 50 ? 1 : 200)) {
        if ((num_entries % 10) == 0) {
          fprintf(stderr, "case %d of %d: num_entries = %d\n", (i + 1),
                  static_cast<int>(args.size()), num_entries);
        }
        for (int e = 0; e < num_entries; e++) {
          std::string v;
          Add(test::RandomKey(&rnd, rnd.Skewed(4)),
              test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());
        }
        Test(&rnd);
      }
    }
  }

J
jorlow@chromium.org 已提交
991
 private:
992
  Options options_ = Options();
L
Lei Jin 已提交
993
  ImmutableCFOptions ioptions_;
994
  BlockBasedTableOptions table_options_ = BlockBasedTableOptions();
J
jorlow@chromium.org 已提交
995
  Constructor* constructor_;
996
  WriteBufferManager write_buffer_;
997 998
  bool support_prev_;
  bool only_support_prefix_seek_;
999
  shared_ptr<InternalKeyComparator> internal_comparator_;
J
jorlow@chromium.org 已提交
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
};

static bool Between(uint64_t val, uint64_t low, uint64_t high) {
  bool result = (val >= low) && (val <= high);
  if (!result) {
    fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
            (unsigned long long)(val),
            (unsigned long long)(low),
            (unsigned long long)(high));
  }
  return result;
}

K
Kai Liu 已提交
1013
// Tests against all kinds of tables
I
Igor Sugak 已提交
1014
class TableTest : public testing::Test {
1015 1016 1017 1018 1019 1020 1021 1022 1023
 public:
  const InternalKeyComparator& GetPlainInternalComparator(
      const Comparator* comp) {
    if (!plain_internal_comparator) {
      plain_internal_comparator.reset(
          new test::PlainInternalKeyComparator(comp));
    }
    return *plain_internal_comparator;
  }
M
Maysam Yabandeh 已提交
1024
  void IndexTest(BlockBasedTableOptions table_options);
1025 1026 1027 1028 1029 1030

 private:
  std::unique_ptr<InternalKeyComparator> plain_internal_comparator;
};

class GeneralTableTest : public TableTest {};
1031 1032 1033 1034
class BlockBasedTableTest : public TableTest {
 protected:
  uint64_t IndexUncompressedHelper(bool indexCompress);
};
1035
class PlainTableTest : public TableTest {};
I
Igor Sugak 已提交
1036
class TablePropertyTest : public testing::Test {};
1037 1038 1039

// This test serves as the living tutorial for the prefix scan of user collected
// properties.
I
Igor Sugak 已提交
1040
TEST_F(TablePropertyTest, PrefixScanTest) {
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
  UserCollectedProperties props{{"num.111.1", "1"},
                                {"num.111.2", "2"},
                                {"num.111.3", "3"},
                                {"num.333.1", "1"},
                                {"num.333.2", "2"},
                                {"num.333.3", "3"},
                                {"num.555.1", "1"},
                                {"num.555.2", "2"},
                                {"num.555.3", "3"}, };

  // prefixes that exist
  for (const std::string& prefix : {"num.111", "num.333", "num.555"}) {
    int num = 0;
    for (auto pos = props.lower_bound(prefix);
         pos != props.end() &&
             pos->first.compare(0, prefix.size(), prefix) == 0;
         ++pos) {
      ++num;
1059
      auto key = prefix + "." + ToString(num);
1060
      ASSERT_EQ(key, pos->first);
1061
      ASSERT_EQ(ToString(num), pos->second);
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
    }
    ASSERT_EQ(3, num);
  }

  // prefixes that don't exist
  for (const std::string& prefix :
       {"num.000", "num.222", "num.444", "num.666"}) {
    auto pos = props.lower_bound(prefix);
    ASSERT_TRUE(pos == props.end() ||
                pos->first.compare(0, prefix.size(), prefix) != 0);
  }
}
J
jorlow@chromium.org 已提交
1074

K
Kai Liu 已提交
1075 1076
// This test include all the basic checks except those for index size and block
// size, which will be conducted in separated unit tests.
I
Igor Sugak 已提交
1077
TEST_F(BlockBasedTableTest, BasicBlockBasedTableProperties) {
1078
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
K
Kai Liu 已提交
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088

  c.Add("a1", "val1");
  c.Add("b2", "val2");
  c.Add("c3", "val3");
  c.Add("d4", "val4");
  c.Add("e5", "val5");
  c.Add("f6", "val6");
  c.Add("g7", "val7");
  c.Add("h8", "val8");
  c.Add("j9", "val9");
1089
  uint64_t diff_internal_user_bytes = 9 * 8;  // 8 is seq size, 9 k-v totally
K
Kai Liu 已提交
1090 1091

  std::vector<std::string> keys;
1092
  stl_wrappers::KVMap kvmap;
1093
  Options options;
K
Kai Liu 已提交
1094
  options.compression = kNoCompression;
1095 1096
  options.statistics = CreateDBStatistics();
  options.statistics->stats_level_ = StatsLevel::kAll;
1097 1098 1099
  BlockBasedTableOptions table_options;
  table_options.block_restart_interval = 1;
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));
K
Kai Liu 已提交
1100

1101 1102
  ImmutableCFOptions ioptions(options);
  ioptions.statistics = options.statistics.get();
L
Lei Jin 已提交
1103
  c.Finish(options, ioptions, table_options,
1104
           GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1105
  ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_NOT_COMPRESSED), 0);
K
Kai Liu 已提交
1106

1107
  auto& props = *c.GetTableReader()->GetTableProperties();
K
kailiu 已提交
1108
  ASSERT_EQ(kvmap.size(), props.num_entries);
K
Kai Liu 已提交
1109 1110 1111 1112

  auto raw_key_size = kvmap.size() * 2ul;
  auto raw_value_size = kvmap.size() * 4ul;

1113
  ASSERT_EQ(raw_key_size + diff_internal_user_bytes, props.raw_key_size);
K
kailiu 已提交
1114 1115 1116
  ASSERT_EQ(raw_value_size, props.raw_value_size);
  ASSERT_EQ(1ul, props.num_data_blocks);
  ASSERT_EQ("", props.filter_policy_name);  // no filter policy is used
K
Kai Liu 已提交
1117 1118

  // Verify data size.
I
Igor Canadi 已提交
1119
  BlockBuilder block_builder(1);
K
Kai Liu 已提交
1120 1121 1122 1123
  for (const auto& item : kvmap) {
    block_builder.Add(item.first, item.second);
  }
  Slice content = block_builder.Finish();
1124 1125
  ASSERT_EQ(content.size() + kBlockTrailerSize + diff_internal_user_bytes,
            props.data_size);
1126
  c.ResetTableReader();
K
Kai Liu 已提交
1127 1128
}

Y
Yi Wu 已提交
1129
#ifdef SNAPPY
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
uint64_t BlockBasedTableTest::IndexUncompressedHelper(bool compressed) {
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
  constexpr size_t kNumKeys = 10000;

  for (size_t k = 0; k < kNumKeys; ++k) {
    c.Add("key" + ToString(k), "val" + ToString(k));
  }

  std::vector<std::string> keys;
  stl_wrappers::KVMap kvmap;
  Options options;
  options.compression = kSnappyCompression;
  options.statistics = CreateDBStatistics();
  options.statistics->stats_level_ = StatsLevel::kAll;
  BlockBasedTableOptions table_options;
  table_options.block_restart_interval = 1;
  table_options.enable_index_compression = compressed;
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));

  ImmutableCFOptions ioptions(options);
  ioptions.statistics = options.statistics.get();
  c.Finish(options, ioptions, table_options,
           GetPlainInternalComparator(options.comparator), &keys, &kvmap);
  c.ResetTableReader();
  return options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
}
TEST_F(BlockBasedTableTest, IndexUncompressed) {
  uint64_t tbl1_compressed_cnt = IndexUncompressedHelper(true);
  uint64_t tbl2_compressed_cnt = IndexUncompressedHelper(false);
  // tbl1_compressed_cnt should include 1 index block
  EXPECT_EQ(tbl2_compressed_cnt + 1, tbl1_compressed_cnt);
}
Y
Yi Wu 已提交
1162
#endif  // SNAPPY
1163

1164 1165 1166 1167 1168 1169 1170
TEST_F(BlockBasedTableTest, BlockBasedTableProperties2) {
  TableConstructor c(&reverse_key_comparator);
  std::vector<std::string> keys;
  stl_wrappers::KVMap kvmap;

  {
    Options options;
1171
    options.compression = CompressionType::kNoCompression;
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
    BlockBasedTableOptions table_options;
    options.table_factory.reset(NewBlockBasedTableFactory(table_options));

    const ImmutableCFOptions ioptions(options);
    c.Finish(options, ioptions, table_options,
             GetPlainInternalComparator(options.comparator), &keys, &kvmap);

    auto& props = *c.GetTableReader()->GetTableProperties();

    // Default comparator
    ASSERT_EQ("leveldb.BytewiseComparator", props.comparator_name);
    // No merge operator
    ASSERT_EQ("nullptr", props.merge_operator_name);
A
Aaron Gao 已提交
1185 1186
    // No prefix extractor
    ASSERT_EQ("nullptr", props.prefix_extractor_name);
1187 1188 1189 1190
    // No property collectors
    ASSERT_EQ("[]", props.property_collectors_names);
    // No filter policy is used
    ASSERT_EQ("", props.filter_policy_name);
1191 1192
    // Compression type == that set:
    ASSERT_EQ("NoCompression", props.compression_name);
1193 1194 1195 1196 1197 1198 1199 1200 1201
    c.ResetTableReader();
  }

  {
    Options options;
    BlockBasedTableOptions table_options;
    options.table_factory.reset(NewBlockBasedTableFactory(table_options));
    options.comparator = &reverse_key_comparator;
    options.merge_operator = MergeOperators::CreateUInt64AddOperator();
A
Aaron Gao 已提交
1202
    options.prefix_extractor.reset(NewNoopTransform());
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
    options.table_properties_collector_factories.emplace_back(
        new DummyPropertiesCollectorFactory1());
    options.table_properties_collector_factories.emplace_back(
        new DummyPropertiesCollectorFactory2());

    const ImmutableCFOptions ioptions(options);
    c.Finish(options, ioptions, table_options,
             GetPlainInternalComparator(options.comparator), &keys, &kvmap);

    auto& props = *c.GetTableReader()->GetTableProperties();

    ASSERT_EQ("rocksdb.ReverseBytewiseComparator", props.comparator_name);
    ASSERT_EQ("UInt64AddOperator", props.merge_operator_name);
A
Aaron Gao 已提交
1216
    ASSERT_EQ("rocksdb.Noop", props.prefix_extractor_name);
1217 1218 1219 1220 1221 1222 1223
    ASSERT_EQ("[DummyPropertiesCollector1,DummyPropertiesCollector2]",
              props.property_collectors_names);
    ASSERT_EQ("", props.filter_policy_name);  // no filter policy is used
    c.ResetTableReader();
  }
}

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
TEST_F(BlockBasedTableTest, RangeDelBlock) {
  TableConstructor c(BytewiseComparator());
  std::vector<std::string> keys = {"1pika", "2chu"};
  std::vector<std::string> vals = {"p", "c"};

  for (int i = 0; i < 2; i++) {
    RangeTombstone t(keys[i], vals[i], i);
    std::pair<InternalKey, Slice> p = t.Serialize();
    c.Add(p.first.Encode().ToString(), p.second);
  }

  std::vector<std::string> sorted_keys;
  stl_wrappers::KVMap kvmap;
  Options options;
  options.compression = kNoCompression;
  BlockBasedTableOptions table_options;
  table_options.block_restart_interval = 1;
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));

  const ImmutableCFOptions ioptions(options);
  std::unique_ptr<InternalKeyComparator> internal_cmp(
      new InternalKeyComparator(options.comparator));
  c.Finish(options, ioptions, table_options, *internal_cmp, &sorted_keys,
           &kvmap);

1249 1250 1251 1252 1253 1254 1255 1256
  for (int j = 0; j < 2; ++j) {
    std::unique_ptr<InternalIterator> iter(
        c.GetTableReader()->NewRangeTombstoneIterator(ReadOptions()));
    if (j > 0) {
      // For second iteration, delete the table reader object and verify the
      // iterator can still access its metablock's range tombstones.
      c.ResetTableReader();
    }
A
Andrew Kryczka 已提交
1257
    ASSERT_FALSE(iter->Valid());
1258
    iter->SeekToFirst();
A
Andrew Kryczka 已提交
1259
    ASSERT_TRUE(iter->Valid());
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
    for (int i = 0; i < 2; i++) {
      ASSERT_TRUE(iter->Valid());
      ParsedInternalKey parsed_key;
      ASSERT_TRUE(ParseInternalKey(iter->key(), &parsed_key));
      RangeTombstone t(parsed_key, iter->value());
      ASSERT_EQ(t.start_key_, keys[i]);
      ASSERT_EQ(t.end_key_, vals[i]);
      ASSERT_EQ(t.seq_, i);
      iter->Next();
    }
    ASSERT_TRUE(!iter->Valid());
  }
1272 1273
}

I
Igor Sugak 已提交
1274
TEST_F(BlockBasedTableTest, FilterPolicyNameProperties) {
1275
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1276 1277
  c.Add("a1", "val1");
  std::vector<std::string> keys;
1278
  stl_wrappers::KVMap kvmap;
1279 1280
  BlockBasedTableOptions table_options;
  table_options.filter_policy.reset(NewBloomFilterPolicy(10));
1281
  Options options;
1282
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1283

L
Lei Jin 已提交
1284 1285
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options,
1286
           GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1287
  auto& props = *c.GetTableReader()->GetTableProperties();
K
kailiu 已提交
1288
  ASSERT_EQ("rocksdb.BuiltinBloomFilter", props.filter_policy_name);
1289
  c.ResetTableReader();
1290 1291
}

1292 1293 1294 1295
//
// BlockBasedTableTest::PrefetchTest
//
void AssertKeysInCache(BlockBasedTable* table_reader,
1296
                       const std::vector<std::string>& keys_in_cache,
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314
                       const std::vector<std::string>& keys_not_in_cache,
                       bool convert = false) {
  if (convert) {
    for (auto key : keys_in_cache) {
      InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
      ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
    }
    for (auto key : keys_not_in_cache) {
      InternalKey ikey(key, kMaxSequenceNumber, kTypeValue);
      ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), ikey.Encode()));
    }
  } else {
    for (auto key : keys_in_cache) {
      ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), key));
    }
    for (auto key : keys_not_in_cache) {
      ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), key));
    }
1315 1316 1317 1318
  }
}

void PrefetchRange(TableConstructor* c, Options* opt,
1319
                   BlockBasedTableOptions* table_options, const char* key_begin,
1320 1321 1322
                   const char* key_end,
                   const std::vector<std::string>& keys_in_cache,
                   const std::vector<std::string>& keys_not_in_cache,
1323 1324
                   const Status expected_status = Status::OK()) {
  // reset the cache and reopen the table
1325
  table_options->block_cache = NewLRUCache(16 * 1024 * 1024, 4);
1326 1327 1328 1329 1330 1331
  opt->table_factory.reset(NewBlockBasedTableFactory(*table_options));
  const ImmutableCFOptions ioptions2(*opt);
  ASSERT_OK(c->Reopen(ioptions2));

  // prefetch
  auto* table_reader = dynamic_cast<BlockBasedTable*>(c->GetTableReader());
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
  Status s;
  unique_ptr<Slice> begin, end;
  unique_ptr<InternalKey> i_begin, i_end;
  if (key_begin != nullptr) {
    if (c->ConvertToInternalKey()) {
      i_begin.reset(new InternalKey(key_begin, kMaxSequenceNumber, kTypeValue));
      begin.reset(new Slice(i_begin->Encode()));
    } else {
      begin.reset(new Slice(key_begin));
    }
  }
  if (key_end != nullptr) {
    if (c->ConvertToInternalKey()) {
      i_end.reset(new InternalKey(key_end, kMaxSequenceNumber, kTypeValue));
      end.reset(new Slice(i_end->Encode()));
    } else {
      end.reset(new Slice(key_end));
    }
  }
  s = table_reader->Prefetch(begin.get(), end.get());

1353 1354 1355
  ASSERT_TRUE(s.code() == expected_status.code());

  // assert our expectation in cache warmup
1356 1357
  AssertKeysInCache(table_reader, keys_in_cache, keys_not_in_cache,
                    c->ConvertToInternalKey());
1358
  c->ResetTableReader();
1359 1360
}

I
Igor Sugak 已提交
1361
TEST_F(BlockBasedTableTest, PrefetchTest) {
1362 1363 1364 1365 1366 1367 1368 1369 1370
  // The purpose of this test is to test the prefetching operation built into
  // BlockBasedTable.
  Options opt;
  unique_ptr<InternalKeyComparator> ikc;
  ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
  opt.compression = kNoCompression;
  BlockBasedTableOptions table_options;
  table_options.block_size = 1024;
  // big enough so we don't ever lose cached values.
1371
  table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
1372 1373
  opt.table_factory.reset(NewBlockBasedTableFactory(table_options));

1374
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1375 1376 1377 1378 1379 1380 1381 1382
  c.Add("k01", "hello");
  c.Add("k02", "hello2");
  c.Add("k03", std::string(10000, 'x'));
  c.Add("k04", std::string(200000, 'x'));
  c.Add("k05", std::string(300000, 'x'));
  c.Add("k06", "hello3");
  c.Add("k07", std::string(100000, 'x'));
  std::vector<std::string> keys;
1383
  stl_wrappers::KVMap kvmap;
1384 1385
  const ImmutableCFOptions ioptions(opt);
  c.Finish(opt, ioptions, table_options, *ikc, &keys, &kvmap);
1386
  c.ResetTableReader();
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398

  // We get the following data spread :
  //
  // Data block         Index
  // ========================
  // [ k01 k02 k03 ]    k03
  // [ k04         ]    k04
  // [ k05         ]    k05
  // [ k06 k07     ]    k07


  // Simple
1399 1400 1401 1402 1403
  PrefetchRange(&c, &opt, &table_options,
                /*key_range=*/"k01", "k05",
                /*keys_in_cache=*/{"k01", "k02", "k03", "k04", "k05"},
                /*keys_not_in_cache=*/{"k06", "k07"});
  PrefetchRange(&c, &opt, &table_options, "k01", "k01", {"k01", "k02", "k03"},
1404 1405
                {"k04", "k05", "k06", "k07"});
  // odd
1406 1407 1408
  PrefetchRange(&c, &opt, &table_options, "a", "z",
                {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
  PrefetchRange(&c, &opt, &table_options, "k00", "k00", {"k01", "k02", "k03"},
1409 1410
                {"k04", "k05", "k06", "k07"});
  // Edge cases
1411 1412 1413 1414
  PrefetchRange(&c, &opt, &table_options, "k00", "k06",
                {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
  PrefetchRange(&c, &opt, &table_options, "k00", "zzz",
                {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
1415
  // null keys
1416 1417 1418 1419 1420 1421
  PrefetchRange(&c, &opt, &table_options, nullptr, nullptr,
                {"k01", "k02", "k03", "k04", "k05", "k06", "k07"}, {});
  PrefetchRange(&c, &opt, &table_options, "k04", nullptr,
                {"k04", "k05", "k06", "k07"}, {"k01", "k02", "k03"});
  PrefetchRange(&c, &opt, &table_options, nullptr, "k05",
                {"k01", "k02", "k03", "k04", "k05"}, {"k06", "k07"});
1422
  // invalid
1423
  PrefetchRange(&c, &opt, &table_options, "k06", "k00", {}, {},
1424
                Status::InvalidArgument(Slice("k06 "), Slice("k07")));
1425
  c.ResetTableReader();
1426 1427
}

I
Igor Sugak 已提交
1428
TEST_F(BlockBasedTableTest, TotalOrderSeekOnHashIndex) {
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
  BlockBasedTableOptions table_options;
  for (int i = 0; i < 4; ++i) {
    Options options;
    // Make each key/value an individual block
    table_options.block_size = 64;
    switch (i) {
    case 0:
      // Binary search index
      table_options.index_type = BlockBasedTableOptions::kBinarySearch;
      options.table_factory.reset(new BlockBasedTableFactory(table_options));
      break;
    case 1:
      // Hash search index
      table_options.index_type = BlockBasedTableOptions::kHashSearch;
      options.table_factory.reset(new BlockBasedTableFactory(table_options));
      options.prefix_extractor.reset(NewFixedPrefixTransform(4));
      break;
    case 2:
      // Hash search index with hash_index_allow_collision
      table_options.index_type = BlockBasedTableOptions::kHashSearch;
      table_options.hash_index_allow_collision = true;
      options.table_factory.reset(new BlockBasedTableFactory(table_options));
      options.prefix_extractor.reset(NewFixedPrefixTransform(4));
      break;
    case 3:
      // Hash search index with filter policy
      table_options.index_type = BlockBasedTableOptions::kHashSearch;
      table_options.filter_policy.reset(NewBloomFilterPolicy(10));
      options.table_factory.reset(new BlockBasedTableFactory(table_options));
      options.prefix_extractor.reset(NewFixedPrefixTransform(4));
      break;
M
Maysam Yabandeh 已提交
1460 1461 1462 1463 1464 1465
    case 4:
    default:
      // Binary search index
      table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
      options.table_factory.reset(new BlockBasedTableFactory(table_options));
      break;
1466 1467
    }

1468 1469
    TableConstructor c(BytewiseComparator(),
                       true /* convert_to_internal_key_ */);
1470 1471 1472 1473 1474 1475 1476 1477
    c.Add("aaaa1", std::string('a', 56));
    c.Add("bbaa1", std::string('a', 56));
    c.Add("cccc1", std::string('a', 56));
    c.Add("bbbb1", std::string('a', 56));
    c.Add("baaa1", std::string('a', 56));
    c.Add("abbb1", std::string('a', 56));
    c.Add("cccc2", std::string('a', 56));
    std::vector<std::string> keys;
1478
    stl_wrappers::KVMap kvmap;
L
Lei Jin 已提交
1479 1480
    const ImmutableCFOptions ioptions(options);
    c.Finish(options, ioptions, table_options,
1481 1482 1483 1484 1485 1486
             GetPlainInternalComparator(options.comparator), &keys, &kvmap);
    auto props = c.GetTableReader()->GetTableProperties();
    ASSERT_EQ(7u, props->num_data_blocks);
    auto* reader = c.GetTableReader();
    ReadOptions ro;
    ro.total_order_seek = true;
S
sdong 已提交
1487
    std::unique_ptr<InternalIterator> iter(reader->NewIterator(ro));
1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517

    iter->Seek(InternalKey("b", 0, kTypeValue).Encode());
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("baaa1", ExtractUserKey(iter->key()).ToString());
    iter->Next();
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());

    iter->Seek(InternalKey("bb", 0, kTypeValue).Encode());
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
    iter->Next();
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());

    iter->Seek(InternalKey("bbb", 0, kTypeValue).Encode());
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
    iter->Next();
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("cccc1", ExtractUserKey(iter->key()).ToString());
  }
}

1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535
TEST_F(BlockBasedTableTest, NoopTransformSeek) {
  BlockBasedTableOptions table_options;
  table_options.filter_policy.reset(NewBloomFilterPolicy(10));

  Options options;
  options.comparator = BytewiseComparator();
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
  options.prefix_extractor.reset(NewNoopTransform());

  TableConstructor c(options.comparator);
  // To tickle the PrefixMayMatch bug it is important that the
  // user-key is a single byte so that the index key exactly matches
  // the user-key.
  InternalKey key("a", 1, kTypeValue);
  c.Add(key.Encode().ToString(), "b");
  std::vector<std::string> keys;
  stl_wrappers::KVMap kvmap;
  const ImmutableCFOptions ioptions(options);
1536 1537 1538
  const InternalKeyComparator internal_comparator(options.comparator);
  c.Finish(options, ioptions, table_options, internal_comparator, &keys,
           &kvmap);
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552

  auto* reader = c.GetTableReader();
  for (int i = 0; i < 2; ++i) {
    ReadOptions ro;
    ro.total_order_seek = (i == 0);
    std::unique_ptr<InternalIterator> iter(reader->NewIterator(ro));

    iter->Seek(key.Encode());
    ASSERT_OK(iter->status());
    ASSERT_TRUE(iter->Valid());
    ASSERT_EQ("a", ExtractUserKey(iter->key()).ToString());
  }
}

A
Aaron Gao 已提交
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
TEST_F(BlockBasedTableTest, SkipPrefixBloomFilter) {
  // if DB is opened with a prefix extractor of a different name,
  // prefix bloom is skipped when read the file
  BlockBasedTableOptions table_options;
  table_options.filter_policy.reset(NewBloomFilterPolicy(2));
  table_options.whole_key_filtering = false;

  Options options;
  options.comparator = BytewiseComparator();
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
  options.prefix_extractor.reset(NewFixedPrefixTransform(1));

  TableConstructor c(options.comparator);
  InternalKey key("abcdefghijk", 1, kTypeValue);
  c.Add(key.Encode().ToString(), "test");
  std::vector<std::string> keys;
  stl_wrappers::KVMap kvmap;
  const ImmutableCFOptions ioptions(options);
  const InternalKeyComparator internal_comparator(options.comparator);
  c.Finish(options, ioptions, table_options, internal_comparator, &keys,
           &kvmap);
  options.prefix_extractor.reset(NewFixedPrefixTransform(9));
  const ImmutableCFOptions new_ioptions(options);
  c.Reopen(new_ioptions);
  auto reader = c.GetTableReader();
  std::unique_ptr<InternalIterator> db_iter(reader->NewIterator(ReadOptions()));

  // Test point lookup
  // only one kv
  for (auto& kv : kvmap) {
    db_iter->Seek(kv.first);
    ASSERT_TRUE(db_iter->Valid());
    ASSERT_OK(db_iter->status());
    ASSERT_EQ(db_iter->key(), kv.first);
    ASSERT_EQ(db_iter->value(), kv.second);
  }
}

K
Kai Liu 已提交
1591 1592 1593 1594 1595 1596
static std::string RandomString(Random* rnd, int len) {
  std::string r;
  test::RandomString(rnd, len, &r);
  return r;
}

1597
void AddInternalKey(TableConstructor* c, const std::string& prefix,
1598
                    int suffix_len = 800) {
1599 1600 1601 1602 1603
  static Random rnd(1023);
  InternalKey k(prefix + RandomString(&rnd, 800), 0, kTypeValue);
  c->Add(k.Encode().ToString(), "v");
}

M
Maysam Yabandeh 已提交
1604
void TableTest::IndexTest(BlockBasedTableOptions table_options) {
1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
  TableConstructor c(BytewiseComparator());

  // keys with prefix length 3, make sure the key/value is big enough to fill
  // one block
  AddInternalKey(&c, "0015");
  AddInternalKey(&c, "0035");

  AddInternalKey(&c, "0054");
  AddInternalKey(&c, "0055");

  AddInternalKey(&c, "0056");
  AddInternalKey(&c, "0057");

  AddInternalKey(&c, "0058");
  AddInternalKey(&c, "0075");

  AddInternalKey(&c, "0076");
  AddInternalKey(&c, "0095");

  std::vector<std::string> keys;
1625
  stl_wrappers::KVMap kvmap;
1626
  Options options;
1627 1628
  options.prefix_extractor.reset(NewFixedPrefixTransform(3));
  table_options.block_size = 1700;
1629
  table_options.block_cache = NewLRUCache(1024, 4);
1630
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1631 1632 1633

  std::unique_ptr<InternalKeyComparator> comparator(
      new InternalKeyComparator(BytewiseComparator()));
L
Lei Jin 已提交
1634 1635
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options, *comparator, &keys, &kvmap);
1636
  auto reader = c.GetTableReader();
1637

1638
  auto props = reader->GetTableProperties();
1639 1640
  ASSERT_EQ(5u, props->num_data_blocks);

M
Maysam Yabandeh 已提交
1641
  std::unique_ptr<InternalIterator> index_iter(
S
sdong 已提交
1642
      reader->NewIterator(ReadOptions()));
1643 1644 1645 1646 1647 1648 1649 1650

  // -- Find keys do not exist, but have common prefix.
  std::vector<std::string> prefixes = {"001", "003", "005", "007", "009"};
  std::vector<std::string> lower_bound = {keys[0], keys[1], keys[2],
                                          keys[7], keys[9], };

  // find the lower bound of the prefix
  for (size_t i = 0; i < prefixes.size(); ++i) {
M
Maysam Yabandeh 已提交
1651 1652 1653
    index_iter->Seek(InternalKey(prefixes[i], 0, kTypeValue).Encode());
    ASSERT_OK(index_iter->status());
    ASSERT_TRUE(index_iter->Valid());
1654 1655

    // seek the first element in the block
M
Maysam Yabandeh 已提交
1656 1657
    ASSERT_EQ(lower_bound[i], index_iter->key().ToString());
    ASSERT_EQ("v", index_iter->value().ToString());
1658 1659 1660 1661 1662 1663 1664 1665
  }

  // find the upper bound of prefixes
  std::vector<std::string> upper_bound = {keys[1], keys[2], keys[7], keys[9], };

  // find existing keys
  for (const auto& item : kvmap) {
    auto ukey = ExtractUserKey(item.first).ToString();
M
Maysam Yabandeh 已提交
1666
    index_iter->Seek(ukey);
1667 1668

    // ASSERT_OK(regular_iter->status());
M
Maysam Yabandeh 已提交
1669
    ASSERT_OK(index_iter->status());
1670 1671

    // ASSERT_TRUE(regular_iter->Valid());
M
Maysam Yabandeh 已提交
1672
    ASSERT_TRUE(index_iter->Valid());
1673

M
Maysam Yabandeh 已提交
1674 1675
    ASSERT_EQ(item.first, index_iter->key().ToString());
    ASSERT_EQ(item.second, index_iter->value().ToString());
1676 1677 1678 1679 1680
  }

  for (size_t i = 0; i < prefixes.size(); ++i) {
    // the key is greater than any existing keys.
    auto key = prefixes[i] + "9";
M
Maysam Yabandeh 已提交
1681
    index_iter->Seek(InternalKey(key, 0, kTypeValue).Encode());
1682

M
Maysam Yabandeh 已提交
1683
    ASSERT_OK(index_iter->status());
1684 1685
    if (i == prefixes.size() - 1) {
      // last key
M
Maysam Yabandeh 已提交
1686
      ASSERT_TRUE(!index_iter->Valid());
1687
    } else {
M
Maysam Yabandeh 已提交
1688
      ASSERT_TRUE(index_iter->Valid());
1689
      // seek the first element in the block
M
Maysam Yabandeh 已提交
1690 1691
      ASSERT_EQ(upper_bound[i], index_iter->key().ToString());
      ASSERT_EQ("v", index_iter->value().ToString());
1692 1693 1694 1695 1696 1697
    }
  }

  // find keys with prefix that don't match any of the existing prefixes.
  std::vector<std::string> non_exist_prefixes = {"002", "004", "006", "008"};
  for (const auto& prefix : non_exist_prefixes) {
M
Maysam Yabandeh 已提交
1698
    index_iter->Seek(InternalKey(prefix, 0, kTypeValue).Encode());
1699 1700
    // regular_iter->Seek(prefix);

M
Maysam Yabandeh 已提交
1701
    ASSERT_OK(index_iter->status());
1702 1703
    // Seek to non-existing prefixes should yield either invalid, or a
    // key with prefix greater than the target.
M
Maysam Yabandeh 已提交
1704 1705
    if (index_iter->Valid()) {
      Slice ukey = ExtractUserKey(index_iter->key());
1706 1707 1708
      Slice ukey_prefix = options.prefix_extractor->Transform(ukey);
      ASSERT_TRUE(BytewiseComparator()->Compare(prefix, ukey_prefix) < 0);
    }
1709
  }
1710
  c.ResetTableReader();
1711 1712
}

M
Maysam Yabandeh 已提交
1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726
TEST_F(TableTest, BinaryIndexTest) {
  BlockBasedTableOptions table_options;
  table_options.index_type = BlockBasedTableOptions::kBinarySearch;
  IndexTest(table_options);
}

TEST_F(TableTest, HashIndexTest) {
  BlockBasedTableOptions table_options;
  table_options.index_type = BlockBasedTableOptions::kHashSearch;
  IndexTest(table_options);
}

TEST_F(TableTest, PartitionIndexTest) {
  const int max_index_keys = 5;
M
Maysam Yabandeh 已提交
1727 1728 1729
  const int est_max_index_key_value_size = 32;
  const int est_max_index_size = max_index_keys * est_max_index_key_value_size;
  for (int i = 1; i <= est_max_index_size + 1; i++) {
M
Maysam Yabandeh 已提交
1730 1731
    BlockBasedTableOptions table_options;
    table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
M
Maysam Yabandeh 已提交
1732
    table_options.metadata_block_size = i;
M
Maysam Yabandeh 已提交
1733 1734 1735 1736
    IndexTest(table_options);
  }
}

K
Kai Liu 已提交
1737 1738 1739
// It's very hard to figure out the index block size of a block accurately.
// To make sure we get the index size, we just make sure as key number
// grows, the filter block size also grows.
I
Igor Sugak 已提交
1740
TEST_F(BlockBasedTableTest, IndexSizeStat) {
K
Kai Liu 已提交
1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755
  uint64_t last_index_size = 0;

  // we need to use random keys since the pure human readable texts
  // may be well compressed, resulting insignifcant change of index
  // block size.
  Random rnd(test::RandomSeed());
  std::vector<std::string> keys;

  for (int i = 0; i < 100; ++i) {
    keys.push_back(RandomString(&rnd, 10000));
  }

  // Each time we load one more key to the table. the table index block
  // size is expected to be larger than last time's.
  for (size_t i = 1; i < keys.size(); ++i) {
1756 1757
    TableConstructor c(BytewiseComparator(),
                       true /* convert_to_internal_key_ */);
K
Kai Liu 已提交
1758 1759 1760 1761 1762
    for (size_t j = 0; j < i; ++j) {
      c.Add(keys[j], "val");
    }

    std::vector<std::string> ks;
1763
    stl_wrappers::KVMap kvmap;
1764
    Options options;
K
Kai Liu 已提交
1765
    options.compression = kNoCompression;
1766 1767 1768
    BlockBasedTableOptions table_options;
    table_options.block_restart_interval = 1;
    options.table_factory.reset(NewBlockBasedTableFactory(table_options));
K
Kai Liu 已提交
1769

L
Lei Jin 已提交
1770 1771
    const ImmutableCFOptions ioptions(options);
    c.Finish(options, ioptions, table_options,
1772
             GetPlainInternalComparator(options.comparator), &ks, &kvmap);
1773
    auto index_size = c.GetTableReader()->GetTableProperties()->index_size;
K
Kai Liu 已提交
1774 1775
    ASSERT_GT(index_size, last_index_size);
    last_index_size = index_size;
1776
    c.ResetTableReader();
K
Kai Liu 已提交
1777 1778 1779
  }
}

I
Igor Sugak 已提交
1780
TEST_F(BlockBasedTableTest, NumBlockStat) {
K
Kai Liu 已提交
1781
  Random rnd(test::RandomSeed());
1782
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
K
Kai Liu 已提交
1783 1784
  Options options;
  options.compression = kNoCompression;
1785 1786 1787 1788
  BlockBasedTableOptions table_options;
  table_options.block_restart_interval = 1;
  table_options.block_size = 1000;
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));
K
Kai Liu 已提交
1789 1790 1791 1792 1793 1794 1795 1796

  for (int i = 0; i < 10; ++i) {
    // the key/val are slightly smaller than block size, so that each block
    // holds roughly one key/value pair.
    c.Add(RandomString(&rnd, 900), "val");
  }

  std::vector<std::string> ks;
1797
  stl_wrappers::KVMap kvmap;
L
Lei Jin 已提交
1798 1799
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options,
1800
           GetPlainInternalComparator(options.comparator), &ks, &kvmap);
1801
  ASSERT_EQ(kvmap.size(),
1802
            c.GetTableReader()->GetTableProperties()->num_data_blocks);
1803
  c.ResetTableReader();
K
Kai Liu 已提交
1804 1805
}

1806 1807
// A simple tool that takes the snapshot of block cache statistics.
class BlockCachePropertiesSnapshot {
K
Kai Liu 已提交
1808
 public:
1809
  explicit BlockCachePropertiesSnapshot(Statistics* statistics) {
I
Igor Canadi 已提交
1810 1811 1812 1813 1814 1815
    block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_MISS);
    block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_HIT);
    index_block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_INDEX_MISS);
    index_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_INDEX_HIT);
    data_block_cache_miss = statistics->getTickerCount(BLOCK_CACHE_DATA_MISS);
    data_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_DATA_HIT);
1816 1817 1818
    filter_block_cache_miss =
        statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS);
    filter_block_cache_hit = statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT);
1819 1820 1821
    block_cache_bytes_read = statistics->getTickerCount(BLOCK_CACHE_BYTES_READ);
    block_cache_bytes_write =
        statistics->getTickerCount(BLOCK_CACHE_BYTES_WRITE);
1822 1823
  }

I
Igor Canadi 已提交
1824 1825 1826 1827
  void AssertIndexBlockStat(int64_t expected_index_block_cache_miss,
                            int64_t expected_index_block_cache_hit) {
    ASSERT_EQ(expected_index_block_cache_miss, index_block_cache_miss);
    ASSERT_EQ(expected_index_block_cache_hit, index_block_cache_hit);
1828 1829
  }

I
Igor Canadi 已提交
1830 1831 1832 1833
  void AssertFilterBlockStat(int64_t expected_filter_block_cache_miss,
                             int64_t expected_filter_block_cache_hit) {
    ASSERT_EQ(expected_filter_block_cache_miss, filter_block_cache_miss);
    ASSERT_EQ(expected_filter_block_cache_hit, filter_block_cache_hit);
K
Kai Liu 已提交
1834 1835
  }

K
kailiu 已提交
1836
  // Check if the fetched props matches the expected ones.
1837
  // TODO(kailiu) Use this only when you disabled filter policy!
I
Igor Canadi 已提交
1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849
  void AssertEqual(int64_t expected_index_block_cache_miss,
                   int64_t expected_index_block_cache_hit,
                   int64_t expected_data_block_cache_miss,
                   int64_t expected_data_block_cache_hit) const {
    ASSERT_EQ(expected_index_block_cache_miss, index_block_cache_miss);
    ASSERT_EQ(expected_index_block_cache_hit, index_block_cache_hit);
    ASSERT_EQ(expected_data_block_cache_miss, data_block_cache_miss);
    ASSERT_EQ(expected_data_block_cache_hit, data_block_cache_hit);
    ASSERT_EQ(expected_index_block_cache_miss + expected_data_block_cache_miss,
              block_cache_miss);
    ASSERT_EQ(expected_index_block_cache_hit + expected_data_block_cache_hit,
              block_cache_hit);
K
Kai Liu 已提交
1850 1851
  }

1852 1853 1854 1855
  int64_t GetCacheBytesRead() { return block_cache_bytes_read; }

  int64_t GetCacheBytesWrite() { return block_cache_bytes_write; }

K
Kai Liu 已提交
1856
 private:
1857 1858 1859 1860 1861 1862
  int64_t block_cache_miss = 0;
  int64_t block_cache_hit = 0;
  int64_t index_block_cache_miss = 0;
  int64_t index_block_cache_hit = 0;
  int64_t data_block_cache_miss = 0;
  int64_t data_block_cache_hit = 0;
1863 1864
  int64_t filter_block_cache_miss = 0;
  int64_t filter_block_cache_hit = 0;
1865 1866
  int64_t block_cache_bytes_read = 0;
  int64_t block_cache_bytes_write = 0;
K
Kai Liu 已提交
1867 1868
};

1869 1870
// Make sure, by default, index/filter blocks were pre-loaded (meaning we won't
// use block cache to store them).
I
Igor Sugak 已提交
1871
TEST_F(BlockBasedTableTest, BlockCacheDisabledTest) {
1872 1873 1874 1875
  Options options;
  options.create_if_missing = true;
  options.statistics = CreateDBStatistics();
  BlockBasedTableOptions table_options;
1876
  table_options.block_cache = NewLRUCache(1024, 4);
1877
  table_options.filter_policy.reset(NewBloomFilterPolicy(10));
1878 1879
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
  std::vector<std::string> keys;
1880
  stl_wrappers::KVMap kvmap;
1881

1882
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
1883
  c.Add("key", "value");
L
Lei Jin 已提交
1884 1885
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options,
1886
           GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1887 1888

  // preloading filter/index blocks is enabled.
1889
  auto reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
1890
  ASSERT_TRUE(reader->TEST_filter_block_preloaded());
1891
  ASSERT_TRUE(reader->TEST_index_reader_preloaded());
1892 1893 1894 1895 1896 1897 1898 1899 1900

  {
    // nothing happens in the beginning
    BlockCachePropertiesSnapshot props(options.statistics.get());
    props.AssertIndexBlockStat(0, 0);
    props.AssertFilterBlockStat(0, 0);
  }

  {
1901
    GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
1902
                           GetContext::kNotFound, Slice(), nullptr, nullptr,
A
Andrew Kryczka 已提交
1903
                           nullptr, nullptr, nullptr);
1904
    // a hack that just to trigger BlockBasedTable::GetFilter.
1905
    reader->Get(ReadOptions(), "non-exist-key", &get_context);
1906 1907 1908 1909 1910 1911 1912 1913
    BlockCachePropertiesSnapshot props(options.statistics.get());
    props.AssertIndexBlockStat(0, 0);
    props.AssertFilterBlockStat(0, 0);
  }
}

// Due to the difficulities of the intersaction between statistics, this test
// only tests the case when "index block is put to block cache"
I
Igor Sugak 已提交
1914
TEST_F(BlockBasedTableTest, FilterBlockInBlockCache) {
K
Kai Liu 已提交
1915
  // -- Table construction
1916
  Options options;
K
Kai Liu 已提交
1917
  options.create_if_missing = true;
1918
  options.statistics = CreateDBStatistics();
1919 1920 1921

  // Enable the cache for index/filter blocks
  BlockBasedTableOptions table_options;
1922
  table_options.block_cache = NewLRUCache(1024, 4);
1923 1924
  table_options.cache_index_and_filter_blocks = true;
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
K
Kai Liu 已提交
1925
  std::vector<std::string> keys;
1926
  stl_wrappers::KVMap kvmap;
K
Kai Liu 已提交
1927

1928
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
K
Kai Liu 已提交
1929
  c.Add("key", "value");
L
Lei Jin 已提交
1930 1931
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options,
1932
           GetPlainInternalComparator(options.comparator), &keys, &kvmap);
1933
  // preloading filter/index blocks is prohibited.
1934
  auto* reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
1935
  ASSERT_TRUE(!reader->TEST_filter_block_preloaded());
1936
  ASSERT_TRUE(!reader->TEST_index_reader_preloaded());
K
Kai Liu 已提交
1937 1938 1939

  // -- PART 1: Open with regular block cache.
  // Since block_cache is disabled, no cache activities will be involved.
S
sdong 已提交
1940
  unique_ptr<InternalIterator> iter;
K
Kai Liu 已提交
1941

1942
  int64_t last_cache_bytes_read = 0;
K
Kai Liu 已提交
1943 1944
  // At first, no block will be accessed.
  {
1945
    BlockCachePropertiesSnapshot props(options.statistics.get());
K
Kai Liu 已提交
1946
    // index will be added to block cache.
1947 1948
    props.AssertEqual(1,  // index block miss
                      0, 0, 0);
1949 1950 1951 1952
    ASSERT_EQ(props.GetCacheBytesRead(), 0);
    ASSERT_EQ(props.GetCacheBytesWrite(),
              table_options.block_cache->GetUsage());
    last_cache_bytes_read = props.GetCacheBytesRead();
K
Kai Liu 已提交
1953 1954 1955 1956 1957
  }

  // Only index block will be accessed
  {
    iter.reset(c.NewIterator());
1958
    BlockCachePropertiesSnapshot props(options.statistics.get());
K
Kai Liu 已提交
1959 1960 1961
    // NOTE: to help better highlight the "detla" of each ticker, I use
    // <last_value> + <added_value> to indicate the increment of changed
    // value; other numbers remain the same.
1962 1963
    props.AssertEqual(1, 0 + 1,  // index block hit
                      0, 0);
1964 1965 1966 1967 1968
    // Cache hit, bytes read from cache should increase
    ASSERT_GT(props.GetCacheBytesRead(), last_cache_bytes_read);
    ASSERT_EQ(props.GetCacheBytesWrite(),
              table_options.block_cache->GetUsage());
    last_cache_bytes_read = props.GetCacheBytesRead();
K
Kai Liu 已提交
1969 1970 1971 1972 1973
  }

  // Only data block will be accessed
  {
    iter->SeekToFirst();
1974
    BlockCachePropertiesSnapshot props(options.statistics.get());
1975 1976
    props.AssertEqual(1, 1, 0 + 1,  // data block miss
                      0);
1977 1978 1979 1980 1981
    // Cache miss, Bytes read from cache should not change
    ASSERT_EQ(props.GetCacheBytesRead(), last_cache_bytes_read);
    ASSERT_EQ(props.GetCacheBytesWrite(),
              table_options.block_cache->GetUsage());
    last_cache_bytes_read = props.GetCacheBytesRead();
K
Kai Liu 已提交
1982 1983 1984 1985 1986 1987
  }

  // Data block will be in cache
  {
    iter.reset(c.NewIterator());
    iter->SeekToFirst();
1988
    BlockCachePropertiesSnapshot props(options.statistics.get());
1989 1990
    props.AssertEqual(1, 1 + 1, /* index block hit */
                      1, 0 + 1 /* data block hit */);
1991 1992 1993 1994
    // Cache hit, bytes read from cache should increase
    ASSERT_GT(props.GetCacheBytesRead(), last_cache_bytes_read);
    ASSERT_EQ(props.GetCacheBytesWrite(),
              table_options.block_cache->GetUsage());
K
Kai Liu 已提交
1995 1996 1997
  }
  // release the iterator so that the block cache can reset correctly.
  iter.reset();
1998

1999 2000
  c.ResetTableReader();

2001
  // -- PART 2: Open with very small block cache
K
Kai Liu 已提交
2002 2003
  // In this test, no block will ever get hit since the block cache is
  // too small to fit even one entry.
2004
  table_options.block_cache = NewLRUCache(1, 4);
2005
  options.statistics = CreateDBStatistics();
2006
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
L
Lei Jin 已提交
2007 2008
  const ImmutableCFOptions ioptions2(options);
  c.Reopen(ioptions2);
K
Kai Liu 已提交
2009
  {
2010
    BlockCachePropertiesSnapshot props(options.statistics.get());
2011 2012
    props.AssertEqual(1,  // index block miss
                      0, 0, 0);
2013 2014
    // Cache miss, Bytes read from cache should not change
    ASSERT_EQ(props.GetCacheBytesRead(), 0);
K
Kai Liu 已提交
2015 2016 2017 2018 2019 2020 2021
  }

  {
    // Both index and data block get accessed.
    // It first cache index block then data block. But since the cache size
    // is only 1, index block will be purged after data block is inserted.
    iter.reset(c.NewIterator());
2022
    BlockCachePropertiesSnapshot props(options.statistics.get());
2023 2024 2025
    props.AssertEqual(1 + 1,  // index block miss
                      0, 0,   // data block miss
                      0);
2026 2027
    // Cache hit, bytes read from cache should increase
    ASSERT_EQ(props.GetCacheBytesRead(), 0);
K
Kai Liu 已提交
2028 2029 2030 2031 2032 2033
  }

  {
    // SeekToFirst() accesses data block. With similar reason, we expect data
    // block's cache miss.
    iter->SeekToFirst();
2034
    BlockCachePropertiesSnapshot props(options.statistics.get());
2035 2036
    props.AssertEqual(2, 0, 0 + 1,  // data block miss
                      0);
2037 2038
    // Cache miss, Bytes read from cache should not change
    ASSERT_EQ(props.GetCacheBytesRead(), 0);
K
Kai Liu 已提交
2039
  }
2040
  iter.reset();
2041
  c.ResetTableReader();
2042 2043

  // -- PART 3: Open table with bloom filter enabled but not in SST file
2044
  table_options.block_cache = NewLRUCache(4096, 4);
2045 2046 2047 2048
  table_options.cache_index_and_filter_blocks = false;
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));

  TableConstructor c3(BytewiseComparator());
L
Lei Jin 已提交
2049 2050 2051
  std::string user_key = "k01";
  InternalKey internal_key(user_key, 0, kTypeValue);
  c3.Add(internal_key.Encode().ToString(), "hello");
2052 2053 2054
  ImmutableCFOptions ioptions3(options);
  // Generate table without filter policy
  c3.Finish(options, ioptions3, table_options,
2055 2056 2057
            GetPlainInternalComparator(options.comparator), &keys, &kvmap);
  c3.ResetTableReader();

2058 2059 2060
  // Open table with filter policy
  table_options.filter_policy.reset(NewBloomFilterPolicy(1));
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
2061
  options.statistics = CreateDBStatistics();
2062 2063 2064 2065
  ImmutableCFOptions ioptions4(options);
  ASSERT_OK(c3.Reopen(ioptions4));
  reader = dynamic_cast<BlockBasedTable*>(c3.GetTableReader());
  ASSERT_TRUE(!reader->TEST_filter_block_preloaded());
M
Maysam Yabandeh 已提交
2066
  PinnableSlice value;
2067
  GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
2068
                         GetContext::kNotFound, user_key, &value, nullptr,
A
Andrew Kryczka 已提交
2069
                         nullptr, nullptr, nullptr);
L
Lei Jin 已提交
2070
  ASSERT_OK(reader->Get(ReadOptions(), user_key, &get_context));
M
Maysam Yabandeh 已提交
2071
  ASSERT_STREQ(value.data(), "hello");
2072 2073
  BlockCachePropertiesSnapshot props(options.statistics.get());
  props.AssertFilterBlockStat(0, 0);
2074
  c3.ResetTableReader();
K
Kai Liu 已提交
2075 2076
}

2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120
void ValidateBlockSizeDeviation(int value, int expected) {
  BlockBasedTableOptions table_options;
  table_options.block_size_deviation = value;
  BlockBasedTableFactory* factory = new BlockBasedTableFactory(table_options);

  const BlockBasedTableOptions* normalized_table_options =
      (const BlockBasedTableOptions*)factory->GetOptions();
  ASSERT_EQ(normalized_table_options->block_size_deviation, expected);

  delete factory;
}

void ValidateBlockRestartInterval(int value, int expected) {
  BlockBasedTableOptions table_options;
  table_options.block_restart_interval = value;
  BlockBasedTableFactory* factory = new BlockBasedTableFactory(table_options);

  const BlockBasedTableOptions* normalized_table_options =
      (const BlockBasedTableOptions*)factory->GetOptions();
  ASSERT_EQ(normalized_table_options->block_restart_interval, expected);

  delete factory;
}

TEST_F(BlockBasedTableTest, InvalidOptions) {
  // invalid values for block_size_deviation (<0 or >100) are silently set to 0
  ValidateBlockSizeDeviation(-10, 0);
  ValidateBlockSizeDeviation(-1, 0);
  ValidateBlockSizeDeviation(0, 0);
  ValidateBlockSizeDeviation(1, 1);
  ValidateBlockSizeDeviation(99, 99);
  ValidateBlockSizeDeviation(100, 100);
  ValidateBlockSizeDeviation(101, 0);
  ValidateBlockSizeDeviation(1000, 0);

  // invalid values for block_restart_interval (<1) are silently set to 1
  ValidateBlockRestartInterval(-10, 1);
  ValidateBlockRestartInterval(-1, 1);
  ValidateBlockRestartInterval(0, 1);
  ValidateBlockRestartInterval(1, 1);
  ValidateBlockRestartInterval(2, 2);
  ValidateBlockRestartInterval(1000, 1000);
}

I
Igor Canadi 已提交
2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136
TEST_F(BlockBasedTableTest, BlockReadCountTest) {
  // bloom_filter_type = 0 -- block-based filter
  // bloom_filter_type = 0 -- full filter
  for (int bloom_filter_type = 0; bloom_filter_type < 2; ++bloom_filter_type) {
    for (int index_and_filter_in_cache = 0; index_and_filter_in_cache < 2;
         ++index_and_filter_in_cache) {
      Options options;
      options.create_if_missing = true;

      BlockBasedTableOptions table_options;
      table_options.block_cache = NewLRUCache(1, 0);
      table_options.cache_index_and_filter_blocks = index_and_filter_in_cache;
      table_options.filter_policy.reset(
          NewBloomFilterPolicy(10, bloom_filter_type == 0));
      options.table_factory.reset(new BlockBasedTableFactory(table_options));
      std::vector<std::string> keys;
I
Igor Canadi 已提交
2137
      stl_wrappers::KVMap kvmap;
I
Igor Canadi 已提交
2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148

      TableConstructor c(BytewiseComparator());
      std::string user_key = "k04";
      InternalKey internal_key(user_key, 0, kTypeValue);
      std::string encoded_key = internal_key.Encode().ToString();
      c.Add(encoded_key, "hello");
      ImmutableCFOptions ioptions(options);
      // Generate table with filter policy
      c.Finish(options, ioptions, table_options,
               GetPlainInternalComparator(options.comparator), &keys, &kvmap);
      auto reader = c.GetTableReader();
M
Maysam Yabandeh 已提交
2149
      PinnableSlice value;
I
Igor Canadi 已提交
2150 2151
      GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
                             GetContext::kNotFound, user_key, &value, nullptr,
A
Andrew Kryczka 已提交
2152
                             nullptr, nullptr, nullptr);
2153
      get_perf_context()->Reset();
I
Igor Canadi 已提交
2154 2155 2156
      ASSERT_OK(reader->Get(ReadOptions(), encoded_key, &get_context));
      if (index_and_filter_in_cache) {
        // data, index and filter block
2157
        ASSERT_EQ(get_perf_context()->block_read_count, 3);
I
Igor Canadi 已提交
2158 2159
      } else {
        // just the data block
2160
        ASSERT_EQ(get_perf_context()->block_read_count, 1);
I
Igor Canadi 已提交
2161 2162
      }
      ASSERT_EQ(get_context.State(), GetContext::kFound);
M
Maysam Yabandeh 已提交
2163
      ASSERT_STREQ(value.data(), "hello");
I
Igor Canadi 已提交
2164 2165 2166 2167 2168 2169

      // Get non-existing key
      user_key = "does-not-exist";
      internal_key = InternalKey(user_key, 0, kTypeValue);
      encoded_key = internal_key.Encode().ToString();

M
Maysam Yabandeh 已提交
2170
      value.Reset();
I
Igor Canadi 已提交
2171 2172
      get_context = GetContext(options.comparator, nullptr, nullptr, nullptr,
                               GetContext::kNotFound, user_key, &value, nullptr,
A
Andrew Kryczka 已提交
2173
                               nullptr, nullptr, nullptr);
2174
      get_perf_context()->Reset();
I
Igor Canadi 已提交
2175 2176 2177 2178 2179 2180
      ASSERT_OK(reader->Get(ReadOptions(), encoded_key, &get_context));
      ASSERT_EQ(get_context.State(), GetContext::kNotFound);

      if (index_and_filter_in_cache) {
        if (bloom_filter_type == 0) {
          // with block-based, we read index and then the filter
2181
          ASSERT_EQ(get_perf_context()->block_read_count, 2);
I
Igor Canadi 已提交
2182 2183
        } else {
          // with full-filter, we read filter first and then we stop
2184
          ASSERT_EQ(get_perf_context()->block_read_count, 1);
I
Igor Canadi 已提交
2185 2186 2187 2188
        }
      } else {
        // filter is already in memory and it figures out that the key doesn't
        // exist
2189
        ASSERT_EQ(get_perf_context()->block_read_count, 0);
I
Igor Canadi 已提交
2190 2191 2192 2193 2194
      }
    }
  }
}

M
Maysam Yabandeh 已提交
2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243
// A wrapper around LRICache that also keeps track of data blocks (in contrast
// with the objects) in the cache. The class is very simple and can be used only
// for trivial tests.
class MockCache : public LRUCache {
 public:
  MockCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
            double high_pri_pool_ratio)
      : LRUCache(capacity, num_shard_bits, strict_capacity_limit,
                 high_pri_pool_ratio) {}
  virtual Status Insert(const Slice& key, void* value, size_t charge,
                        void (*deleter)(const Slice& key, void* value),
                        Handle** handle = nullptr,
                        Priority priority = Priority::LOW) override {
    // Replace the deleter with our own so that we keep track of data blocks
    // erased from the cache
    deleters_[key.ToString()] = deleter;
    return ShardedCache::Insert(key, value, charge, &MockDeleter, handle,
                                priority);
  }
  // This is called by the application right after inserting a data block
  virtual void TEST_mark_as_data_block(const Slice& key,
                                       size_t charge) override {
    marked_data_in_cache_[key.ToString()] = charge;
    marked_size_ += charge;
  }
  using DeleterFunc = void (*)(const Slice& key, void* value);
  static std::map<std::string, DeleterFunc> deleters_;
  static std::map<std::string, size_t> marked_data_in_cache_;
  static size_t marked_size_;
  static void MockDeleter(const Slice& key, void* value) {
    // If the item was marked for being data block, decrease its usage from  the
    // total data block usage of the cache
    if (marked_data_in_cache_.find(key.ToString()) !=
        marked_data_in_cache_.end()) {
      marked_size_ -= marked_data_in_cache_[key.ToString()];
    }
    // Then call the origianl deleter
    assert(deleters_.find(key.ToString()) != deleters_.end());
    auto deleter = deleters_[key.ToString()];
    deleter(key, value);
  }
};

size_t MockCache::marked_size_ = 0;
std::map<std::string, MockCache::DeleterFunc> MockCache::deleters_;
std::map<std::string, size_t> MockCache::marked_data_in_cache_;

// Block cache can contain raw data blocks as well as general objects. If an
// object depends on the table to be live, it then must be destructed before the
M
Maysam Yabandeh 已提交
2244
// table is closed. This test makes sure that the only items remains in the
M
Maysam Yabandeh 已提交
2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325
// cache after the table is closed are raw data blocks.
TEST_F(BlockBasedTableTest, NoObjectInCacheAfterTableClose) {
  for (auto index_type :
       {BlockBasedTableOptions::IndexType::kBinarySearch,
        BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch}) {
    for (bool block_based_filter : {true, false}) {
      for (bool partition_filter : {true, false}) {
        if (partition_filter &&
            (block_based_filter ||
             index_type !=
                 BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch)) {
          continue;
        }
        for (bool index_and_filter_in_cache : {true, false}) {
          for (bool pin_l0 : {true, false}) {
            if (pin_l0 && !index_and_filter_in_cache) {
              continue;
            }
            // Create a table
            Options opt;
            unique_ptr<InternalKeyComparator> ikc;
            ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
            opt.compression = kNoCompression;
            BlockBasedTableOptions table_options;
            table_options.block_size = 1024;
            table_options.index_type =
                BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
            table_options.pin_l0_filter_and_index_blocks_in_cache = pin_l0;
            table_options.partition_filters = partition_filter;
            table_options.cache_index_and_filter_blocks =
                index_and_filter_in_cache;
            // big enough so we don't ever lose cached values.
            table_options.block_cache = std::shared_ptr<rocksdb::Cache>(
                new MockCache(16 * 1024 * 1024, 4, false, 0.0));
            table_options.filter_policy.reset(
                rocksdb::NewBloomFilterPolicy(10, block_based_filter));
            opt.table_factory.reset(NewBlockBasedTableFactory(table_options));

            TableConstructor c(BytewiseComparator());
            std::string user_key = "k01";
            std::string key =
                InternalKey(user_key, 0, kTypeValue).Encode().ToString();
            c.Add(key, "hello");
            std::vector<std::string> keys;
            stl_wrappers::KVMap kvmap;
            const ImmutableCFOptions ioptions(opt);
            c.Finish(opt, ioptions, table_options, *ikc, &keys, &kvmap);

            // Doing a read to make index/filter loaded into the cache
            auto table_reader =
                dynamic_cast<BlockBasedTable*>(c.GetTableReader());
            PinnableSlice value;
            GetContext get_context(opt.comparator, nullptr, nullptr, nullptr,
                                   GetContext::kNotFound, user_key, &value,
                                   nullptr, nullptr, nullptr, nullptr);
            InternalKey ikey(user_key, 0, kTypeValue);
            auto s = table_reader->Get(ReadOptions(), key, &get_context);
            ASSERT_EQ(get_context.State(), GetContext::kFound);
            ASSERT_STREQ(value.data(), "hello");

            // Close the table
            c.ResetTableReader();

            auto usage = table_options.block_cache->GetUsage();
            auto pinned_usage = table_options.block_cache->GetPinnedUsage();
            // The only usage must be for marked data blocks
            ASSERT_EQ(usage, MockCache::marked_size_);
            // There must be some pinned data since PinnableSlice has not
            // released them yet
            ASSERT_GT(pinned_usage, 0);
            // Release pinnable slice reousrces
            value.Reset();
            pinned_usage = table_options.block_cache->GetPinnedUsage();
            ASSERT_EQ(pinned_usage, 0);
          }
        }
      }
    }
  }
}

I
Igor Sugak 已提交
2326
TEST_F(BlockBasedTableTest, BlockCacheLeak) {
K
Kai Liu 已提交
2327 2328 2329 2330 2331
  // Check that when we reopen a table we don't lose access to blocks already
  // in the cache. This test checks whether the Table actually makes use of the
  // unique ID from the file.

  Options opt;
2332 2333
  unique_ptr<InternalKeyComparator> ikc;
  ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
K
Kai Liu 已提交
2334
  opt.compression = kNoCompression;
2335 2336 2337
  BlockBasedTableOptions table_options;
  table_options.block_size = 1024;
  // big enough so we don't ever lose cached values.
2338
  table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
2339
  opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
K
Kai Liu 已提交
2340

2341
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
K
Kai Liu 已提交
2342 2343 2344 2345 2346 2347 2348 2349
  c.Add("k01", "hello");
  c.Add("k02", "hello2");
  c.Add("k03", std::string(10000, 'x'));
  c.Add("k04", std::string(200000, 'x'));
  c.Add("k05", std::string(300000, 'x'));
  c.Add("k06", "hello3");
  c.Add("k07", std::string(100000, 'x'));
  std::vector<std::string> keys;
2350
  stl_wrappers::KVMap kvmap;
L
Lei Jin 已提交
2351 2352
  const ImmutableCFOptions ioptions(opt);
  c.Finish(opt, ioptions, table_options, *ikc, &keys, &kvmap);
K
Kai Liu 已提交
2353

S
sdong 已提交
2354
  unique_ptr<InternalIterator> iter(c.NewIterator());
K
Kai Liu 已提交
2355 2356 2357 2358 2359 2360 2361 2362
  iter->SeekToFirst();
  while (iter->Valid()) {
    iter->key();
    iter->value();
    iter->Next();
  }
  ASSERT_OK(iter->status());

L
Lei Jin 已提交
2363 2364
  const ImmutableCFOptions ioptions1(opt);
  ASSERT_OK(c.Reopen(ioptions1));
2365
  auto table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
K
Kai Liu 已提交
2366
  for (const std::string& key : keys) {
K
kailiu 已提交
2367
    ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), key));
K
Kai Liu 已提交
2368
  }
2369
  c.ResetTableReader();
I
Igor Canadi 已提交
2370 2371

  // rerun with different block cache
2372
  table_options.block_cache = NewLRUCache(16 * 1024 * 1024, 4);
2373
  opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
L
Lei Jin 已提交
2374 2375
  const ImmutableCFOptions ioptions2(opt);
  ASSERT_OK(c.Reopen(ioptions2));
2376
  table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
I
Igor Canadi 已提交
2377 2378 2379
  for (const std::string& key : keys) {
    ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), key));
  }
2380
  c.ResetTableReader();
K
Kai Liu 已提交
2381 2382
}

2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433
TEST_F(BlockBasedTableTest, NewIndexIteratorLeak) {
  // A regression test to avoid data race described in
  // https://github.com/facebook/rocksdb/issues/1267
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
  std::vector<std::string> keys;
  stl_wrappers::KVMap kvmap;
  c.Add("a1", "val1");
  Options options;
  options.prefix_extractor.reset(NewFixedPrefixTransform(1));
  BlockBasedTableOptions table_options;
  table_options.index_type = BlockBasedTableOptions::kHashSearch;
  table_options.cache_index_and_filter_blocks = true;
  table_options.block_cache = NewLRUCache(0);
  options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options,
           GetPlainInternalComparator(options.comparator), &keys, &kvmap);

  rocksdb::SyncPoint::GetInstance()->LoadDependencyAndMarkers(
      {
          {"BlockBasedTable::NewIndexIterator::thread1:1",
           "BlockBasedTable::NewIndexIterator::thread2:2"},
          {"BlockBasedTable::NewIndexIterator::thread2:3",
           "BlockBasedTable::NewIndexIterator::thread1:4"},
      },
      {
          {"BlockBasedTableTest::NewIndexIteratorLeak:Thread1Marker",
           "BlockBasedTable::NewIndexIterator::thread1:1"},
          {"BlockBasedTableTest::NewIndexIteratorLeak:Thread1Marker",
           "BlockBasedTable::NewIndexIterator::thread1:4"},
          {"BlockBasedTableTest::NewIndexIteratorLeak:Thread2Marker",
           "BlockBasedTable::NewIndexIterator::thread2:2"},
          {"BlockBasedTableTest::NewIndexIteratorLeak:Thread2Marker",
           "BlockBasedTable::NewIndexIterator::thread2:3"},
      });

  rocksdb::SyncPoint::GetInstance()->EnableProcessing();
  ReadOptions ro;
  auto* reader = c.GetTableReader();

  std::function<void()> func1 = [&]() {
    TEST_SYNC_POINT("BlockBasedTableTest::NewIndexIteratorLeak:Thread1Marker");
    std::unique_ptr<InternalIterator> iter(reader->NewIterator(ro));
    iter->Seek(InternalKey("a1", 0, kTypeValue).Encode());
  };

  std::function<void()> func2 = [&]() {
    TEST_SYNC_POINT("BlockBasedTableTest::NewIndexIteratorLeak:Thread2Marker");
    std::unique_ptr<InternalIterator> iter(reader->NewIterator(ro));
  };

D
Dmitri Smirnov 已提交
2434 2435
  auto thread1 = port::Thread(func1);
  auto thread2 = port::Thread(func2);
2436 2437 2438 2439 2440 2441
  thread1.join();
  thread2.join();
  rocksdb::SyncPoint::GetInstance()->DisableProcessing();
  c.ResetTableReader();
}

2442 2443
// Plain table is not supported in ROCKSDB_LITE
#ifndef ROCKSDB_LITE
I
Igor Sugak 已提交
2444
TEST_F(PlainTableTest, BasicPlainTableProperties) {
S
Stanislau Hlebik 已提交
2445 2446 2447 2448 2449 2450
  PlainTableOptions plain_table_options;
  plain_table_options.user_key_len = 8;
  plain_table_options.bloom_bits_per_key = 8;
  plain_table_options.hash_table_ratio = 0;

  PlainTableFactory factory(plain_table_options);
A
Andres Notzli 已提交
2451
  test::StringSink sink;
2452
  unique_ptr<WritableFileWriter> file_writer(
A
Andres Notzli 已提交
2453
      test::GetWritableFileWriter(new test::StringSink()));
2454
  Options options;
L
Lei Jin 已提交
2455
  const ImmutableCFOptions ioptions(options);
2456
  InternalKeyComparator ikc(options.comparator);
2457 2458
  std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
      int_tbl_prop_collector_factories;
2459
  std::string column_family_name;
2460
  int unknown_level = -1;
2461 2462
  std::unique_ptr<TableBuilder> builder(factory.NewTableBuilder(
      TableBuilderOptions(ioptions, ikc, &int_tbl_prop_collector_factories,
2463
                          kNoCompression, CompressionOptions(),
2464
                          nullptr /* compression_dict */,
2465 2466
                          false /* skip_filters */, column_family_name,
                          unknown_level),
2467
      TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
2468
      file_writer.get()));
K
Kai Liu 已提交
2469 2470

  for (char c = 'a'; c <= 'z'; ++c) {
2471 2472
    std::string key(8, c);
    key.append("\1       ");  // PlainTable expects internal key structure
K
Kai Liu 已提交
2473 2474 2475 2476
    std::string value(28, c + 42);
    builder->Add(key, value);
  }
  ASSERT_OK(builder->Finish());
2477
  file_writer->Flush();
K
Kai Liu 已提交
2478

A
Andres Notzli 已提交
2479 2480
  test::StringSink* ss =
    static_cast<test::StringSink*>(file_writer->writable_file());
2481 2482
  unique_ptr<RandomAccessFileReader> file_reader(
      test::GetRandomAccessFileReader(
A
Andres Notzli 已提交
2483
          new test::StringSource(ss->contents(), 72242, true)));
K
Kai Liu 已提交
2484

K
kailiu 已提交
2485
  TableProperties* props = nullptr;
2486
  auto s = ReadTableProperties(file_reader.get(), ss->contents().size(),
2487
                               kPlainTableMagicNumber, ioptions,
K
Kai Liu 已提交
2488
                               &props);
K
Kai Liu 已提交
2489
  std::unique_ptr<TableProperties> props_guard(props);
K
Kai Liu 已提交
2490 2491
  ASSERT_OK(s);

K
kailiu 已提交
2492 2493 2494 2495 2496 2497
  ASSERT_EQ(0ul, props->index_size);
  ASSERT_EQ(0ul, props->filter_size);
  ASSERT_EQ(16ul * 26, props->raw_key_size);
  ASSERT_EQ(28ul * 26, props->raw_value_size);
  ASSERT_EQ(26ul, props->num_entries);
  ASSERT_EQ(1ul, props->num_data_blocks);
K
Kai Liu 已提交
2498
}
2499
#endif  // !ROCKSDB_LITE
K
Kai Liu 已提交
2500

I
Igor Sugak 已提交
2501
TEST_F(GeneralTableTest, ApproximateOffsetOfPlain) {
2502
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
J
jorlow@chromium.org 已提交
2503 2504 2505 2506 2507 2508 2509 2510
  c.Add("k01", "hello");
  c.Add("k02", "hello2");
  c.Add("k03", std::string(10000, 'x'));
  c.Add("k04", std::string(200000, 'x'));
  c.Add("k05", std::string(300000, 'x'));
  c.Add("k06", "hello3");
  c.Add("k07", std::string(100000, 'x'));
  std::vector<std::string> keys;
2511
  stl_wrappers::KVMap kvmap;
2512
  Options options;
2513
  test::PlainInternalKeyComparator internal_comparator(options.comparator);
J
jorlow@chromium.org 已提交
2514
  options.compression = kNoCompression;
2515 2516
  BlockBasedTableOptions table_options;
  table_options.block_size = 1024;
L
Lei Jin 已提交
2517 2518 2519
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options, internal_comparator,
           &keys, &kvmap);
J
jorlow@chromium.org 已提交
2520 2521 2522 2523 2524 2525 2526

  ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"),      0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"),   10000,  11000));
2527 2528 2529
  // k04 and k05 will be in two consecutive blocks, the index is
  // an arbitrary slice between k04 and k05, either before or after k04a
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 10000, 211000));
J
jorlow@chromium.org 已提交
2530 2531 2532
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"),  210000, 211000));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"),  510000, 511000));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"),  510000, 511000));
S
Sanjay Ghemawat 已提交
2533
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"),  610000, 612000));
2534
  c.ResetTableReader();
J
jorlow@chromium.org 已提交
2535 2536
}

K
Kai Liu 已提交
2537
static void DoCompressionTest(CompressionType comp) {
J
jorlow@chromium.org 已提交
2538
  Random rnd(301);
2539
  TableConstructor c(BytewiseComparator(), true /* convert_to_internal_key_ */);
J
jorlow@chromium.org 已提交
2540 2541 2542 2543 2544 2545
  std::string tmp;
  c.Add("k01", "hello");
  c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
  c.Add("k03", "hello3");
  c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
  std::vector<std::string> keys;
2546
  stl_wrappers::KVMap kvmap;
2547
  Options options;
2548
  test::PlainInternalKeyComparator ikc(options.comparator);
H
heyongqiang 已提交
2549
  options.compression = comp;
2550 2551
  BlockBasedTableOptions table_options;
  table_options.block_size = 1024;
L
Lei Jin 已提交
2552 2553
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options, ikc, &keys, &kvmap);
J
jorlow@chromium.org 已提交
2554 2555 2556 2557 2558 2559

  ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"),       0,      0));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"),    2000,   3000));
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"),    2000,   3000));
2560
  ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"),    4000,   6100));
2561
  c.ResetTableReader();
J
jorlow@chromium.org 已提交
2562 2563
}

I
Igor Sugak 已提交
2564
TEST_F(GeneralTableTest, ApproximateOffsetOfCompressed) {
K
kailiu 已提交
2565
  std::vector<CompressionType> compression_state;
I
Igor Canadi 已提交
2566
  if (!Snappy_Supported()) {
H
heyongqiang 已提交
2567 2568
    fprintf(stderr, "skipping snappy compression tests\n");
  } else {
K
kailiu 已提交
2569
    compression_state.push_back(kSnappyCompression);
H
heyongqiang 已提交
2570 2571
  }

I
Igor Canadi 已提交
2572
  if (!Zlib_Supported()) {
H
heyongqiang 已提交
2573 2574
    fprintf(stderr, "skipping zlib compression tests\n");
  } else {
K
kailiu 已提交
2575
    compression_state.push_back(kZlibCompression);
H
heyongqiang 已提交
2576 2577
  }

K
kailiu 已提交
2578 2579
  // TODO(kailiu) DoCompressionTest() doesn't work with BZip2.
  /*
I
Igor Canadi 已提交
2580
  if (!BZip2_Supported()) {
A
Albert Strasheim 已提交
2581 2582
    fprintf(stderr, "skipping bzip2 compression tests\n");
  } else {
K
kailiu 已提交
2583
    compression_state.push_back(kBZip2Compression);
A
Albert Strasheim 已提交
2584
  }
K
kailiu 已提交
2585
  */
A
Albert Strasheim 已提交
2586

I
Igor Canadi 已提交
2587 2588
  if (!LZ4_Supported()) {
    fprintf(stderr, "skipping lz4 and lz4hc compression tests\n");
A
Albert Strasheim 已提交
2589
  } else {
K
kailiu 已提交
2590 2591
    compression_state.push_back(kLZ4Compression);
    compression_state.push_back(kLZ4HCCompression);
H
heyongqiang 已提交
2592 2593
  }

2594 2595 2596 2597 2598 2599 2600
  if (!XPRESS_Supported()) {
    fprintf(stderr, "skipping xpress and xpress compression tests\n");
  }
  else {
    compression_state.push_back(kXpressCompression);
  }

K
kailiu 已提交
2601 2602
  for (auto state : compression_state) {
    DoCompressionTest(state);
H
heyongqiang 已提交
2603 2604 2605
  }
}

2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617
TEST_F(HarnessTest, Randomized0) {
  // part 1 out of 2
  const size_t part = 1;
  const size_t total = 2;
  RandomizedHarnessTest(part, total);
}

TEST_F(HarnessTest, Randomized1) {
  // part 2 out of 2
  const size_t part = 2;
  const size_t total = 2;
  RandomizedHarnessTest(part, total);
2618 2619
}

2620
#ifndef ROCKSDB_LITE
I
Igor Sugak 已提交
2621
TEST_F(HarnessTest, RandomizedLongDB) {
2622
  Random rnd(test::RandomSeed());
2623
  TestArgs args = {DB_TEST, false, 16, kNoCompression, 0, false};
2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643
  Init(args);
  int num_entries = 100000;
  for (int e = 0; e < num_entries; e++) {
    std::string v;
    Add(test::RandomKey(&rnd, rnd.Skewed(4)),
        test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());
  }
  Test(&rnd);

  // We must have created enough data to force merging
  int files = 0;
  for (int level = 0; level < db()->NumberLevels(); level++) {
    std::string value;
    char name[100];
    snprintf(name, sizeof(name), "rocksdb.num-files-at-level%d", level);
    ASSERT_TRUE(db()->GetProperty(name, &value));
    files += atoi(value.c_str());
  }
  ASSERT_GT(files, 0);
}
2644
#endif  // ROCKSDB_LITE
2645

I
Igor Sugak 已提交
2646
class MemTableTest : public testing::Test {};
2647

I
Igor Sugak 已提交
2648
TEST_F(MemTableTest, Simple) {
2649 2650
  InternalKeyComparator cmp(BytewiseComparator());
  auto table_factory = std::make_shared<SkipListFactory>();
I
Igor Canadi 已提交
2651 2652
  Options options;
  options.memtable_factory = table_factory;
2653
  ImmutableCFOptions ioptions(options);
2654
  WriteBufferManager wb(options.db_write_buffer_size);
2655 2656 2657
  MemTable* memtable =
      new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
                   kMaxSequenceNumber, 0 /* column_family_id */);
2658 2659 2660 2661 2662 2663 2664
  memtable->Ref();
  WriteBatch batch;
  WriteBatchInternal::SetSequence(&batch, 100);
  batch.Put(std::string("k1"), std::string("v1"));
  batch.Put(std::string("k2"), std::string("v2"));
  batch.Put(std::string("k3"), std::string("v3"));
  batch.Put(std::string("largekey"), std::string("vlarge"));
2665 2666
  batch.DeleteRange(std::string("chi"), std::string("xigua"));
  batch.DeleteRange(std::string("begin"), std::string("end"));
2667
  ColumnFamilyMemTablesDefault cf_mems_default(memtable);
2668 2669
  ASSERT_TRUE(
      WriteBatchInternal::InsertInto(&batch, &cf_mems_default, nullptr).ok());
2670

2671 2672
  for (int i = 0; i < 2; ++i) {
    Arena arena;
2673 2674 2675 2676 2677 2678 2679 2680 2681 2682
    ScopedArenaIterator arena_iter_guard;
    std::unique_ptr<InternalIterator> iter_guard;
    InternalIterator* iter;
    if (i == 0) {
      iter = memtable->NewIterator(ReadOptions(), &arena);
      arena_iter_guard.set(iter);
    } else {
      iter = memtable->NewRangeTombstoneIterator(ReadOptions());
      iter_guard.reset(iter);
    }
A
Andrew Kryczka 已提交
2683 2684 2685
    if (iter == nullptr) {
      continue;
    }
2686 2687 2688 2689 2690 2691
    iter->SeekToFirst();
    while (iter->Valid()) {
      fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(),
              iter->value().ToString().c_str());
      iter->Next();
    }
2692 2693
  }

2694
  delete memtable->Unref();
2695 2696
}

2697
// Test the empty key
I
Igor Sugak 已提交
2698
TEST_F(HarnessTest, SimpleEmptyKey) {
K
Kai Liu 已提交
2699 2700 2701
  auto args = GenerateArgList();
  for (const auto& arg : args) {
    Init(arg);
2702 2703 2704 2705 2706 2707
    Random rnd(test::RandomSeed() + 1);
    Add("", "v");
    Test(&rnd);
  }
}

I
Igor Sugak 已提交
2708
TEST_F(HarnessTest, SimpleSingle) {
K
Kai Liu 已提交
2709 2710 2711
  auto args = GenerateArgList();
  for (const auto& arg : args) {
    Init(arg);
2712 2713 2714 2715 2716 2717
    Random rnd(test::RandomSeed() + 2);
    Add("abc", "v");
    Test(&rnd);
  }
}

I
Igor Sugak 已提交
2718
TEST_F(HarnessTest, SimpleMulti) {
K
Kai Liu 已提交
2719 2720 2721
  auto args = GenerateArgList();
  for (const auto& arg : args) {
    Init(arg);
2722 2723 2724 2725 2726 2727 2728 2729
    Random rnd(test::RandomSeed() + 3);
    Add("abc", "v");
    Add("abcd", "v");
    Add("ac", "v2");
    Test(&rnd);
  }
}

I
Igor Sugak 已提交
2730
TEST_F(HarnessTest, SimpleSpecialKey) {
K
Kai Liu 已提交
2731 2732 2733
  auto args = GenerateArgList();
  for (const auto& arg : args) {
    Init(arg);
2734 2735 2736 2737 2738
    Random rnd(test::RandomSeed() + 4);
    Add("\xff\xff", "v3");
    Test(&rnd);
  }
}
2739

I
Igor Sugak 已提交
2740
TEST_F(HarnessTest, FooterTests) {
I
xxHash  
Igor Canadi 已提交
2741 2742 2743
  {
    // upconvert legacy block based
    std::string encoded;
2744
    Footer footer(kLegacyBlockBasedTableMagicNumber, 0);
I
xxHash  
Igor Canadi 已提交
2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757
    BlockHandle meta_index(10, 5), index(20, 15);
    footer.set_metaindex_handle(meta_index);
    footer.set_index_handle(index);
    footer.EncodeTo(&encoded);
    Footer decoded_footer;
    Slice encoded_slice(encoded);
    decoded_footer.DecodeFrom(&encoded_slice);
    ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
    ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
    ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
    ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
    ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
    ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
2758
    ASSERT_EQ(decoded_footer.version(), 0U);
I
xxHash  
Igor Canadi 已提交
2759 2760 2761 2762
  }
  {
    // xxhash block based
    std::string encoded;
2763
    Footer footer(kBlockBasedTableMagicNumber, 1);
I
xxHash  
Igor Canadi 已提交
2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777
    BlockHandle meta_index(10, 5), index(20, 15);
    footer.set_metaindex_handle(meta_index);
    footer.set_index_handle(index);
    footer.set_checksum(kxxHash);
    footer.EncodeTo(&encoded);
    Footer decoded_footer;
    Slice encoded_slice(encoded);
    decoded_footer.DecodeFrom(&encoded_slice);
    ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
    ASSERT_EQ(decoded_footer.checksum(), kxxHash);
    ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
    ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
    ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
    ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
2778
    ASSERT_EQ(decoded_footer.version(), 1U);
I
xxHash  
Igor Canadi 已提交
2779
  }
2780 2781
// Plain table is not supported in ROCKSDB_LITE
#ifndef ROCKSDB_LITE
I
xxHash  
Igor Canadi 已提交
2782 2783 2784
  {
    // upconvert legacy plain table
    std::string encoded;
2785
    Footer footer(kLegacyPlainTableMagicNumber, 0);
I
xxHash  
Igor Canadi 已提交
2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798
    BlockHandle meta_index(10, 5), index(20, 15);
    footer.set_metaindex_handle(meta_index);
    footer.set_index_handle(index);
    footer.EncodeTo(&encoded);
    Footer decoded_footer;
    Slice encoded_slice(encoded);
    decoded_footer.DecodeFrom(&encoded_slice);
    ASSERT_EQ(decoded_footer.table_magic_number(), kPlainTableMagicNumber);
    ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
    ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
    ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
    ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
    ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
2799
    ASSERT_EQ(decoded_footer.version(), 0U);
I
xxHash  
Igor Canadi 已提交
2800 2801 2802 2803
  }
  {
    // xxhash block based
    std::string encoded;
2804
    Footer footer(kPlainTableMagicNumber, 1);
I
xxHash  
Igor Canadi 已提交
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818
    BlockHandle meta_index(10, 5), index(20, 15);
    footer.set_metaindex_handle(meta_index);
    footer.set_index_handle(index);
    footer.set_checksum(kxxHash);
    footer.EncodeTo(&encoded);
    Footer decoded_footer;
    Slice encoded_slice(encoded);
    decoded_footer.DecodeFrom(&encoded_slice);
    ASSERT_EQ(decoded_footer.table_magic_number(), kPlainTableMagicNumber);
    ASSERT_EQ(decoded_footer.checksum(), kxxHash);
    ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
    ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
    ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
    ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
2819 2820
    ASSERT_EQ(decoded_footer.version(), 1U);
  }
2821
#endif  // !ROCKSDB_LITE
2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839
  {
    // version == 2
    std::string encoded;
    Footer footer(kBlockBasedTableMagicNumber, 2);
    BlockHandle meta_index(10, 5), index(20, 15);
    footer.set_metaindex_handle(meta_index);
    footer.set_index_handle(index);
    footer.EncodeTo(&encoded);
    Footer decoded_footer;
    Slice encoded_slice(encoded);
    decoded_footer.DecodeFrom(&encoded_slice);
    ASSERT_EQ(decoded_footer.table_magic_number(), kBlockBasedTableMagicNumber);
    ASSERT_EQ(decoded_footer.checksum(), kCRC32c);
    ASSERT_EQ(decoded_footer.metaindex_handle().offset(), meta_index.offset());
    ASSERT_EQ(decoded_footer.metaindex_handle().size(), meta_index.size());
    ASSERT_EQ(decoded_footer.index_handle().offset(), index.offset());
    ASSERT_EQ(decoded_footer.index_handle().size(), index.size());
    ASSERT_EQ(decoded_footer.version(), 2U);
I
xxHash  
Igor Canadi 已提交
2840 2841 2842
  }
}

2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901
class IndexBlockRestartIntervalTest
    : public BlockBasedTableTest,
      public ::testing::WithParamInterface<int> {
 public:
  static std::vector<int> GetRestartValues() { return {-1, 0, 1, 8, 16, 32}; }
};

INSTANTIATE_TEST_CASE_P(
    IndexBlockRestartIntervalTest, IndexBlockRestartIntervalTest,
    ::testing::ValuesIn(IndexBlockRestartIntervalTest::GetRestartValues()));

TEST_P(IndexBlockRestartIntervalTest, IndexBlockRestartInterval) {
  const int kKeysInTable = 10000;
  const int kKeySize = 100;
  const int kValSize = 500;

  int index_block_restart_interval = GetParam();

  Options options;
  BlockBasedTableOptions table_options;
  table_options.block_size = 64;  // small block size to get big index block
  table_options.index_block_restart_interval = index_block_restart_interval;
  options.table_factory.reset(new BlockBasedTableFactory(table_options));

  TableConstructor c(BytewiseComparator());
  static Random rnd(301);
  for (int i = 0; i < kKeysInTable; i++) {
    InternalKey k(RandomString(&rnd, kKeySize), 0, kTypeValue);
    c.Add(k.Encode().ToString(), RandomString(&rnd, kValSize));
  }

  std::vector<std::string> keys;
  stl_wrappers::KVMap kvmap;
  std::unique_ptr<InternalKeyComparator> comparator(
      new InternalKeyComparator(BytewiseComparator()));
  const ImmutableCFOptions ioptions(options);
  c.Finish(options, ioptions, table_options, *comparator, &keys, &kvmap);
  auto reader = c.GetTableReader();

  std::unique_ptr<InternalIterator> db_iter(reader->NewIterator(ReadOptions()));

  // Test point lookup
  for (auto& kv : kvmap) {
    db_iter->Seek(kv.first);

    ASSERT_TRUE(db_iter->Valid());
    ASSERT_OK(db_iter->status());
    ASSERT_EQ(db_iter->key(), kv.first);
    ASSERT_EQ(db_iter->value(), kv.second);
  }

  // Test iterating
  auto kv_iter = kvmap.begin();
  for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
    ASSERT_EQ(db_iter->key(), kv_iter->first);
    ASSERT_EQ(db_iter->value(), kv_iter->second);
    kv_iter++;
  }
  ASSERT_EQ(kv_iter, kvmap.end());
2902
  c.ResetTableReader();
2903 2904
}

2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927
class PrefixTest : public testing::Test {
 public:
  PrefixTest() : testing::Test() {}
  ~PrefixTest() {}
};

namespace {
// A simple PrefixExtractor that only works for test PrefixAndWholeKeyTest
class TestPrefixExtractor : public rocksdb::SliceTransform {
 public:
  ~TestPrefixExtractor() override{};
  const char* Name() const override { return "TestPrefixExtractor"; }

  rocksdb::Slice Transform(const rocksdb::Slice& src) const override {
    assert(IsValid(src));
    return rocksdb::Slice(src.data(), 3);
  }

  bool InDomain(const rocksdb::Slice& src) const override {
    assert(IsValid(src));
    return true;
  }

2928
  bool InRange(const rocksdb::Slice& dst) const override { return true; }
2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963

  bool IsValid(const rocksdb::Slice& src) const {
    if (src.size() != 4) {
      return false;
    }
    if (src[0] != '[') {
      return false;
    }
    if (src[1] < '0' || src[1] > '9') {
      return false;
    }
    if (src[2] != ']') {
      return false;
    }
    if (src[3] < '0' || src[3] > '9') {
      return false;
    }
    return true;
  }
};
}  // namespace

TEST_F(PrefixTest, PrefixAndWholeKeyTest) {
  rocksdb::Options options;
  options.compaction_style = rocksdb::kCompactionStyleUniversal;
  options.num_levels = 20;
  options.create_if_missing = true;
  options.optimize_filters_for_hits = false;
  options.target_file_size_base = 268435456;
  options.prefix_extractor = std::make_shared<TestPrefixExtractor>();
  rocksdb::BlockBasedTableOptions bbto;
  bbto.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10));
  bbto.block_size = 262144;
  bbto.whole_key_filtering = true;

2964
  const std::string kDBPath = test::TmpDir() + "/table_prefix_test";
2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985
  options.table_factory.reset(NewBlockBasedTableFactory(bbto));
  DestroyDB(kDBPath, options);
  rocksdb::DB* db;
  ASSERT_OK(rocksdb::DB::Open(options, kDBPath, &db));

  // Create a bunch of keys with 10 filters.
  for (int i = 0; i < 10; i++) {
    std::string prefix = "[" + std::to_string(i) + "]";
    for (int j = 0; j < 10; j++) {
      std::string key = prefix + std::to_string(j);
      db->Put(rocksdb::WriteOptions(), key, "1");
    }
  }

  // Trigger compaction.
  db->CompactRange(CompactRangeOptions(), nullptr, nullptr);
  delete db;
  // In the second round, turn whole_key_filtering off and expect
  // rocksdb still works.
}

2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162
TEST_F(BlockBasedTableTest, TableWithGlobalSeqno) {
  BlockBasedTableOptions bbto;
  test::StringSink* sink = new test::StringSink();
  unique_ptr<WritableFileWriter> file_writer(test::GetWritableFileWriter(sink));
  Options options;
  options.table_factory.reset(NewBlockBasedTableFactory(bbto));
  const ImmutableCFOptions ioptions(options);
  InternalKeyComparator ikc(options.comparator);
  std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
      int_tbl_prop_collector_factories;
  int_tbl_prop_collector_factories.emplace_back(
      new SstFileWriterPropertiesCollectorFactory(2 /* version */,
                                                  0 /* global_seqno*/));
  std::string column_family_name;
  std::unique_ptr<TableBuilder> builder(options.table_factory->NewTableBuilder(
      TableBuilderOptions(ioptions, ikc, &int_tbl_prop_collector_factories,
                          kNoCompression, CompressionOptions(),
                          nullptr /* compression_dict */,
                          false /* skip_filters */, column_family_name, -1),
      TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
      file_writer.get()));

  for (char c = 'a'; c <= 'z'; ++c) {
    std::string key(8, c);
    std::string value = key;
    InternalKey ik(key, 0, kTypeValue);

    builder->Add(ik.Encode(), value);
  }
  ASSERT_OK(builder->Finish());
  file_writer->Flush();

  test::RandomRWStringSink ss_rw(sink);
  uint32_t version;
  uint64_t global_seqno;
  uint64_t global_seqno_offset;

  // Helper function to get version, global_seqno, global_seqno_offset
  std::function<void()> GetVersionAndGlobalSeqno = [&]() {
    unique_ptr<RandomAccessFileReader> file_reader(
        test::GetRandomAccessFileReader(
            new test::StringSource(ss_rw.contents(), 73342, true)));

    TableProperties* props = nullptr;
    ASSERT_OK(ReadTableProperties(file_reader.get(), ss_rw.contents().size(),
                                  kBlockBasedTableMagicNumber, ioptions,
                                  &props));

    UserCollectedProperties user_props = props->user_collected_properties;
    version = DecodeFixed32(
        user_props[ExternalSstFilePropertyNames::kVersion].c_str());
    global_seqno = DecodeFixed64(
        user_props[ExternalSstFilePropertyNames::kGlobalSeqno].c_str());
    global_seqno_offset =
        props->properties_offsets[ExternalSstFilePropertyNames::kGlobalSeqno];

    delete props;
  };

  // Helper function to update the value of the global seqno in the file
  std::function<void(uint64_t)> SetGlobalSeqno = [&](uint64_t val) {
    std::string new_global_seqno;
    PutFixed64(&new_global_seqno, val);

    ASSERT_OK(ss_rw.Write(global_seqno_offset, new_global_seqno));
  };

  // Helper function to get the contents of the table InternalIterator
  unique_ptr<TableReader> table_reader;
  std::function<InternalIterator*()> GetTableInternalIter = [&]() {
    unique_ptr<RandomAccessFileReader> file_reader(
        test::GetRandomAccessFileReader(
            new test::StringSource(ss_rw.contents(), 73342, true)));

    options.table_factory->NewTableReader(
        TableReaderOptions(ioptions, EnvOptions(), ikc), std::move(file_reader),
        ss_rw.contents().size(), &table_reader);

    return table_reader->NewIterator(ReadOptions());
  };

  GetVersionAndGlobalSeqno();
  ASSERT_EQ(2, version);
  ASSERT_EQ(0, global_seqno);

  InternalIterator* iter = GetTableInternalIter();
  char current_c = 'a';
  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
    ParsedInternalKey pik;
    ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));

    ASSERT_EQ(pik.type, ValueType::kTypeValue);
    ASSERT_EQ(pik.sequence, 0);
    ASSERT_EQ(pik.user_key, iter->value());
    ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
    current_c++;
  }
  ASSERT_EQ(current_c, 'z' + 1);
  delete iter;

  // Update global sequence number to 10
  SetGlobalSeqno(10);
  GetVersionAndGlobalSeqno();
  ASSERT_EQ(2, version);
  ASSERT_EQ(10, global_seqno);

  iter = GetTableInternalIter();
  current_c = 'a';
  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
    ParsedInternalKey pik;
    ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));

    ASSERT_EQ(pik.type, ValueType::kTypeValue);
    ASSERT_EQ(pik.sequence, 10);
    ASSERT_EQ(pik.user_key, iter->value());
    ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
    current_c++;
  }
  ASSERT_EQ(current_c, 'z' + 1);

  // Verify Seek
  for (char c = 'a'; c <= 'z'; c++) {
    std::string k = std::string(8, c);
    InternalKey ik(k, 10, kValueTypeForSeek);
    iter->Seek(ik.Encode());
    ASSERT_TRUE(iter->Valid());

    ParsedInternalKey pik;
    ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));

    ASSERT_EQ(pik.type, ValueType::kTypeValue);
    ASSERT_EQ(pik.sequence, 10);
    ASSERT_EQ(pik.user_key.ToString(), k);
    ASSERT_EQ(iter->value().ToString(), k);
  }
  delete iter;

  // Update global sequence number to 3
  SetGlobalSeqno(3);
  GetVersionAndGlobalSeqno();
  ASSERT_EQ(2, version);
  ASSERT_EQ(3, global_seqno);

  iter = GetTableInternalIter();
  current_c = 'a';
  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
    ParsedInternalKey pik;
    ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));

    ASSERT_EQ(pik.type, ValueType::kTypeValue);
    ASSERT_EQ(pik.sequence, 3);
    ASSERT_EQ(pik.user_key, iter->value());
    ASSERT_EQ(pik.user_key.ToString(), std::string(8, current_c));
    current_c++;
  }
  ASSERT_EQ(current_c, 'z' + 1);

  // Verify Seek
  for (char c = 'a'; c <= 'z'; c++) {
    std::string k = std::string(8, c);
    // seqno=4 is less than 3 so we still should get our key
    InternalKey ik(k, 4, kValueTypeForSeek);
    iter->Seek(ik.Encode());
    ASSERT_TRUE(iter->Valid());

    ParsedInternalKey pik;
    ASSERT_TRUE(ParseInternalKey(iter->key(), &pik));

    ASSERT_EQ(pik.type, ValueType::kTypeValue);
    ASSERT_EQ(pik.sequence, 3);
    ASSERT_EQ(pik.user_key.ToString(), k);
    ASSERT_EQ(iter->value().ToString(), k);
  }

  delete iter;
}

3163
}  // namespace rocksdb
J
jorlow@chromium.org 已提交
3164 3165

int main(int argc, char** argv) {
I
Igor Sugak 已提交
3166 3167
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
J
jorlow@chromium.org 已提交
3168
}