CacheActivityLogger, component to log cache activity into a file
Summary: Simple component that will add a new entry in a log file every time we lookup/insert a key in SimCache. API: ``` SimCache::StartActivityLogging(<file_name>, <env>, <optional_max_size>) SimCache::StopActivityLogging() ``` Sending for review, Still need to add more comments. I was thinking about a better approach, but I ended up deciding I will use a mutex to sync the writes to the file, since this feature should not be heavily used and only used to collect info that will be analyzed offline. I think it's okay to hold the mutex every time we lookup/add to the SimCache. Closes https://github.com/facebook/rocksdb/pull/2295 Differential Revision: D5063826 Pulled By: IslamAbdelRahman fbshipit-source-id: f3b5daed8b201987c9a071146ddd5c5740a2dd8c
This commit is contained in:
parent
6083bc79f8
commit
50a969131f
@ -727,6 +727,7 @@ set(TESTS
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/redis/redis_lists_test.cc
|
||||
utilities/spatialdb/spatial_db_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
@ -67,6 +68,19 @@ class SimCache : public Cache {
|
||||
// String representation of the statistics of the simcache
|
||||
virtual std::string ToString() const = 0;
|
||||
|
||||
// Start storing logs of the cache activity (Add/Lookup) into
|
||||
// a file located at activity_log_file, max_logging_size option can be used to
|
||||
// stop logging to the file automatically after reaching a specific size in
|
||||
// bytes, a values of 0 disable this feature
|
||||
virtual Status StartActivityLogging(const std::string& activity_log_file,
|
||||
Env* env, uint64_t max_logging_size = 0) = 0;
|
||||
|
||||
// Stop cache activity logging if any
|
||||
virtual void StopActivityLogging() = 0;
|
||||
|
||||
// Status of cache logging happening in background
|
||||
virtual Status GetActivityLoggingStatus() = 0;
|
||||
|
||||
private:
|
||||
SimCache(const SimCache&);
|
||||
SimCache& operator=(const SimCache&);
|
||||
|
@ -7,10 +7,144 @@
|
||||
#include <atomic>
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
|
||||
class CacheActivityLogger {
|
||||
public:
|
||||
CacheActivityLogger()
|
||||
: activity_logging_enabled_(false), max_logging_size_(0) {}
|
||||
|
||||
~CacheActivityLogger() {
|
||||
MutexLock l(&mutex_);
|
||||
|
||||
StopLoggingInternal();
|
||||
}
|
||||
|
||||
Status StartLogging(const std::string& activity_log_file, Env* env,
|
||||
uint64_t max_logging_size = 0) {
|
||||
assert(activity_log_file != "");
|
||||
assert(env != nullptr);
|
||||
|
||||
Status status;
|
||||
EnvOptions env_opts;
|
||||
std::unique_ptr<WritableFile> log_file;
|
||||
|
||||
MutexLock l(&mutex_);
|
||||
|
||||
// Stop existing logging if any
|
||||
StopLoggingInternal();
|
||||
|
||||
// Open log file
|
||||
status = env->NewWritableFile(activity_log_file, &log_file, env_opts);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
file_writer_.reset(new WritableFileWriter(std::move(log_file), env_opts));
|
||||
|
||||
max_logging_size_ = max_logging_size;
|
||||
activity_logging_enabled_.store(true);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void StopLogging() {
|
||||
MutexLock l(&mutex_);
|
||||
|
||||
StopLoggingInternal();
|
||||
}
|
||||
|
||||
void ReportLookup(const Slice& key) {
|
||||
if (activity_logging_enabled_.load() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string log_line = "LOOKUP - " + key.ToString(true) + "\n";
|
||||
|
||||
// line format: "LOOKUP - <KEY>"
|
||||
MutexLock l(&mutex_);
|
||||
Status s = file_writer_->Append(log_line);
|
||||
if (!s.ok() && bg_status_.ok()) {
|
||||
bg_status_ = s;
|
||||
}
|
||||
if (MaxLoggingSizeReached() || !bg_status_.ok()) {
|
||||
// Stop logging if we have reached the max file size or
|
||||
// encountered an error
|
||||
StopLoggingInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void ReportAdd(const Slice& key, size_t size) {
|
||||
if (activity_logging_enabled_.load() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string log_line = "ADD - ";
|
||||
log_line += key.ToString(true);
|
||||
log_line += " - ";
|
||||
AppendNumberTo(&log_line, size);
|
||||
log_line += "\n";
|
||||
|
||||
// line format: "ADD - <KEY> - <KEY-SIZE>"
|
||||
MutexLock l(&mutex_);
|
||||
Status s = file_writer_->Append(log_line);
|
||||
if (!s.ok() && bg_status_.ok()) {
|
||||
bg_status_ = s;
|
||||
}
|
||||
|
||||
if (MaxLoggingSizeReached() || !bg_status_.ok()) {
|
||||
// Stop logging if we have reached the max file size or
|
||||
// encountered an error
|
||||
StopLoggingInternal();
|
||||
}
|
||||
}
|
||||
|
||||
Status& bg_status() {
|
||||
MutexLock l(&mutex_);
|
||||
return bg_status_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool MaxLoggingSizeReached() {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
return (max_logging_size_ > 0 &&
|
||||
file_writer_->GetFileSize() >= max_logging_size_);
|
||||
}
|
||||
|
||||
void StopLoggingInternal() {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
if (!activity_logging_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity_logging_enabled_.store(false);
|
||||
Status s = file_writer_->Close();
|
||||
if (!s.ok() && bg_status_.ok()) {
|
||||
bg_status_ = s;
|
||||
}
|
||||
}
|
||||
|
||||
// Mutex to sync writes to file_writer, and all following
|
||||
// class data members
|
||||
port::Mutex mutex_;
|
||||
// Indicates if logging is currently enabled
|
||||
// atomic to allow reads without mutex
|
||||
std::atomic<bool> activity_logging_enabled_;
|
||||
// When reached, we will stop logging and close the file
|
||||
// Value of 0 means unlimited
|
||||
uint64_t max_logging_size_;
|
||||
std::unique_ptr<WritableFileWriter> file_writer_;
|
||||
Status bg_status_;
|
||||
};
|
||||
|
||||
// SimCacheImpl definition
|
||||
class SimCacheImpl : public SimCache {
|
||||
public:
|
||||
@ -48,6 +182,9 @@ class SimCacheImpl : public SimCache {
|
||||
} else {
|
||||
key_only_cache_->Release(h);
|
||||
}
|
||||
|
||||
cache_activity_logger_.ReportAdd(key, charge);
|
||||
|
||||
return cache_->Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
@ -61,6 +198,9 @@ class SimCacheImpl : public SimCache {
|
||||
inc_miss_counter();
|
||||
RecordTick(stats, SIM_BLOCK_CACHE_MISS);
|
||||
}
|
||||
|
||||
cache_activity_logger_.ReportLookup(key);
|
||||
|
||||
return cache_->Lookup(key, stats);
|
||||
}
|
||||
|
||||
@ -158,12 +298,29 @@ class SimCacheImpl : public SimCache {
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual Status StartActivityLogging(const std::string& activity_log_file,
|
||||
Env* env,
|
||||
uint64_t max_logging_size = 0) override {
|
||||
return cache_activity_logger_.StartLogging(activity_log_file, env,
|
||||
max_logging_size);
|
||||
}
|
||||
|
||||
virtual void StopActivityLogging() override {
|
||||
cache_activity_logger_.StopLogging();
|
||||
}
|
||||
|
||||
virtual Status GetActivityLoggingStatus() override {
|
||||
return cache_activity_logger_.bg_status();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<Cache> key_only_cache_;
|
||||
std::atomic<uint64_t> miss_times_;
|
||||
std::atomic<uint64_t> hit_times_;
|
||||
Statistics* stats_;
|
||||
CacheActivityLogger cache_activity_logger_;
|
||||
|
||||
void inc_miss_counter() {
|
||||
miss_times_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
@ -138,6 +138,77 @@ TEST_F(SimCacheTest, SimCache) {
|
||||
ASSERT_EQ(6, simCache->get_hit_counter());
|
||||
}
|
||||
|
||||
TEST_F(SimCacheTest, SimCacheLogging) {
|
||||
auto table_options = GetTableOptions();
|
||||
auto options = GetOptions(table_options);
|
||||
options.disable_auto_compactions = true;
|
||||
std::shared_ptr<SimCache> sim_cache =
|
||||
NewSimCache(NewLRUCache(1024 * 1024), 20000, 0);
|
||||
table_options.block_cache = sim_cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
int num_block_entries = 20;
|
||||
for (int i = 0; i < num_block_entries; i++) {
|
||||
Put(Key(i), "val");
|
||||
Flush();
|
||||
}
|
||||
|
||||
std::string log_file = test::TmpDir(env_) + "/cache_log.txt";
|
||||
ASSERT_OK(sim_cache->StartActivityLogging(log_file, env_));
|
||||
for (int i = 0; i < num_block_entries; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), "val");
|
||||
}
|
||||
for (int i = 0; i < num_block_entries; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), "val");
|
||||
}
|
||||
sim_cache->StopActivityLogging();
|
||||
ASSERT_OK(sim_cache->GetActivityLoggingStatus());
|
||||
|
||||
std::string file_contents = "";
|
||||
ReadFileToString(env_, log_file, &file_contents);
|
||||
|
||||
int lookup_num = 0;
|
||||
int add_num = 0;
|
||||
std::string::size_type pos;
|
||||
|
||||
// count number of lookups
|
||||
pos = 0;
|
||||
while ((pos = file_contents.find("LOOKUP -", pos)) != std::string::npos) {
|
||||
++lookup_num;
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
// count number of additions
|
||||
pos = 0;
|
||||
while ((pos = file_contents.find("ADD -", pos)) != std::string::npos) {
|
||||
++add_num;
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
// We asked for every block twice
|
||||
ASSERT_EQ(lookup_num, num_block_entries * 2);
|
||||
|
||||
// We added every block only once, since the cache can hold all blocks
|
||||
ASSERT_EQ(add_num, num_block_entries);
|
||||
|
||||
// Log things again but stop logging automatically after reaching 512 bytes
|
||||
int max_size = 512;
|
||||
ASSERT_OK(sim_cache->StartActivityLogging(log_file, env_, max_size));
|
||||
for (int it = 0; it < 10; it++) {
|
||||
for (int i = 0; i < num_block_entries; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), "val");
|
||||
}
|
||||
}
|
||||
ASSERT_OK(sim_cache->GetActivityLoggingStatus());
|
||||
|
||||
uint64_t fsize = 0;
|
||||
ASSERT_OK(env_->GetFileSize(log_file, &fsize));
|
||||
// error margin of 100 bytes
|
||||
ASSERT_LT(fsize, max_size + 100);
|
||||
ASSERT_GT(fsize, max_size - 100);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
Loading…
Reference in New Issue
Block a user