Packages
rocksdb
1.3.2
3.1.1
3.1.0
3.0.0
2.6.2
2.6.1
retired
2.6.0
retired
2.5.0
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
1.9.0
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.1
1.1.0
1.0.0
0.26.2
0.26.1
0.26.0
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
RocksDB for Erlang
Current section
Files
Jump to
Current section
Files
deps/rocksdb/utilities/memory/memory_test.cc
// 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).
#ifndef ROCKSDB_LITE
#include "db/db_impl.h"
#include "rocksdb/cache.h"
#include "rocksdb/table.h"
#include "rocksdb/utilities/memory_util.h"
#include "rocksdb/utilities/stackable_db.h"
#include "table/block_based_table_factory.h"
#include "util/string_util.h"
#include "util/testharness.h"
#include "util/testutil.h"
namespace rocksdb {
class MemoryTest : public testing::Test {
public:
MemoryTest() : kDbDir(test::PerThreadDBPath("memory_test")), rnd_(301) {
assert(Env::Default()->CreateDirIfMissing(kDbDir).ok());
}
std::string GetDBName(int id) { return kDbDir + "db_" + ToString(id); }
std::string RandomString(int len) {
std::string r;
test::RandomString(&rnd_, len, &r);
return r;
}
void UpdateUsagesHistory(const std::vector<DB*>& dbs) {
std::map<MemoryUtil::UsageType, uint64_t> usage_by_type;
ASSERT_OK(GetApproximateMemoryUsageByType(dbs, &usage_by_type));
for (int i = 0; i < MemoryUtil::kNumUsageTypes; ++i) {
usage_history_[i].push_back(
usage_by_type[static_cast<MemoryUtil::UsageType>(i)]);
}
}
void GetCachePointersFromTableFactory(
const TableFactory* factory,
std::unordered_set<const Cache*>* cache_set) {
const BlockBasedTableFactory* bbtf =
dynamic_cast<const BlockBasedTableFactory*>(factory);
if (bbtf != nullptr) {
const auto bbt_opts = bbtf->table_options();
cache_set->insert(bbt_opts.block_cache.get());
cache_set->insert(bbt_opts.block_cache_compressed.get());
}
}
void GetCachePointers(const std::vector<DB*>& dbs,
std::unordered_set<const Cache*>* cache_set) {
cache_set->clear();
for (auto* db : dbs) {
// Cache from DBImpl
StackableDB* sdb = dynamic_cast<StackableDB*>(db);
DBImpl* db_impl = dynamic_cast<DBImpl*>(sdb ? sdb->GetBaseDB() : db);
if (db_impl != nullptr) {
cache_set->insert(db_impl->TEST_table_cache());
}
// Cache from DBOptions
cache_set->insert(db->GetDBOptions().row_cache.get());
// Cache from table factories
std::unordered_map<std::string, const ImmutableCFOptions*> iopts_map;
if (db_impl != nullptr) {
ASSERT_OK(db_impl->TEST_GetAllImmutableCFOptions(&iopts_map));
}
for (auto pair : iopts_map) {
GetCachePointersFromTableFactory(pair.second->table_factory, cache_set);
}
}
}
Status GetApproximateMemoryUsageByType(
const std::vector<DB*>& dbs,
std::map<MemoryUtil::UsageType, uint64_t>* usage_by_type) {
std::unordered_set<const Cache*> cache_set;
GetCachePointers(dbs, &cache_set);
return MemoryUtil::GetApproximateMemoryUsageByType(dbs, cache_set,
usage_by_type);
}
const std::string kDbDir;
Random rnd_;
std::vector<uint64_t> usage_history_[MemoryUtil::kNumUsageTypes];
};
TEST_F(MemoryTest, SharedBlockCacheTotal) {
std::vector<DB*> dbs;
std::vector<uint64_t> usage_by_type;
const int kNumDBs = 10;
const int kKeySize = 100;
const int kValueSize = 500;
Options opt;
opt.create_if_missing = true;
opt.write_buffer_size = kKeySize + kValueSize;
opt.max_write_buffer_number = 10;
opt.min_write_buffer_number_to_merge = 10;
opt.disable_auto_compactions = true;
BlockBasedTableOptions bbt_opts;
bbt_opts.block_cache = NewLRUCache(4096 * 1000 * 10);
for (int i = 0; i < kNumDBs; ++i) {
DestroyDB(GetDBName(i), opt);
DB* db = nullptr;
ASSERT_OK(DB::Open(opt, GetDBName(i), &db));
dbs.push_back(db);
}
std::vector<std::string> keys_by_db[kNumDBs];
// Fill one memtable per Put to make memtable use more memory.
for (int p = 0; p < opt.min_write_buffer_number_to_merge / 2; ++p) {
for (int i = 0; i < kNumDBs; ++i) {
for (int j = 0; j < 100; ++j) {
keys_by_db[i].emplace_back(RandomString(kKeySize));
dbs[i]->Put(WriteOptions(), keys_by_db[i].back(),
RandomString(kValueSize));
}
dbs[i]->Flush(FlushOptions());
}
}
for (int i = 0; i < kNumDBs; ++i) {
for (auto& key : keys_by_db[i]) {
std::string value;
dbs[i]->Get(ReadOptions(), key, &value);
}
UpdateUsagesHistory(dbs);
}
for (size_t i = 1; i < usage_history_[MemoryUtil::kMemTableTotal].size();
++i) {
// Expect EQ as we didn't flush more memtables.
ASSERT_EQ(usage_history_[MemoryUtil::kTableReadersTotal][i],
usage_history_[MemoryUtil::kTableReadersTotal][i - 1]);
}
for (int i = 0; i < kNumDBs; ++i) {
delete dbs[i];
}
}
TEST_F(MemoryTest, MemTableAndTableReadersTotal) {
std::vector<DB*> dbs;
std::vector<uint64_t> usage_by_type;
std::vector<std::vector<ColumnFamilyHandle*>> vec_handles;
const int kNumDBs = 10;
const int kKeySize = 100;
const int kValueSize = 500;
Options opt;
opt.create_if_missing = true;
opt.create_missing_column_families = true;
opt.write_buffer_size = kKeySize + kValueSize;
opt.max_write_buffer_number = 10;
opt.min_write_buffer_number_to_merge = 10;
opt.disable_auto_compactions = true;
std::vector<ColumnFamilyDescriptor> cf_descs = {
{kDefaultColumnFamilyName, ColumnFamilyOptions(opt)},
{"one", ColumnFamilyOptions(opt)},
{"two", ColumnFamilyOptions(opt)},
};
for (int i = 0; i < kNumDBs; ++i) {
DestroyDB(GetDBName(i), opt);
std::vector<ColumnFamilyHandle*> handles;
dbs.emplace_back();
vec_handles.emplace_back();
ASSERT_OK(DB::Open(DBOptions(opt), GetDBName(i), cf_descs,
&vec_handles.back(), &dbs.back()));
}
// Fill one memtable per Put to make memtable use more memory.
for (int p = 0; p < opt.min_write_buffer_number_to_merge / 2; ++p) {
for (int i = 0; i < kNumDBs; ++i) {
for (auto* handle : vec_handles[i]) {
dbs[i]->Put(WriteOptions(), handle, RandomString(kKeySize),
RandomString(kValueSize));
UpdateUsagesHistory(dbs);
}
}
}
// Expect the usage history is monotonically increasing
for (size_t i = 1; i < usage_history_[MemoryUtil::kMemTableTotal].size();
++i) {
ASSERT_GT(usage_history_[MemoryUtil::kMemTableTotal][i],
usage_history_[MemoryUtil::kMemTableTotal][i - 1]);
ASSERT_GT(usage_history_[MemoryUtil::kMemTableUnFlushed][i],
usage_history_[MemoryUtil::kMemTableUnFlushed][i - 1]);
ASSERT_EQ(usage_history_[MemoryUtil::kTableReadersTotal][i],
usage_history_[MemoryUtil::kTableReadersTotal][i - 1]);
}
size_t usage_check_point = usage_history_[MemoryUtil::kMemTableTotal].size();
std::vector<Iterator*> iters;
// Create an iterator and flush all memtables for each db
for (int i = 0; i < kNumDBs; ++i) {
iters.push_back(dbs[i]->NewIterator(ReadOptions()));
dbs[i]->Flush(FlushOptions());
for (int j = 0; j < 100; ++j) {
std::string value;
dbs[i]->Get(ReadOptions(), RandomString(kKeySize), &value);
}
UpdateUsagesHistory(dbs);
}
for (size_t i = usage_check_point;
i < usage_history_[MemoryUtil::kMemTableTotal].size(); ++i) {
// Since memtables are pinned by iterators, we don't expect the
// memory usage of all the memtables decreases as they are pinned
// by iterators.
ASSERT_GE(usage_history_[MemoryUtil::kMemTableTotal][i],
usage_history_[MemoryUtil::kMemTableTotal][i - 1]);
// Expect the usage history from the "usage_decay_point" is
// monotonically decreasing.
ASSERT_LT(usage_history_[MemoryUtil::kMemTableUnFlushed][i],
usage_history_[MemoryUtil::kMemTableUnFlushed][i - 1]);
// Expect the usage history of the table readers increases
// as we flush tables.
ASSERT_GT(usage_history_[MemoryUtil::kTableReadersTotal][i],
usage_history_[MemoryUtil::kTableReadersTotal][i - 1]);
ASSERT_GT(usage_history_[MemoryUtil::kCacheTotal][i],
usage_history_[MemoryUtil::kCacheTotal][i - 1]);
}
usage_check_point = usage_history_[MemoryUtil::kMemTableTotal].size();
for (int i = 0; i < kNumDBs; ++i) {
delete iters[i];
UpdateUsagesHistory(dbs);
}
for (size_t i = usage_check_point;
i < usage_history_[MemoryUtil::kMemTableTotal].size(); ++i) {
// Expect the usage of all memtables decreasing as we delete iterators.
ASSERT_LT(usage_history_[MemoryUtil::kMemTableTotal][i],
usage_history_[MemoryUtil::kMemTableTotal][i - 1]);
// Since the memory usage of un-flushed memtables is only affected
// by Put and flush, we expect EQ here as we only delete iterators.
ASSERT_EQ(usage_history_[MemoryUtil::kMemTableUnFlushed][i],
usage_history_[MemoryUtil::kMemTableUnFlushed][i - 1]);
// Expect EQ as we didn't flush more memtables.
ASSERT_EQ(usage_history_[MemoryUtil::kTableReadersTotal][i],
usage_history_[MemoryUtil::kTableReadersTotal][i - 1]);
}
for (int i = 0; i < kNumDBs; ++i) {
for (auto* handle : vec_handles[i]) {
delete handle;
}
delete dbs[i];
}
}
} // namespace rocksdb
int main(int argc, char** argv) {
#if !(defined NDEBUG) || !defined(OS_WIN)
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
#else
return 0;
#endif
}
#else
#include <cstdio>
int main(int /*argc*/, char** /*argv*/) {
printf("Skipped in RocksDBLite as utilities are not supported.\n");
return 0;
}
#endif // !ROCKSDB_LITE