cache_test.cc raw

   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(); }
 227