sim_cache_test.cc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include "rocksdb/utilities/sim_cache.h"
  6. #include <cstdlib>
  7. #include "db/db_test_util.h"
  8. #include "port/stack_trace.h"
  9. namespace ROCKSDB_NAMESPACE {
  10. class SimCacheTest : public DBTestBase {
  11. private:
  12. size_t miss_count_ = 0;
  13. size_t hit_count_ = 0;
  14. size_t insert_count_ = 0;
  15. size_t failure_count_ = 0;
  16. public:
  17. const size_t kNumBlocks = 5;
  18. const size_t kValueSize = 1000;
  19. SimCacheTest() : DBTestBase("sim_cache_test", /*env_do_fsync=*/true) {}
  20. BlockBasedTableOptions GetTableOptions() {
  21. BlockBasedTableOptions table_options;
  22. // Set a small enough block size so that each key-value get its own block.
  23. table_options.block_size = 1;
  24. return table_options;
  25. }
  26. Options GetOptions(const BlockBasedTableOptions& table_options) {
  27. Options options = CurrentOptions();
  28. options.create_if_missing = true;
  29. // options.compression = kNoCompression;
  30. options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
  31. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  32. return options;
  33. }
  34. void InitTable(const Options& /*options*/) {
  35. std::string value(kValueSize, 'a');
  36. for (size_t i = 0; i < kNumBlocks * 2; i++) {
  37. ASSERT_OK(Put(std::to_string(i), value.c_str()));
  38. }
  39. }
  40. void RecordCacheCounters(const Options& options) {
  41. miss_count_ = TestGetTickerCount(options, BLOCK_CACHE_MISS);
  42. hit_count_ = TestGetTickerCount(options, BLOCK_CACHE_HIT);
  43. insert_count_ = TestGetTickerCount(options, BLOCK_CACHE_ADD);
  44. failure_count_ = TestGetTickerCount(options, BLOCK_CACHE_ADD_FAILURES);
  45. }
  46. void CheckCacheCounters(const Options& options, size_t expected_misses,
  47. size_t expected_hits, size_t expected_inserts,
  48. size_t expected_failures) {
  49. size_t new_miss_count = TestGetTickerCount(options, BLOCK_CACHE_MISS);
  50. size_t new_hit_count = TestGetTickerCount(options, BLOCK_CACHE_HIT);
  51. size_t new_insert_count = TestGetTickerCount(options, BLOCK_CACHE_ADD);
  52. size_t new_failure_count =
  53. TestGetTickerCount(options, BLOCK_CACHE_ADD_FAILURES);
  54. ASSERT_EQ(miss_count_ + expected_misses, new_miss_count);
  55. ASSERT_EQ(hit_count_ + expected_hits, new_hit_count);
  56. ASSERT_EQ(insert_count_ + expected_inserts, new_insert_count);
  57. ASSERT_EQ(failure_count_ + expected_failures, new_failure_count);
  58. miss_count_ = new_miss_count;
  59. hit_count_ = new_hit_count;
  60. insert_count_ = new_insert_count;
  61. failure_count_ = new_failure_count;
  62. }
  63. };
  64. TEST_F(SimCacheTest, SimCache) {
  65. ReadOptions read_options;
  66. auto table_options = GetTableOptions();
  67. auto options = GetOptions(table_options);
  68. InitTable(options);
  69. LRUCacheOptions co;
  70. co.capacity = 0;
  71. co.num_shard_bits = 0;
  72. co.strict_capacity_limit = false;
  73. co.metadata_charge_policy = kDontChargeCacheMetadata;
  74. std::shared_ptr<SimCache> simCache = NewSimCache(NewLRUCache(co), 20000, 0);
  75. table_options.block_cache = simCache;
  76. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  77. Reopen(options);
  78. RecordCacheCounters(options);
  79. // due to cache entry stats collector
  80. uint64_t base_misses = simCache->get_miss_counter();
  81. std::vector<std::unique_ptr<Iterator>> iterators(kNumBlocks);
  82. Iterator* iter = nullptr;
  83. // Load blocks into cache.
  84. for (size_t i = 0; i < kNumBlocks; i++) {
  85. iter = db_->NewIterator(read_options);
  86. iter->Seek(std::to_string(i));
  87. ASSERT_OK(iter->status());
  88. CheckCacheCounters(options, 1, 0, 1, 0);
  89. iterators[i].reset(iter);
  90. }
  91. ASSERT_EQ(kNumBlocks, simCache->get_hit_counter() +
  92. simCache->get_miss_counter() - base_misses);
  93. ASSERT_EQ(0, simCache->get_hit_counter());
  94. size_t usage = simCache->GetUsage();
  95. ASSERT_LT(0, usage);
  96. ASSERT_EQ(usage, simCache->GetSimUsage());
  97. simCache->SetCapacity(usage);
  98. ASSERT_EQ(usage, simCache->GetPinnedUsage());
  99. // Test with strict capacity limit.
  100. simCache->SetStrictCapacityLimit(true);
  101. iter = db_->NewIterator(read_options);
  102. iter->Seek(std::to_string(kNumBlocks * 2 - 1));
  103. ASSERT_TRUE(iter->status().IsMemoryLimit());
  104. CheckCacheCounters(options, 1, 0, 0, 1);
  105. delete iter;
  106. iter = nullptr;
  107. // Release iterators and access cache again.
  108. for (size_t i = 0; i < kNumBlocks; i++) {
  109. iterators[i].reset();
  110. CheckCacheCounters(options, 0, 0, 0, 0);
  111. }
  112. // Add kNumBlocks again
  113. for (size_t i = 0; i < kNumBlocks; i++) {
  114. std::unique_ptr<Iterator> it(db_->NewIterator(read_options));
  115. it->Seek(std::to_string(i));
  116. ASSERT_OK(it->status());
  117. CheckCacheCounters(options, 0, 1, 0, 0);
  118. }
  119. ASSERT_EQ(5, simCache->get_hit_counter());
  120. for (size_t i = kNumBlocks; i < kNumBlocks * 2; i++) {
  121. std::unique_ptr<Iterator> it(db_->NewIterator(read_options));
  122. it->Seek(std::to_string(i));
  123. ASSERT_OK(it->status());
  124. CheckCacheCounters(options, 1, 0, 1, 0);
  125. }
  126. ASSERT_EQ(0, simCache->GetPinnedUsage());
  127. ASSERT_EQ(3 * kNumBlocks + 1, simCache->get_hit_counter() +
  128. simCache->get_miss_counter() - base_misses);
  129. ASSERT_EQ(6, simCache->get_hit_counter());
  130. }
  131. TEST_F(SimCacheTest, SimCacheLogging) {
  132. auto table_options = GetTableOptions();
  133. auto options = GetOptions(table_options);
  134. options.disable_auto_compactions = true;
  135. LRUCacheOptions co;
  136. co.capacity = 1024 * 1024;
  137. co.metadata_charge_policy = kDontChargeCacheMetadata;
  138. std::shared_ptr<SimCache> sim_cache = NewSimCache(NewLRUCache(co), 20000, 0);
  139. table_options.block_cache = sim_cache;
  140. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  141. Reopen(options);
  142. int num_block_entries = 20;
  143. for (int i = 0; i < num_block_entries; i++) {
  144. ASSERT_OK(Put(Key(i), "val"));
  145. ASSERT_OK(Flush());
  146. }
  147. std::string log_file = test::PerThreadDBPath(env_, "cache_log.txt");
  148. ASSERT_OK(sim_cache->StartActivityLogging(log_file, env_));
  149. for (int i = 0; i < num_block_entries; i++) {
  150. ASSERT_EQ(Get(Key(i)), "val");
  151. }
  152. for (int i = 0; i < num_block_entries; i++) {
  153. ASSERT_EQ(Get(Key(i)), "val");
  154. }
  155. sim_cache->StopActivityLogging();
  156. ASSERT_OK(sim_cache->GetActivityLoggingStatus());
  157. std::string file_contents;
  158. ASSERT_OK(ReadFileToString(env_, log_file, &file_contents));
  159. std::istringstream contents(file_contents);
  160. int lookup_num = 0;
  161. int add_num = 0;
  162. std::string line;
  163. // count number of lookups and additions
  164. while (std::getline(contents, line)) {
  165. // check if the line starts with LOOKUP or ADD
  166. if (line.rfind("LOOKUP -", 0) == 0) {
  167. ++lookup_num;
  168. }
  169. if (line.rfind("ADD -", 0) == 0) {
  170. ++add_num;
  171. }
  172. }
  173. // We asked for every block twice
  174. ASSERT_EQ(lookup_num, num_block_entries * 2);
  175. // We added every block only once, since the cache can hold all blocks
  176. ASSERT_EQ(add_num, num_block_entries);
  177. // Log things again but stop logging automatically after reaching 512 bytes
  178. int max_size = 512;
  179. ASSERT_OK(sim_cache->StartActivityLogging(log_file, env_, max_size));
  180. for (int it = 0; it < 10; it++) {
  181. for (int i = 0; i < num_block_entries; i++) {
  182. ASSERT_EQ(Get(Key(i)), "val");
  183. }
  184. }
  185. ASSERT_OK(sim_cache->GetActivityLoggingStatus());
  186. uint64_t fsize = 0;
  187. ASSERT_OK(env_->GetFileSize(log_file, &fsize));
  188. // error margin of 100 bytes
  189. ASSERT_LT(fsize, max_size + 100);
  190. ASSERT_GT(fsize, max_size - 100);
  191. }
  192. } // namespace ROCKSDB_NAMESPACE
  193. int main(int argc, char** argv) {
  194. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  195. ::testing::InitGoogleTest(&argc, argv);
  196. return RUN_ALL_TESTS();
  197. }