1*9507f98cSAndroid Build Coastguard Worker // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2*9507f98cSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*9507f98cSAndroid Build Coastguard Worker // found in the LICENSE file. See the AUTHORS file for names of contributors.
4*9507f98cSAndroid Build Coastguard Worker
5*9507f98cSAndroid Build Coastguard Worker #include "leveldb/cache.h"
6*9507f98cSAndroid Build Coastguard Worker
7*9507f98cSAndroid Build Coastguard Worker #include <vector>
8*9507f98cSAndroid Build Coastguard Worker
9*9507f98cSAndroid Build Coastguard Worker #include "gtest/gtest.h"
10*9507f98cSAndroid Build Coastguard Worker #include "util/coding.h"
11*9507f98cSAndroid Build Coastguard Worker
12*9507f98cSAndroid Build Coastguard Worker namespace leveldb {
13*9507f98cSAndroid Build Coastguard Worker
14*9507f98cSAndroid Build Coastguard Worker // Conversions between numeric keys/values and the types expected by Cache.
EncodeKey(int k)15*9507f98cSAndroid Build Coastguard Worker static std::string EncodeKey(int k) {
16*9507f98cSAndroid Build Coastguard Worker std::string result;
17*9507f98cSAndroid Build Coastguard Worker PutFixed32(&result, k);
18*9507f98cSAndroid Build Coastguard Worker return result;
19*9507f98cSAndroid Build Coastguard Worker }
DecodeKey(const Slice & k)20*9507f98cSAndroid Build Coastguard Worker static int DecodeKey(const Slice& k) {
21*9507f98cSAndroid Build Coastguard Worker assert(k.size() == 4);
22*9507f98cSAndroid Build Coastguard Worker return DecodeFixed32(k.data());
23*9507f98cSAndroid Build Coastguard Worker }
EncodeValue(uintptr_t v)24*9507f98cSAndroid Build Coastguard Worker static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); }
DecodeValue(void * v)25*9507f98cSAndroid Build Coastguard Worker static int DecodeValue(void* v) { return reinterpret_cast<uintptr_t>(v); }
26*9507f98cSAndroid Build Coastguard Worker
27*9507f98cSAndroid Build Coastguard Worker class CacheTest : public testing::Test {
28*9507f98cSAndroid Build Coastguard Worker public:
Deleter(const Slice & key,void * v)29*9507f98cSAndroid Build Coastguard Worker static void Deleter(const Slice& key, void* v) {
30*9507f98cSAndroid Build Coastguard Worker current_->deleted_keys_.push_back(DecodeKey(key));
31*9507f98cSAndroid Build Coastguard Worker current_->deleted_values_.push_back(DecodeValue(v));
32*9507f98cSAndroid Build Coastguard Worker }
33*9507f98cSAndroid Build Coastguard Worker
34*9507f98cSAndroid Build Coastguard Worker static constexpr int kCacheSize = 1000;
35*9507f98cSAndroid Build Coastguard Worker std::vector<int> deleted_keys_;
36*9507f98cSAndroid Build Coastguard Worker std::vector<int> deleted_values_;
37*9507f98cSAndroid Build Coastguard Worker Cache* cache_;
38*9507f98cSAndroid Build Coastguard Worker
CacheTest()39*9507f98cSAndroid Build Coastguard Worker CacheTest() : cache_(NewLRUCache(kCacheSize)) { current_ = this; }
40*9507f98cSAndroid Build Coastguard Worker
~CacheTest()41*9507f98cSAndroid Build Coastguard Worker ~CacheTest() { delete cache_; }
42*9507f98cSAndroid Build Coastguard Worker
Lookup(int key)43*9507f98cSAndroid Build Coastguard Worker int Lookup(int key) {
44*9507f98cSAndroid Build Coastguard Worker Cache::Handle* handle = cache_->Lookup(EncodeKey(key));
45*9507f98cSAndroid Build Coastguard Worker const int r = (handle == nullptr) ? -1 : DecodeValue(cache_->Value(handle));
46*9507f98cSAndroid Build Coastguard Worker if (handle != nullptr) {
47*9507f98cSAndroid Build Coastguard Worker cache_->Release(handle);
48*9507f98cSAndroid Build Coastguard Worker }
49*9507f98cSAndroid Build Coastguard Worker return r;
50*9507f98cSAndroid Build Coastguard Worker }
51*9507f98cSAndroid Build Coastguard Worker
Insert(int key,int value,int charge=1)52*9507f98cSAndroid Build Coastguard Worker void Insert(int key, int value, int charge = 1) {
53*9507f98cSAndroid Build Coastguard Worker cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge,
54*9507f98cSAndroid Build Coastguard Worker &CacheTest::Deleter));
55*9507f98cSAndroid Build Coastguard Worker }
56*9507f98cSAndroid Build Coastguard Worker
InsertAndReturnHandle(int key,int value,int charge=1)57*9507f98cSAndroid Build Coastguard Worker Cache::Handle* InsertAndReturnHandle(int key, int value, int charge = 1) {
58*9507f98cSAndroid Build Coastguard Worker return cache_->Insert(EncodeKey(key), EncodeValue(value), charge,
59*9507f98cSAndroid Build Coastguard Worker &CacheTest::Deleter);
60*9507f98cSAndroid Build Coastguard Worker }
61*9507f98cSAndroid Build Coastguard Worker
Erase(int key)62*9507f98cSAndroid Build Coastguard Worker void Erase(int key) { cache_->Erase(EncodeKey(key)); }
63*9507f98cSAndroid Build Coastguard Worker static CacheTest* current_;
64*9507f98cSAndroid Build Coastguard Worker };
65*9507f98cSAndroid Build Coastguard Worker CacheTest* CacheTest::current_;
66*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,HitAndMiss)67*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, HitAndMiss) {
68*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(100));
69*9507f98cSAndroid Build Coastguard Worker
70*9507f98cSAndroid Build Coastguard Worker Insert(100, 101);
71*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, Lookup(100));
72*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(200));
73*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(300));
74*9507f98cSAndroid Build Coastguard Worker
75*9507f98cSAndroid Build Coastguard Worker Insert(200, 201);
76*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, Lookup(100));
77*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(201, Lookup(200));
78*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(300));
79*9507f98cSAndroid Build Coastguard Worker
80*9507f98cSAndroid Build Coastguard Worker Insert(100, 102);
81*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(102, Lookup(100));
82*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(201, Lookup(200));
83*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(300));
84*9507f98cSAndroid Build Coastguard Worker
85*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(1, deleted_keys_.size());
86*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(100, deleted_keys_[0]);
87*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, deleted_values_[0]);
88*9507f98cSAndroid Build Coastguard Worker }
89*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,Erase)90*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, Erase) {
91*9507f98cSAndroid Build Coastguard Worker Erase(200);
92*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(0, deleted_keys_.size());
93*9507f98cSAndroid Build Coastguard Worker
94*9507f98cSAndroid Build Coastguard Worker Insert(100, 101);
95*9507f98cSAndroid Build Coastguard Worker Insert(200, 201);
96*9507f98cSAndroid Build Coastguard Worker Erase(100);
97*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(100));
98*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(201, Lookup(200));
99*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(1, deleted_keys_.size());
100*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(100, deleted_keys_[0]);
101*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, deleted_values_[0]);
102*9507f98cSAndroid Build Coastguard Worker
103*9507f98cSAndroid Build Coastguard Worker Erase(100);
104*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(100));
105*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(201, Lookup(200));
106*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(1, deleted_keys_.size());
107*9507f98cSAndroid Build Coastguard Worker }
108*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,EntriesArePinned)109*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, EntriesArePinned) {
110*9507f98cSAndroid Build Coastguard Worker Insert(100, 101);
111*9507f98cSAndroid Build Coastguard Worker Cache::Handle* h1 = cache_->Lookup(EncodeKey(100));
112*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, DecodeValue(cache_->Value(h1)));
113*9507f98cSAndroid Build Coastguard Worker
114*9507f98cSAndroid Build Coastguard Worker Insert(100, 102);
115*9507f98cSAndroid Build Coastguard Worker Cache::Handle* h2 = cache_->Lookup(EncodeKey(100));
116*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(102, DecodeValue(cache_->Value(h2)));
117*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(0, deleted_keys_.size());
118*9507f98cSAndroid Build Coastguard Worker
119*9507f98cSAndroid Build Coastguard Worker cache_->Release(h1);
120*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(1, deleted_keys_.size());
121*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(100, deleted_keys_[0]);
122*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, deleted_values_[0]);
123*9507f98cSAndroid Build Coastguard Worker
124*9507f98cSAndroid Build Coastguard Worker Erase(100);
125*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(100));
126*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(1, deleted_keys_.size());
127*9507f98cSAndroid Build Coastguard Worker
128*9507f98cSAndroid Build Coastguard Worker cache_->Release(h2);
129*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(2, deleted_keys_.size());
130*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(100, deleted_keys_[1]);
131*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(102, deleted_values_[1]);
132*9507f98cSAndroid Build Coastguard Worker }
133*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,EvictionPolicy)134*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, EvictionPolicy) {
135*9507f98cSAndroid Build Coastguard Worker Insert(100, 101);
136*9507f98cSAndroid Build Coastguard Worker Insert(200, 201);
137*9507f98cSAndroid Build Coastguard Worker Insert(300, 301);
138*9507f98cSAndroid Build Coastguard Worker Cache::Handle* h = cache_->Lookup(EncodeKey(300));
139*9507f98cSAndroid Build Coastguard Worker
140*9507f98cSAndroid Build Coastguard Worker // Frequently used entry must be kept around,
141*9507f98cSAndroid Build Coastguard Worker // as must things that are still in use.
142*9507f98cSAndroid Build Coastguard Worker for (int i = 0; i < kCacheSize + 100; i++) {
143*9507f98cSAndroid Build Coastguard Worker Insert(1000 + i, 2000 + i);
144*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(2000 + i, Lookup(1000 + i));
145*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, Lookup(100));
146*9507f98cSAndroid Build Coastguard Worker }
147*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(101, Lookup(100));
148*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(200));
149*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(301, Lookup(300));
150*9507f98cSAndroid Build Coastguard Worker cache_->Release(h);
151*9507f98cSAndroid Build Coastguard Worker }
152*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,UseExceedsCacheSize)153*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, UseExceedsCacheSize) {
154*9507f98cSAndroid Build Coastguard Worker // Overfill the cache, keeping handles on all inserted entries.
155*9507f98cSAndroid Build Coastguard Worker std::vector<Cache::Handle*> h;
156*9507f98cSAndroid Build Coastguard Worker for (int i = 0; i < kCacheSize + 100; i++) {
157*9507f98cSAndroid Build Coastguard Worker h.push_back(InsertAndReturnHandle(1000 + i, 2000 + i));
158*9507f98cSAndroid Build Coastguard Worker }
159*9507f98cSAndroid Build Coastguard Worker
160*9507f98cSAndroid Build Coastguard Worker // Check that all the entries can be found in the cache.
161*9507f98cSAndroid Build Coastguard Worker for (int i = 0; i < h.size(); i++) {
162*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(2000 + i, Lookup(1000 + i));
163*9507f98cSAndroid Build Coastguard Worker }
164*9507f98cSAndroid Build Coastguard Worker
165*9507f98cSAndroid Build Coastguard Worker for (int i = 0; i < h.size(); i++) {
166*9507f98cSAndroid Build Coastguard Worker cache_->Release(h[i]);
167*9507f98cSAndroid Build Coastguard Worker }
168*9507f98cSAndroid Build Coastguard Worker }
169*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,HeavyEntries)170*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, HeavyEntries) {
171*9507f98cSAndroid Build Coastguard Worker // Add a bunch of light and heavy entries and then count the combined
172*9507f98cSAndroid Build Coastguard Worker // size of items still in the cache, which must be approximately the
173*9507f98cSAndroid Build Coastguard Worker // same as the total capacity.
174*9507f98cSAndroid Build Coastguard Worker const int kLight = 1;
175*9507f98cSAndroid Build Coastguard Worker const int kHeavy = 10;
176*9507f98cSAndroid Build Coastguard Worker int added = 0;
177*9507f98cSAndroid Build Coastguard Worker int index = 0;
178*9507f98cSAndroid Build Coastguard Worker while (added < 2 * kCacheSize) {
179*9507f98cSAndroid Build Coastguard Worker const int weight = (index & 1) ? kLight : kHeavy;
180*9507f98cSAndroid Build Coastguard Worker Insert(index, 1000 + index, weight);
181*9507f98cSAndroid Build Coastguard Worker added += weight;
182*9507f98cSAndroid Build Coastguard Worker index++;
183*9507f98cSAndroid Build Coastguard Worker }
184*9507f98cSAndroid Build Coastguard Worker
185*9507f98cSAndroid Build Coastguard Worker int cached_weight = 0;
186*9507f98cSAndroid Build Coastguard Worker for (int i = 0; i < index; i++) {
187*9507f98cSAndroid Build Coastguard Worker const int weight = (i & 1 ? kLight : kHeavy);
188*9507f98cSAndroid Build Coastguard Worker int r = Lookup(i);
189*9507f98cSAndroid Build Coastguard Worker if (r >= 0) {
190*9507f98cSAndroid Build Coastguard Worker cached_weight += weight;
191*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(1000 + i, r);
192*9507f98cSAndroid Build Coastguard Worker }
193*9507f98cSAndroid Build Coastguard Worker }
194*9507f98cSAndroid Build Coastguard Worker ASSERT_LE(cached_weight, kCacheSize + kCacheSize / 10);
195*9507f98cSAndroid Build Coastguard Worker }
196*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,NewId)197*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, NewId) {
198*9507f98cSAndroid Build Coastguard Worker uint64_t a = cache_->NewId();
199*9507f98cSAndroid Build Coastguard Worker uint64_t b = cache_->NewId();
200*9507f98cSAndroid Build Coastguard Worker ASSERT_NE(a, b);
201*9507f98cSAndroid Build Coastguard Worker }
202*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,Prune)203*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, Prune) {
204*9507f98cSAndroid Build Coastguard Worker Insert(1, 100);
205*9507f98cSAndroid Build Coastguard Worker Insert(2, 200);
206*9507f98cSAndroid Build Coastguard Worker
207*9507f98cSAndroid Build Coastguard Worker Cache::Handle* handle = cache_->Lookup(EncodeKey(1));
208*9507f98cSAndroid Build Coastguard Worker ASSERT_TRUE(handle);
209*9507f98cSAndroid Build Coastguard Worker cache_->Prune();
210*9507f98cSAndroid Build Coastguard Worker cache_->Release(handle);
211*9507f98cSAndroid Build Coastguard Worker
212*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(100, Lookup(1));
213*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(2));
214*9507f98cSAndroid Build Coastguard Worker }
215*9507f98cSAndroid Build Coastguard Worker
TEST_F(CacheTest,ZeroSizeCache)216*9507f98cSAndroid Build Coastguard Worker TEST_F(CacheTest, ZeroSizeCache) {
217*9507f98cSAndroid Build Coastguard Worker delete cache_;
218*9507f98cSAndroid Build Coastguard Worker cache_ = NewLRUCache(0);
219*9507f98cSAndroid Build Coastguard Worker
220*9507f98cSAndroid Build Coastguard Worker Insert(1, 100);
221*9507f98cSAndroid Build Coastguard Worker ASSERT_EQ(-1, Lookup(1));
222*9507f98cSAndroid Build Coastguard Worker }
223*9507f98cSAndroid Build Coastguard Worker
224*9507f98cSAndroid Build Coastguard Worker } // namespace leveldb
225*9507f98cSAndroid Build Coastguard Worker
226