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