mirror of https://github.com/facebook/rocksdb.git
351 lines
9.8 KiB
C++
351 lines
9.8 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// 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).
|
|
//
|
|
// 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.
|
|
|
|
#include <string>
|
|
#include "db/file_indexer.h"
|
|
#include "db/dbformat.h"
|
|
#include "db/version_edit.h"
|
|
#include "port/stack_trace.h"
|
|
#include "rocksdb/comparator.h"
|
|
#include "util/testharness.h"
|
|
#include "util/testutil.h"
|
|
|
|
namespace rocksdb {
|
|
|
|
class IntComparator : public Comparator {
|
|
public:
|
|
int Compare(const Slice& a, const Slice& b) const override {
|
|
assert(a.size() == 8);
|
|
assert(b.size() == 8);
|
|
int64_t diff = *reinterpret_cast<const int64_t*>(a.data()) -
|
|
*reinterpret_cast<const int64_t*>(b.data());
|
|
if (diff < 0) {
|
|
return -1;
|
|
} else if (diff == 0) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const char* Name() const override { return "IntComparator"; }
|
|
|
|
void FindShortestSeparator(std::string* /*start*/,
|
|
const Slice& /*limit*/) const override {}
|
|
|
|
void FindShortSuccessor(std::string* /*key*/) const override {}
|
|
};
|
|
|
|
class FileIndexerTest : public testing::Test {
|
|
public:
|
|
FileIndexerTest()
|
|
: kNumLevels(4), files(new std::vector<FileMetaData*>[kNumLevels]) {}
|
|
|
|
~FileIndexerTest() override {
|
|
ClearFiles();
|
|
delete[] files;
|
|
}
|
|
|
|
void AddFile(int level, int64_t smallest, int64_t largest) {
|
|
auto* f = new FileMetaData();
|
|
f->smallest = IntKey(smallest);
|
|
f->largest = IntKey(largest);
|
|
files[level].push_back(f);
|
|
}
|
|
|
|
InternalKey IntKey(int64_t v) {
|
|
return InternalKey(Slice(reinterpret_cast<char*>(&v), 8), 0, kTypeValue);
|
|
}
|
|
|
|
void ClearFiles() {
|
|
for (uint32_t i = 0; i < kNumLevels; ++i) {
|
|
for (auto* f : files[i]) {
|
|
delete f;
|
|
}
|
|
files[i].clear();
|
|
}
|
|
}
|
|
|
|
void GetNextLevelIndex(const uint32_t level, const uint32_t file_index,
|
|
const int cmp_smallest, const int cmp_largest, int32_t* left_index,
|
|
int32_t* right_index) {
|
|
*left_index = 100;
|
|
*right_index = 100;
|
|
indexer->GetNextLevelIndex(level, file_index, cmp_smallest, cmp_largest,
|
|
left_index, right_index);
|
|
}
|
|
|
|
int32_t left = 100;
|
|
int32_t right = 100;
|
|
const uint32_t kNumLevels;
|
|
IntComparator ucmp;
|
|
FileIndexer* indexer;
|
|
|
|
std::vector<FileMetaData*>* files;
|
|
};
|
|
|
|
// Case 0: Empty
|
|
TEST_F(FileIndexerTest, Empty) {
|
|
Arena arena;
|
|
indexer = new FileIndexer(&ucmp);
|
|
indexer->UpdateIndex(&arena, 0, files);
|
|
delete indexer;
|
|
}
|
|
|
|
// Case 1: no overlap, files are on the left of next level files
|
|
TEST_F(FileIndexerTest, no_overlap_left) {
|
|
Arena arena;
|
|
indexer = new FileIndexer(&ucmp);
|
|
// level 1
|
|
AddFile(1, 100, 200);
|
|
AddFile(1, 300, 400);
|
|
AddFile(1, 500, 600);
|
|
// level 2
|
|
AddFile(2, 1500, 1600);
|
|
AddFile(2, 1601, 1699);
|
|
AddFile(2, 1700, 1800);
|
|
// level 3
|
|
AddFile(3, 2500, 2600);
|
|
AddFile(3, 2601, 2699);
|
|
AddFile(3, 2700, 2800);
|
|
indexer->UpdateIndex(&arena, kNumLevels, files);
|
|
for (uint32_t level = 1; level < 3; ++level) {
|
|
for (uint32_t f = 0; f < 3; ++f) {
|
|
GetNextLevelIndex(level, f, -1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(level, f, 0, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(level, f, 1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(level, f, 1, 0, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(level, f, 1, 1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(2, right);
|
|
}
|
|
}
|
|
delete indexer;
|
|
ClearFiles();
|
|
}
|
|
|
|
// Case 2: no overlap, files are on the right of next level files
|
|
TEST_F(FileIndexerTest, no_overlap_right) {
|
|
Arena arena;
|
|
indexer = new FileIndexer(&ucmp);
|
|
// level 1
|
|
AddFile(1, 2100, 2200);
|
|
AddFile(1, 2300, 2400);
|
|
AddFile(1, 2500, 2600);
|
|
// level 2
|
|
AddFile(2, 1500, 1600);
|
|
AddFile(2, 1501, 1699);
|
|
AddFile(2, 1700, 1800);
|
|
// level 3
|
|
AddFile(3, 500, 600);
|
|
AddFile(3, 501, 699);
|
|
AddFile(3, 700, 800);
|
|
indexer->UpdateIndex(&arena, kNumLevels, files);
|
|
for (uint32_t level = 1; level < 3; ++level) {
|
|
for (uint32_t f = 0; f < 3; ++f) {
|
|
GetNextLevelIndex(level, f, -1, -1, &left, &right);
|
|
ASSERT_EQ(f == 0 ? 0 : 3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(level, f, 0, -1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(level, f, 1, -1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(level, f, 1, -1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(level, f, 1, 0, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(level, f, 1, 1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
}
|
|
}
|
|
delete indexer;
|
|
}
|
|
|
|
// Case 3: empty L2
|
|
TEST_F(FileIndexerTest, empty_L2) {
|
|
Arena arena;
|
|
indexer = new FileIndexer(&ucmp);
|
|
for (uint32_t i = 1; i < kNumLevels; ++i) {
|
|
ASSERT_EQ(0U, indexer->LevelIndexSize(i));
|
|
}
|
|
// level 1
|
|
AddFile(1, 2100, 2200);
|
|
AddFile(1, 2300, 2400);
|
|
AddFile(1, 2500, 2600);
|
|
// level 3
|
|
AddFile(3, 500, 600);
|
|
AddFile(3, 501, 699);
|
|
AddFile(3, 700, 800);
|
|
indexer->UpdateIndex(&arena, kNumLevels, files);
|
|
for (uint32_t f = 0; f < 3; ++f) {
|
|
GetNextLevelIndex(1, f, -1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(1, f, 0, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(1, f, 1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(1, f, 1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(1, f, 1, 0, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
GetNextLevelIndex(1, f, 1, 1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(-1, right);
|
|
}
|
|
delete indexer;
|
|
ClearFiles();
|
|
}
|
|
|
|
// Case 4: mixed
|
|
TEST_F(FileIndexerTest, mixed) {
|
|
Arena arena;
|
|
indexer = new FileIndexer(&ucmp);
|
|
// level 1
|
|
AddFile(1, 100, 200);
|
|
AddFile(1, 250, 400);
|
|
AddFile(1, 450, 500);
|
|
// level 2
|
|
AddFile(2, 100, 150); // 0
|
|
AddFile(2, 200, 250); // 1
|
|
AddFile(2, 251, 300); // 2
|
|
AddFile(2, 301, 350); // 3
|
|
AddFile(2, 500, 600); // 4
|
|
// level 3
|
|
AddFile(3, 0, 50);
|
|
AddFile(3, 100, 200);
|
|
AddFile(3, 201, 250);
|
|
indexer->UpdateIndex(&arena, kNumLevels, files);
|
|
// level 1, 0
|
|
GetNextLevelIndex(1, 0, -1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(0, right);
|
|
GetNextLevelIndex(1, 0, 0, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(0, right);
|
|
GetNextLevelIndex(1, 0, 1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(1, 0, 1, 0, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(1, 0, 1, 1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(4, right);
|
|
// level 1, 1
|
|
GetNextLevelIndex(1, 1, -1, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(1, 1, 0, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(1, 1, 1, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(3, right);
|
|
GetNextLevelIndex(1, 1, 1, 0, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(3, right);
|
|
GetNextLevelIndex(1, 1, 1, 1, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(4, right);
|
|
// level 1, 2
|
|
GetNextLevelIndex(1, 2, -1, -1, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(3, right);
|
|
GetNextLevelIndex(1, 2, 0, -1, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(3, right);
|
|
GetNextLevelIndex(1, 2, 1, -1, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(4, right);
|
|
GetNextLevelIndex(1, 2, 1, 0, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(4, right);
|
|
GetNextLevelIndex(1, 2, 1, 1, &left, &right);
|
|
ASSERT_EQ(4, left);
|
|
ASSERT_EQ(4, right);
|
|
// level 2, 0
|
|
GetNextLevelIndex(2, 0, -1, -1, &left, &right);
|
|
ASSERT_EQ(0, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(2, 0, 0, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(2, 0, 1, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(2, 0, 1, 0, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(2, 0, 1, 1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(2, right);
|
|
// level 2, 1
|
|
GetNextLevelIndex(2, 1, -1, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(2, 1, 0, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(1, right);
|
|
GetNextLevelIndex(2, 1, 1, -1, &left, &right);
|
|
ASSERT_EQ(1, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(2, 1, 1, 0, &left, &right);
|
|
ASSERT_EQ(2, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(2, 1, 1, 1, &left, &right);
|
|
ASSERT_EQ(2, left);
|
|
ASSERT_EQ(2, right);
|
|
// level 2, [2 - 4], no overlap
|
|
for (uint32_t f = 2; f <= 4; ++f) {
|
|
GetNextLevelIndex(2, f, -1, -1, &left, &right);
|
|
ASSERT_EQ(f == 2 ? 2 : 3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(2, f, 0, -1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(2, f, 1, -1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(2, f, 1, 0, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
GetNextLevelIndex(2, f, 1, 1, &left, &right);
|
|
ASSERT_EQ(3, left);
|
|
ASSERT_EQ(2, right);
|
|
}
|
|
delete indexer;
|
|
ClearFiles();
|
|
}
|
|
|
|
} // namespace rocksdb
|
|
|
|
int main(int argc, char** argv) {
|
|
rocksdb::port::InstallStackTraceHandler();
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|