xtreemfs/cpp/test/libxtreemfs/metadata_cache_test.cpp
2020-09-22 02:25:22 +02:00

336 lines
10 KiB
C++

/*
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <stdint.h>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <string>
#include "libxtreemfs/metadata_cache.h"
#include "libxtreemfs/helper.h"
#include "util/logging.h"
#include "xtreemfs/MRC.pb.h"
using namespace std;
using namespace xtreemfs;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
class MetadataCacheTestSize2 : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
metadata_cache_ = new MetadataCache(2, 3600); // Max 2 entries, 1 hour.
}
virtual void TearDown() {
delete metadata_cache_;
google::protobuf::ShutdownProtobufLibrary();
shutdown_logger();
}
MetadataCache* metadata_cache_;
};
class MetadataCacheTestSize1024 : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
metadata_cache_ = new MetadataCache(1024, 3600); // Max 1k entries, 1 hour.
}
virtual void TearDown() {
delete metadata_cache_;
google::protobuf::ShutdownProtobufLibrary();
shutdown_logger();
}
MetadataCache* metadata_cache_;
};
/** If a Stat entry gets updated through UpdateStatTime(), the new timeout must
* be respected in case of an eviction. */
TEST_F(MetadataCacheTestSize2, UpdateStatTimeKeepsSequentialTimeoutOrder) {
Stat a, b, c;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
metadata_cache_->UpdateStat("/a", a);
metadata_cache_->UpdateStat("/b", b);
// Cache is full now. a would be first item to get evicted.
metadata_cache_->UpdateStatTime("/a", 0, SETATTR_MTIME);
// "b" should be the oldest Stat element now and get evicted.
metadata_cache_->UpdateStat("/c", c);
// Was "a" found or did "b" survive?
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/a", &a));
EXPECT_EQ(0, a.ino());
// "c" is also still there.
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/c", &c));
EXPECT_EQ(2, c.ino());
}
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
* respected in case of an eviction. */
TEST_F(MetadataCacheTestSize2, UpdateStatKeepsSequentialTimeoutOrder) {
Stat a, b, c;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
metadata_cache_->UpdateStat("/a", a);
metadata_cache_->UpdateStat("/b", b);
// Cache is full now. a would be first item to get evicted.
metadata_cache_->UpdateStat("/a", a);
// "b" should be the oldest Stat element now and get evicted.
metadata_cache_->UpdateStat("/c", c);
// Was "a" found or did "b" survive?
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/a", &a));
EXPECT_EQ(0, a.ino());
// "c" is also still there.
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/c", &c));
EXPECT_EQ(2, c.ino());
}
/** Test if Size is updated correctly after UpdateStat() or Invalidate(). */
TEST_F(MetadataCacheTestSize2, CheckSizeAfterUpdateAndInvalidate) {
Stat a, b, c;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
EXPECT_EQ(0, metadata_cache_->Size());
metadata_cache_->UpdateStat("/a", a);
EXPECT_EQ(1, metadata_cache_->Size());
metadata_cache_->UpdateStat("/b", b);
EXPECT_EQ(2, metadata_cache_->Size());
metadata_cache_->UpdateStat("/c", b);
// Cache has only room for two entries.
EXPECT_EQ(2, metadata_cache_->Size());
metadata_cache_->Invalidate("/b");
EXPECT_EQ(1, metadata_cache_->Size());
metadata_cache_->Invalidate("/c");
EXPECT_EQ(0, metadata_cache_->Size());
}
/** Test (Get|Update)DirEntries methods. */
TEST_F(MetadataCacheTestSize2, UpdateAndGetDirEntries) {
DirectoryEntries dir_entries;
int chunk_size = 1024;
int entry_count = chunk_size;
string dir = "/";
// Fill dir_entries;
for (int i = 0; i < entry_count; i++) {
Stat a;
a.set_ino(i);
dir_entries.add_entries();
dir_entries.mutable_entries(dir_entries.entries_size()-1)
->mutable_stbuf()->CopyFrom(a);
string path_to_stat = dir + boost::lexical_cast<std::string>(i);
dir_entries.mutable_entries(dir_entries.entries_size()-1)
->set_name(path_to_stat);
}
metadata_cache_->UpdateDirEntries(dir, dir_entries);
boost::scoped_ptr<DirectoryEntries> dir_entries_read;
// Read all entries.
dir_entries_read.reset(metadata_cache_->GetDirEntries(dir, 0, entry_count));
EXPECT_EQ(entry_count, dir_entries_read->entries_size());
for (int i = 0; i < dir_entries_read->entries_size(); i++) {
string path_to_stat = dir + boost::lexical_cast<std::string>(i);
EXPECT_EQ(i, dir_entries_read->entries(i).stbuf().ino());
EXPECT_EQ(path_to_stat, dir_entries_read->entries(i).name());
}
// Read a subset.
dir_entries_read.reset(metadata_cache_
->GetDirEntries(dir, entry_count/2, entry_count/2-1));
EXPECT_EQ(entry_count/2-1, dir_entries_read->entries_size());
int offset = entry_count/2;
for (int i = 0; i < dir_entries_read->entries_size(); i++) {
string path_to_stat = dir + boost::lexical_cast<std::string>(offset);
EXPECT_EQ(path_to_stat, dir_entries_read->entries(i).name());
EXPECT_EQ(offset, dir_entries_read->entries(i).stbuf().ino());
offset++;
}
}
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
* respected in case of an eviction. */
TEST_F(MetadataCacheTestSize1024, InvalidatePrefix) {
Stat a, b, c, d;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
InitializeStat(&d);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
d.set_ino(3);
metadata_cache_->UpdateStat("/dir", a);
metadata_cache_->UpdateStat("/dir/file1", b);
metadata_cache_->UpdateStat("/dir.file1", c);
metadata_cache_->UpdateStat("/dirZfile1", d);
metadata_cache_->InvalidatePrefix("/dir");
// Invalidate of all matching entries successful?
EXPECT_EQ(MetadataCache::kStatNotCached,
metadata_cache_->GetStat("/dir", &a));
EXPECT_EQ(MetadataCache::kStatNotCached,
metadata_cache_->GetStat("/dir/file1", &b));
// Similiar entries which do not match the prefix "/dir/" have not been
// invalidated.
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dir.file1", &c));
EXPECT_EQ(2, c.ino());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dirZfile1", &d));
EXPECT_EQ(3, d.ino());
}
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
* respected in case of an eviction. */
TEST_F(MetadataCacheTestSize1024, RenamePrefix) {
Stat a, b, c, d;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
InitializeStat(&d);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
d.set_ino(3);
metadata_cache_->UpdateStat("/dir", a);
metadata_cache_->UpdateStat("/dir/file1", b);
metadata_cache_->UpdateStat("/dir.file1", c);
metadata_cache_->UpdateStat("/dirZfile1", d);
EXPECT_EQ(4, metadata_cache_->Size());
metadata_cache_->RenamePrefix("/dir", "/newdir");
EXPECT_EQ(4, metadata_cache_->Size());
// Rename of all matching entries successful?
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/newdir", &a));
EXPECT_EQ(0, a.ino());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/newdir/file1", &b));
EXPECT_EQ(1, b.ino());
// Similiar entries which do not match the prefix "/dir/" have not been
// renamed.
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dir.file1", &c));
EXPECT_EQ(2, c.ino());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dirZfile1", &d));
EXPECT_EQ(3, d.ino());
}
/** Are large nanoseconds values correctly updated by
* UpdateStatAttributes? */
TEST_F(MetadataCacheTestSize1024, UpdateStatAttributes) {
string path = "/file";
Stat stat, update_stat;
InitializeStat(&stat);
InitializeStat(&update_stat);
stat.set_ino(0);
update_stat.set_ino(1);
metadata_cache_->UpdateStat(path, stat);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat(path, &stat));
EXPECT_EQ(0, stat.ino());
EXPECT_EQ(0, stat.mtime_ns());
uint64_t time = 1234567890;
time *= 1000000000;
update_stat.set_atime_ns(time);
update_stat.set_mtime_ns(time);
metadata_cache_->UpdateStatAttributes(
path,
update_stat,
static_cast<Setattrs>(SETATTR_ATIME | SETATTR_MTIME));
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat(path, &stat));
EXPECT_EQ(0, stat.ino());
EXPECT_EQ(time, stat.atime_ns());
EXPECT_EQ(time, stat.mtime_ns());
}
/** Changing the file access mode may only modify the last 12 bits (3 bits for
* sticky bit, set GID and set UID and 3 * 3 bits for the file access mode). */
TEST_F(MetadataCacheTestSize1024, UpdateStatAttributesPreservesModeBits) {
string path = "/file";
Stat stat, cached_stat;
InitializeStat(&stat);
stat.set_ino(0);
stat.set_mode(33188); // Octal: 100644 (regular file + 644).
metadata_cache_->UpdateStat(path, stat);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(33188, cached_stat.mode());
stat.set_mode(420); // Octal: 644.
metadata_cache_->UpdateStatAttributes(path, stat, SETATTR_MODE);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(33188, cached_stat.mode());
stat.set_mode(263076); // Octal: 1001644 (regular file + sticky bit + 644).
metadata_cache_->UpdateStat(path, stat);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(263076, cached_stat.mode());
stat.set_mode(511); // Octal: 0777 (no sticky bit + 777).
metadata_cache_->UpdateStatAttributes(path, stat, SETATTR_MODE);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(262655, cached_stat.mode()); // Octal: 1000777.
}
/** Ideas:
*
* test TTL expiration.
* test invalidateprefix.
*
* test correct setting of maximum ttl for every operation
*
*/