cache_test.cc 5.7 KB

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