blob: 80c125c50d21eab1adeaa130869980b9a4d7cbca [file]
// Some portions Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "util/cache/cache.h"
#include <memory>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "kudu/util/coding.h"
#include "kudu/util/faststring.h"
#include "kudu/util/mem_tracker.h"
#include "kudu/util/slice.h"
namespace impala {
// Conversions between numeric keys/values and the types expected by Cache.
static std::string EncodeInt(int k) {
kudu::faststring result;
kudu::PutFixed32(&result, k);
return result.ToString();
}
static int DecodeInt(const Slice& k) {
CHECK_EQ(4, k.size());
return kudu::DecodeFixed32(k.data());
}
// Cache sharding policy affects the composition of the cache. Some test
// scenarios assume cache is single-sharded to keep the logic simpler.
enum class ShardingPolicy {
MultiShard,
SingleShard,
};
// CacheBaseTest provides a base test class for testing a variety of different cache
// eviction policies (LRU, LIRS, FIFO). It wraps the common functions to simplify the
// calls for tests and to use integer keys and values, so that tests can avoid using
// Slice types directly. It also maintains a record of all evictions that take place,
// both the keys and the values.
// NOTE: The structures are not synchronized, so this is only useful for single-threaded
// tests.
class CacheBaseTest : public ::testing::Test,
public Cache::EvictionCallback {
public:
explicit CacheBaseTest(size_t cache_size)
: cache_size_(cache_size) {
}
size_t cache_size() const {
return cache_size_;
}
// Implementation of the EvictionCallback interface. This maintains a record of all
// evictions, keys and values.
void EvictedEntry(Slice key, Slice val) override {
evicted_keys_.push_back(DecodeInt(key));
evicted_values_.push_back(DecodeInt(val));
}
// Lookup the key. Return the value on success or -1 on failure.
int Lookup(int key, Cache::LookupBehavior behavior = Cache::NORMAL) {
auto handle(cache_->Lookup(EncodeInt(key), behavior));
return handle ? DecodeInt(cache_->Value(handle)) : -1;
}
void UpdateCharge(int key, int charge) {
auto handle(cache_->Lookup(EncodeInt(key), Cache::NO_UPDATE));
cache_->UpdateCharge(handle, charge);
}
// Insert a key with the give value and charge. Return whether the insert was
// successful.
bool Insert(int key, int value, int charge = 1) {
std::string key_str = EncodeInt(key);
std::string val_str = EncodeInt(value);
auto handle(cache_->Allocate(key_str, val_str.size(), charge));
// This can be null if Allocate rejects something with this charge.
if (handle == nullptr) return false;
memcpy(cache_->MutableValue(&handle), val_str.data(), val_str.size());
auto inserted_handle(cache_->Insert(std::move(handle), this));
// Insert can fail and return nullptr
if (inserted_handle == nullptr) return false;
return true;
}
void Erase(int key) {
cache_->Erase(EncodeInt(key));
}
protected:
// This initializes the cache with the specified 'eviction_policy' and
// 'sharding_policy'. Child classes frequently call this from the SetUp() method.
// For example, there are shared tests that apply to all eviction algorithms. This
// function is used during SetUp() to allow those shared tests to be parameterized to
// use various combinations of eviction policy and sharding policy.
void SetupWithParameters(Cache::EvictionPolicy eviction_policy,
ShardingPolicy sharding_policy);
const size_t cache_size_;
// Record of all evicted keys/values. When an entry is evicted, its key is put in
// evicted_keys_ and its value is put in evicted_values_. These are inserted in
// the order they are evicted (i.e. index 0 happened before index 1).
std::vector<int> evicted_keys_;
std::vector<int> evicted_values_;
std::shared_ptr<kudu::MemTracker> mem_tracker_;
std::unique_ptr<Cache> cache_;
};
} // namespace impala