// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
-// This source code is licensed under the BSD-style license found in the
-// LICENSE file in the root directory of this source tree. An additional grant
-// of patent rights can be found in the PATENTS file in the same directory.
+// 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 "db/version_set.h"
+#include "db/log_writer.h"
+#include "table/mock_table.h"
#include "util/logging.h"
+#include "util/string_util.h"
#include "util/testharness.h"
#include "util/testutil.h"
public:
CountingLogger() : log_count(0) {}
using Logger::Logv;
- virtual void Logv(const char* format, va_list ap) override { log_count++; }
+ virtual void Logv(const char* /*format*/, va_list /*ap*/) override {
+ log_count++;
+ }
int log_count;
};
f->num_deletions = 0;
vstorage_.AddFile(level, f);
}
+
+ void Add(int level, uint32_t file_number, const InternalKey& smallest,
+ const InternalKey& largest, uint64_t file_size = 0) {
+ assert(level < vstorage_.num_levels());
+ FileMetaData* f = new FileMetaData;
+ f->fd = FileDescriptor(file_number, 0, file_size);
+ f->smallest = smallest;
+ f->largest = largest;
+ f->compensated_file_size = file_size;
+ f->refs = 0;
+ f->num_entries = 0;
+ f->num_deletions = 0;
+ vstorage_.AddFile(level, f);
+ }
+
+ std::string GetOverlappingFiles(int level, const InternalKey& begin,
+ const InternalKey& end) {
+ std::vector<FileMetaData*> inputs;
+ vstorage_.GetOverlappingInputs(level, &begin, &end, &inputs);
+
+ std::string result;
+ for (size_t i = 0; i < inputs.size(); ++i) {
+ if (i > 0) {
+ result += ",";
+ }
+ AppendNumberTo(&result, inputs[i]->fd.GetNumber());
+ }
+ return result;
+ }
};
TEST_F(VersionStorageInfoTest, MaxBytesForLevelStatic) {
Add(5, 2U, "3", "4", 550U);
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
ASSERT_EQ(0, logger_->log_count);
- ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 210U);
+ ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1000U);
ASSERT_EQ(vstorage_.base_level(), 4);
Add(4, 3U, "3", "4", 550U);
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
ASSERT_EQ(0, logger_->log_count);
- ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 210U);
+ ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1000U);
ASSERT_EQ(vstorage_.base_level(), 4);
Add(3, 4U, "3", "4", 250U);
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
ASSERT_EQ(1, logger_->log_count);
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1005U);
- ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 201U);
+ ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 1000U);
ASSERT_EQ(vstorage_.base_level(), 3);
Add(1, 6U, "3", "4", 5U);
ASSERT_GT(vstorage_.MaxBytesForLevel(4), 1005U);
ASSERT_GT(vstorage_.MaxBytesForLevel(3), 1005U);
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 1005U);
- ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 201U);
+ ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 1000U);
ASSERT_EQ(vstorage_.base_level(), 1);
}
ASSERT_EQ(vstorage_.MaxBytesForLevel(5), 3000U * kOneGB);
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 300U * kOneGB);
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 30U * kOneGB);
- ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 3U * kOneGB);
+ ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 10U * kOneGB);
ASSERT_EQ(vstorage_.base_level(), 2);
ASSERT_EQ(0, logger_->log_count);
}
ASSERT_EQ(4U, vstorage_.EstimateLiveDataSize());
}
+TEST_F(VersionStorageInfoTest, GetOverlappingInputs) {
+ // Two files that overlap at the range deletion tombstone sentinel.
+ Add(1, 1U, {"a", 0, kTypeValue}, {"b", kMaxSequenceNumber, kTypeRangeDeletion}, 1);
+ Add(1, 2U, {"b", 0, kTypeValue}, {"c", 0, kTypeValue}, 1);
+ // Two files that overlap at the same user key.
+ Add(1, 3U, {"d", 0, kTypeValue}, {"e", kMaxSequenceNumber, kTypeValue}, 1);
+ Add(1, 4U, {"e", 0, kTypeValue}, {"f", 0, kTypeValue}, 1);
+ // Two files that do not overlap.
+ Add(1, 5U, {"g", 0, kTypeValue}, {"h", 0, kTypeValue}, 1);
+ Add(1, 6U, {"i", 0, kTypeValue}, {"j", 0, kTypeValue}, 1);
+ vstorage_.UpdateNumNonEmptyLevels();
+ vstorage_.GenerateLevelFilesBrief();
+
+ ASSERT_EQ("1,2", GetOverlappingFiles(
+ 1, {"a", 0, kTypeValue}, {"b", 0, kTypeValue}));
+ ASSERT_EQ("1", GetOverlappingFiles(
+ 1, {"a", 0, kTypeValue}, {"b", kMaxSequenceNumber, kTypeRangeDeletion}));
+ ASSERT_EQ("2", GetOverlappingFiles(
+ 1, {"b", kMaxSequenceNumber, kTypeValue}, {"c", 0, kTypeValue}));
+ ASSERT_EQ("3,4", GetOverlappingFiles(
+ 1, {"d", 0, kTypeValue}, {"e", 0, kTypeValue}));
+ ASSERT_EQ("3", GetOverlappingFiles(
+ 1, {"d", 0, kTypeValue}, {"e", kMaxSequenceNumber, kTypeRangeDeletion}));
+ ASSERT_EQ("3,4", GetOverlappingFiles(
+ 1, {"e", kMaxSequenceNumber, kTypeValue}, {"f", 0, kTypeValue}));
+ ASSERT_EQ("3,4", GetOverlappingFiles(
+ 1, {"e", 0, kTypeValue}, {"f", 0, kTypeValue}));
+ ASSERT_EQ("5", GetOverlappingFiles(
+ 1, {"g", 0, kTypeValue}, {"h", 0, kTypeValue}));
+ ASSERT_EQ("6", GetOverlappingFiles(
+ 1, {"i", 0, kTypeValue}, {"j", 0, kTypeValue}));
+}
+
+
class FindLevelFileTest : public testing::Test {
public:
LevelFilesBrief file_level_;
ASSERT_TRUE(Overlaps("600", "700"));
}
+class ManifestWriterTest : public testing::Test {
+ public:
+ ManifestWriterTest()
+ : env_(Env::Default()),
+ dbname_(test::PerThreadDBPath("version_set_test")),
+ db_options_(),
+ mutable_cf_options_(cf_options_),
+ table_cache_(NewLRUCache(50000, 16)),
+ write_buffer_manager_(db_options_.db_write_buffer_size),
+ versions_(new VersionSet(dbname_, &db_options_, env_options_,
+ table_cache_.get(), &write_buffer_manager_,
+ &write_controller_)),
+ shutting_down_(false),
+ mock_table_factory_(std::make_shared<mock::MockTableFactory>()) {
+ EXPECT_OK(env_->CreateDirIfMissing(dbname_));
+ db_options_.db_paths.emplace_back(dbname_,
+ std::numeric_limits<uint64_t>::max());
+ }
+
+ // Create DB with 3 column families.
+ void NewDB() {
+ VersionEdit new_db;
+ new_db.SetLogNumber(0);
+ new_db.SetNextFile(2);
+ new_db.SetLastSequence(0);
+
+ const std::vector<std::string> cf_names = {kDefaultColumnFamilyName,
+ "alice", "bob"};
+ const int kInitialNumOfCfs = static_cast<int>(cf_names.size());
+ autovector<VersionEdit> new_cfs;
+ uint64_t last_seq = 1;
+ uint32_t cf_id = 1;
+ for (int i = 1; i != kInitialNumOfCfs; ++i) {
+ VersionEdit new_cf;
+ new_cf.AddColumnFamily(cf_names[i]);
+ new_cf.SetColumnFamily(cf_id++);
+ new_cf.SetLogNumber(0);
+ new_cf.SetNextFile(2);
+ new_cf.SetLastSequence(last_seq++);
+ new_cfs.emplace_back(new_cf);
+ }
+
+ const std::string manifest = DescriptorFileName(dbname_, 1);
+ unique_ptr<WritableFile> file;
+ Status s = env_->NewWritableFile(
+ manifest, &file, env_->OptimizeForManifestWrite(env_options_));
+ ASSERT_OK(s);
+ unique_ptr<WritableFileWriter> file_writer(
+ new WritableFileWriter(std::move(file), manifest, env_options_));
+ {
+ log::Writer log(std::move(file_writer), 0, false);
+ std::string record;
+ new_db.EncodeTo(&record);
+ s = log.AddRecord(record);
+ for (const auto& e : new_cfs) {
+ e.EncodeTo(&record);
+ s = log.AddRecord(record);
+ ASSERT_OK(s);
+ }
+ }
+ ASSERT_OK(s);
+ // Make "CURRENT" file point to the new manifest file.
+ s = SetCurrentFile(env_, dbname_, 1, nullptr);
+
+ std::vector<ColumnFamilyDescriptor> column_families;
+ cf_options_.table_factory = mock_table_factory_;
+ for (const auto& cf_name : cf_names) {
+ column_families.emplace_back(cf_name, cf_options_);
+ }
+
+ EXPECT_OK(versions_->Recover(column_families, false));
+ EXPECT_EQ(kInitialNumOfCfs,
+ versions_->GetColumnFamilySet()->NumberOfColumnFamilies());
+ for (auto cfd : *versions_->GetColumnFamilySet()) {
+ cfds_.emplace_back(cfd);
+ }
+ }
+
+ Env* env_;
+ const std::string dbname_;
+ EnvOptions env_options_;
+ ImmutableDBOptions db_options_;
+ ColumnFamilyOptions cf_options_;
+ MutableCFOptions mutable_cf_options_;
+ std::shared_ptr<Cache> table_cache_;
+ WriteController write_controller_;
+ WriteBufferManager write_buffer_manager_;
+ std::unique_ptr<VersionSet> versions_;
+ InstrumentedMutex mutex_;
+ std::atomic<bool> shutting_down_;
+ std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
+ std::vector<ColumnFamilyData*> cfds_;
+};
+
+TEST_F(ManifestWriterTest, SameColumnFamilyGroupCommit) {
+ NewDB();
+ const int kGroupSize = 5;
+ std::vector<VersionEdit> edits(kGroupSize);
+ std::vector<ColumnFamilyData*> cfds(kGroupSize, cfds_[0]);
+ std::vector<MutableCFOptions> all_mutable_cf_options(kGroupSize,
+ mutable_cf_options_);
+ std::vector<autovector<VersionEdit*>> edit_lists(kGroupSize);
+ for (int i = 0; i != kGroupSize; ++i) {
+ edit_lists[i].emplace_back(&edits[i]);
+ }
+
+ int count = 0;
+ SyncPoint::GetInstance()->SetCallBack(
+ "VersionSet::ProcessManifestWrites:SameColumnFamily", [&](void* arg) {
+ uint32_t* cf_id = reinterpret_cast<uint32_t*>(arg);
+ EXPECT_EQ(0, *cf_id);
+ ++count;
+ });
+ SyncPoint::GetInstance()->EnableProcessing();
+ mutex_.Lock();
+ Status s =
+ versions_->LogAndApply(cfds, all_mutable_cf_options, edit_lists, &mutex_);
+ mutex_.Unlock();
+ EXPECT_OK(s);
+ EXPECT_EQ(kGroupSize - 1, count);
+}
} // namespace rocksdb
int main(int argc, char** argv) {