|
|
|
// Copyright (c) 2013, 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 "utilities/persistent_cache/volatile_tier_impl.h"
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
|
|
|
|
void VolatileCacheTier::DeleteCacheData(VolatileCacheTier::CacheData* data) {
|
|
|
|
assert(data);
|
|
|
|
delete data;
|
|
|
|
}
|
|
|
|
|
|
|
|
VolatileCacheTier::~VolatileCacheTier() { index_.Clear(&DeleteCacheData); }
|
|
|
|
|
|
|
|
PersistentCache::StatsType VolatileCacheTier::Stats() {
|
|
|
|
std::map<std::string, double> stat;
|
|
|
|
stat.insert({"persistent_cache.volatile_cache.hits",
|
|
|
|
static_cast<double>(stats_.cache_hits_)});
|
|
|
|
stat.insert({"persistent_cache.volatile_cache.misses",
|
|
|
|
static_cast<double>(stats_.cache_misses_)});
|
|
|
|
stat.insert({"persistent_cache.volatile_cache.inserts",
|
|
|
|
static_cast<double>(stats_.cache_inserts_)});
|
|
|
|
stat.insert({"persistent_cache.volatile_cache.evicts",
|
|
|
|
static_cast<double>(stats_.cache_evicts_)});
|
|
|
|
stat.insert({"persistent_cache.volatile_cache.hit_pct",
|
|
|
|
static_cast<double>(stats_.CacheHitPct())});
|
|
|
|
stat.insert({"persistent_cache.volatile_cache.miss_pct",
|
|
|
|
static_cast<double>(stats_.CacheMissPct())});
|
|
|
|
|
|
|
|
auto out = PersistentCacheTier::Stats();
|
|
|
|
out.push_back(stat);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status VolatileCacheTier::Insert(const Slice& page_key, const char* data,
|
|
|
|
const size_t size) {
|
|
|
|
// precondition
|
|
|
|
assert(data);
|
|
|
|
assert(size);
|
|
|
|
|
|
|
|
// increment the size
|
|
|
|
size_ += size;
|
|
|
|
|
|
|
|
// check if we have overshot the limit, if so evict some space
|
|
|
|
while (size_ > max_size_) {
|
|
|
|
if (!Evict()) {
|
|
|
|
// unable to evict data, we give up so we don't spike read
|
|
|
|
// latency
|
|
|
|
assert(size_ >= size);
|
|
|
|
size_ -= size;
|
|
|
|
return Status::TryAgain("Unable to evict any data");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(size_ >= size);
|
|
|
|
|
|
|
|
// insert order: LRU, followed by index
|
|
|
|
std::string key(page_key.data(), page_key.size());
|
|
|
|
std::string value(data, size);
|
|
|
|
std::unique_ptr<CacheData> cache_data(
|
|
|
|
new CacheData(std::move(key), std::move(value)));
|
|
|
|
bool ok = index_.Insert(cache_data.get());
|
|
|
|
if (!ok) {
|
|
|
|
// decrement the size that we incremented ahead of time
|
|
|
|
assert(size_ >= size);
|
|
|
|
size_ -= size;
|
|
|
|
// failed to insert to cache, block already in cache
|
|
|
|
return Status::TryAgain("key already exists in volatile cache");
|
|
|
|
}
|
|
|
|
|
|
|
|
cache_data.release();
|
|
|
|
stats_.cache_inserts_++;
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
Status VolatileCacheTier::Lookup(const Slice& page_key,
|
|
|
|
std::unique_ptr<char[]>* result,
|
|
|
|
size_t* size) {
|
|
|
|
CacheData key(std::move(page_key.ToString()));
|
|
|
|
CacheData* kv;
|
|
|
|
bool ok = index_.Find(&key, &kv);
|
|
|
|
if (ok) {
|
|
|
|
// set return data
|
|
|
|
result->reset(new char[kv->value.size()]);
|
|
|
|
memcpy(result->get(), kv->value.c_str(), kv->value.size());
|
|
|
|
*size = kv->value.size();
|
|
|
|
// drop the reference on cache data
|
|
|
|
kv->refs_--;
|
|
|
|
// update stats
|
|
|
|
stats_.cache_hits_++;
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
stats_.cache_misses_++;
|
|
|
|
|
|
|
|
if (next_tier()) {
|
|
|
|
return next_tier()->Lookup(page_key, result, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status::NotFound("key not found in volatile cache");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VolatileCacheTier::Erase(const Slice& /*key*/) {
|
|
|
|
assert(!"not supported");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VolatileCacheTier::Evict() {
|
|
|
|
CacheData* edata = index_.Evict();
|
|
|
|
if (!edata) {
|
|
|
|
// not able to evict any object
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
stats_.cache_evicts_++;
|
|
|
|
|
|
|
|
// push the evicted object to the next level
|
|
|
|
if (next_tier()) {
|
|
|
|
next_tier()->Insert(Slice(edata->key), edata->value.c_str(),
|
|
|
|
edata->value.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjust size and destroy data
|
|
|
|
size_ -= edata->value.size();
|
|
|
|
delete edata;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace rocksdb
|
|
|
|
|
|
|
|
#endif
|