// 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 namespace ROCKSDB_NAMESPACE { void VolatileCacheTier::DeleteCacheData(VolatileCacheTier::CacheData* data) { assert(data); delete data; } VolatileCacheTier::~VolatileCacheTier() { index_.Clear(&DeleteCacheData); } PersistentCache::StatsType VolatileCacheTier::Stats() { std::map stat; stat.insert({"persistent_cache.volatile_cache.hits", static_cast(stats_.cache_hits_)}); stat.insert({"persistent_cache.volatile_cache.misses", static_cast(stats_.cache_misses_)}); stat.insert({"persistent_cache.volatile_cache.inserts", static_cast(stats_.cache_inserts_)}); stat.insert({"persistent_cache.volatile_cache.evicts", static_cast(stats_.cache_evicts_)}); stat.insert({"persistent_cache.volatile_cache.hit_pct", static_cast(stats_.CacheHitPct())}); stat.insert({"persistent_cache.volatile_cache.miss_pct", static_cast(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 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* 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_NAMESPACE #endif