cache_simulator_test.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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 "utilities/simulator_cache/cache_simulator.h"
  6. #include <cstdlib>
  7. #include "rocksdb/env.h"
  8. #include "rocksdb/trace_record.h"
  9. #include "test_util/testharness.h"
  10. #include "test_util/testutil.h"
  11. namespace ROCKSDB_NAMESPACE {
  12. namespace {
  13. const std::string kBlockKeyPrefix = "test-block-";
  14. const std::string kRefKeyPrefix = "test-get-";
  15. const std::string kRefKeySequenceNumber = std::string(8, 'c');
  16. const uint64_t kGetId = 1;
  17. const uint64_t kGetBlockId = 100;
  18. const uint64_t kCompactionBlockId = 1000;
  19. const uint64_t kCacheSize = 1024 * 1024 * 1024;
  20. const uint64_t kGhostCacheSize = 1024 * 1024;
  21. } // namespace
  22. class CacheSimulatorTest : public testing::Test {
  23. public:
  24. const size_t kNumBlocks = 5;
  25. const size_t kValueSize = 1000;
  26. CacheSimulatorTest() { env_ = ROCKSDB_NAMESPACE::Env::Default(); }
  27. BlockCacheTraceRecord GenerateGetRecord(uint64_t getid) {
  28. BlockCacheTraceRecord record;
  29. record.block_type = TraceType::kBlockTraceDataBlock;
  30. record.block_size = 4096;
  31. record.block_key = kBlockKeyPrefix + std::to_string(kGetBlockId);
  32. record.access_timestamp = env_->NowMicros();
  33. record.cf_id = 0;
  34. record.cf_name = "test";
  35. record.caller = TableReaderCaller::kUserGet;
  36. record.level = 6;
  37. record.sst_fd_number = 0;
  38. record.get_id = getid;
  39. record.is_cache_hit = false;
  40. record.no_insert = false;
  41. record.referenced_key =
  42. kRefKeyPrefix + std::to_string(kGetId) + kRefKeySequenceNumber;
  43. record.referenced_key_exist_in_block = true;
  44. record.referenced_data_size = 100;
  45. record.num_keys_in_block = 300;
  46. return record;
  47. }
  48. BlockCacheTraceRecord GenerateCompactionRecord() {
  49. BlockCacheTraceRecord record;
  50. record.block_type = TraceType::kBlockTraceDataBlock;
  51. record.block_size = 4096;
  52. record.block_key = kBlockKeyPrefix + std::to_string(kCompactionBlockId);
  53. record.access_timestamp = env_->NowMicros();
  54. record.cf_id = 0;
  55. record.cf_name = "test";
  56. record.caller = TableReaderCaller::kCompaction;
  57. record.level = 6;
  58. record.sst_fd_number = kCompactionBlockId;
  59. record.is_cache_hit = false;
  60. record.no_insert = true;
  61. return record;
  62. }
  63. void AssertCache(std::shared_ptr<Cache> sim_cache,
  64. const MissRatioStats& miss_ratio_stats,
  65. uint64_t expected_usage, uint64_t expected_num_accesses,
  66. uint64_t expected_num_misses,
  67. std::vector<std::string> blocks,
  68. std::vector<std::string> keys) {
  69. EXPECT_EQ(expected_usage, sim_cache->GetUsage());
  70. EXPECT_EQ(expected_num_accesses, miss_ratio_stats.total_accesses());
  71. EXPECT_EQ(expected_num_misses, miss_ratio_stats.total_misses());
  72. for (auto const& block : blocks) {
  73. auto handle = sim_cache->Lookup(block);
  74. EXPECT_NE(nullptr, handle);
  75. sim_cache->Release(handle);
  76. }
  77. for (auto const& key : keys) {
  78. std::string row_key = kRefKeyPrefix + key + kRefKeySequenceNumber;
  79. auto handle =
  80. sim_cache->Lookup("0_" + ExtractUserKey(row_key).ToString());
  81. EXPECT_NE(nullptr, handle);
  82. sim_cache->Release(handle);
  83. }
  84. }
  85. Env* env_;
  86. };
  87. TEST_F(CacheSimulatorTest, GhostCache) {
  88. const std::string key1 = "test1";
  89. const std::string key2 = "test2";
  90. std::unique_ptr<GhostCache> ghost_cache(new GhostCache(
  91. NewLRUCache(/*capacity=*/kGhostCacheSize, /*num_shard_bits=*/1,
  92. /*strict_capacity_limit=*/false,
  93. /*high_pri_pool_ratio=*/0)));
  94. EXPECT_FALSE(ghost_cache->Admit(key1));
  95. EXPECT_TRUE(ghost_cache->Admit(key1));
  96. EXPECT_TRUE(ghost_cache->Admit(key1));
  97. EXPECT_FALSE(ghost_cache->Admit(key2));
  98. EXPECT_TRUE(ghost_cache->Admit(key2));
  99. }
  100. TEST_F(CacheSimulatorTest, CacheSimulator) {
  101. const BlockCacheTraceRecord& access = GenerateGetRecord(kGetId);
  102. const BlockCacheTraceRecord& compaction_access = GenerateCompactionRecord();
  103. std::shared_ptr<Cache> sim_cache =
  104. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  105. /*strict_capacity_limit=*/false,
  106. /*high_pri_pool_ratio=*/0);
  107. std::unique_ptr<CacheSimulator> cache_simulator(
  108. new CacheSimulator(nullptr, sim_cache));
  109. cache_simulator->Access(access);
  110. cache_simulator->Access(access);
  111. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().total_accesses());
  112. ASSERT_EQ(50, cache_simulator->miss_ratio_stats().miss_ratio());
  113. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().user_accesses());
  114. ASSERT_EQ(50, cache_simulator->miss_ratio_stats().user_miss_ratio());
  115. cache_simulator->Access(compaction_access);
  116. cache_simulator->Access(compaction_access);
  117. ASSERT_EQ(4, cache_simulator->miss_ratio_stats().total_accesses());
  118. ASSERT_EQ(75, cache_simulator->miss_ratio_stats().miss_ratio());
  119. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().user_accesses());
  120. ASSERT_EQ(50, cache_simulator->miss_ratio_stats().user_miss_ratio());
  121. cache_simulator->reset_counter();
  122. ASSERT_EQ(0, cache_simulator->miss_ratio_stats().total_accesses());
  123. ASSERT_EQ(-1, cache_simulator->miss_ratio_stats().miss_ratio());
  124. auto handle = sim_cache->Lookup(access.block_key);
  125. ASSERT_NE(nullptr, handle);
  126. sim_cache->Release(handle);
  127. handle = sim_cache->Lookup(compaction_access.block_key);
  128. ASSERT_EQ(nullptr, handle);
  129. }
  130. TEST_F(CacheSimulatorTest, GhostCacheSimulator) {
  131. const BlockCacheTraceRecord& access = GenerateGetRecord(kGetId);
  132. std::unique_ptr<GhostCache> ghost_cache(new GhostCache(
  133. NewLRUCache(/*capacity=*/kGhostCacheSize, /*num_shard_bits=*/1,
  134. /*strict_capacity_limit=*/false,
  135. /*high_pri_pool_ratio=*/0)));
  136. std::unique_ptr<CacheSimulator> cache_simulator(new CacheSimulator(
  137. std::move(ghost_cache),
  138. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  139. /*strict_capacity_limit=*/false,
  140. /*high_pri_pool_ratio=*/0)));
  141. cache_simulator->Access(access);
  142. cache_simulator->Access(access);
  143. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().total_accesses());
  144. // Both of them will be miss since we have a ghost cache.
  145. ASSERT_EQ(100, cache_simulator->miss_ratio_stats().miss_ratio());
  146. }
  147. TEST_F(CacheSimulatorTest, PrioritizedCacheSimulator) {
  148. const BlockCacheTraceRecord& access = GenerateGetRecord(kGetId);
  149. std::shared_ptr<Cache> sim_cache =
  150. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  151. /*strict_capacity_limit=*/false,
  152. /*high_pri_pool_ratio=*/0);
  153. std::unique_ptr<PrioritizedCacheSimulator> cache_simulator(
  154. new PrioritizedCacheSimulator(nullptr, sim_cache));
  155. cache_simulator->Access(access);
  156. cache_simulator->Access(access);
  157. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().total_accesses());
  158. ASSERT_EQ(50, cache_simulator->miss_ratio_stats().miss_ratio());
  159. auto handle = sim_cache->Lookup(access.block_key);
  160. ASSERT_NE(nullptr, handle);
  161. sim_cache->Release(handle);
  162. }
  163. TEST_F(CacheSimulatorTest, GhostPrioritizedCacheSimulator) {
  164. const BlockCacheTraceRecord& access = GenerateGetRecord(kGetId);
  165. std::unique_ptr<GhostCache> ghost_cache(new GhostCache(
  166. NewLRUCache(/*capacity=*/kGhostCacheSize, /*num_shard_bits=*/1,
  167. /*strict_capacity_limit=*/false,
  168. /*high_pri_pool_ratio=*/0)));
  169. std::unique_ptr<PrioritizedCacheSimulator> cache_simulator(
  170. new PrioritizedCacheSimulator(
  171. std::move(ghost_cache),
  172. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  173. /*strict_capacity_limit=*/false,
  174. /*high_pri_pool_ratio=*/0)));
  175. cache_simulator->Access(access);
  176. cache_simulator->Access(access);
  177. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().total_accesses());
  178. // Both of them will be miss since we have a ghost cache.
  179. ASSERT_EQ(100, cache_simulator->miss_ratio_stats().miss_ratio());
  180. }
  181. TEST_F(CacheSimulatorTest, HybridRowBlockCacheSimulator) {
  182. uint64_t block_id = 100;
  183. BlockCacheTraceRecord first_get = GenerateGetRecord(kGetId);
  184. first_get.get_from_user_specified_snapshot = true;
  185. BlockCacheTraceRecord second_get = GenerateGetRecord(kGetId + 1);
  186. second_get.referenced_data_size = 0;
  187. second_get.referenced_key_exist_in_block = false;
  188. second_get.get_from_user_specified_snapshot = true;
  189. BlockCacheTraceRecord third_get = GenerateGetRecord(kGetId + 2);
  190. third_get.referenced_data_size = 0;
  191. third_get.referenced_key_exist_in_block = false;
  192. third_get.referenced_key = kRefKeyPrefix + "third_get";
  193. // We didn't find the referenced key in the third get.
  194. third_get.referenced_key_exist_in_block = false;
  195. third_get.referenced_data_size = 0;
  196. std::shared_ptr<Cache> sim_cache =
  197. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  198. /*strict_capacity_limit=*/false,
  199. /*high_pri_pool_ratio=*/0);
  200. std::unique_ptr<HybridRowBlockCacheSimulator> cache_simulator(
  201. new HybridRowBlockCacheSimulator(
  202. nullptr, sim_cache, /*insert_blocks_row_kvpair_misses=*/true));
  203. // The first get request accesses 10 blocks. We should only report 10 accesses
  204. // and 100% miss.
  205. for (uint32_t i = 0; i < 10; i++) {
  206. first_get.block_key = kBlockKeyPrefix + std::to_string(block_id);
  207. cache_simulator->Access(first_get);
  208. block_id++;
  209. }
  210. ASSERT_EQ(10, cache_simulator->miss_ratio_stats().total_accesses());
  211. ASSERT_EQ(100, cache_simulator->miss_ratio_stats().miss_ratio());
  212. ASSERT_EQ(10, cache_simulator->miss_ratio_stats().user_accesses());
  213. ASSERT_EQ(100, cache_simulator->miss_ratio_stats().user_miss_ratio());
  214. auto handle =
  215. sim_cache->Lookup(std::to_string(first_get.sst_fd_number) + "_" +
  216. ExtractUserKey(first_get.referenced_key).ToString());
  217. ASSERT_NE(nullptr, handle);
  218. sim_cache->Release(handle);
  219. for (uint32_t i = 100; i < block_id; i++) {
  220. handle = sim_cache->Lookup(kBlockKeyPrefix + std::to_string(i));
  221. ASSERT_NE(nullptr, handle);
  222. sim_cache->Release(handle);
  223. }
  224. // The second get request accesses the same key. We should report 15
  225. // access and 66% miss, 10 misses with 15 accesses.
  226. // We do not consider these 5 block lookups as misses since the row hits the
  227. // cache.
  228. for (uint32_t i = 0; i < 5; i++) {
  229. second_get.block_key = kBlockKeyPrefix + std::to_string(block_id);
  230. cache_simulator->Access(second_get);
  231. block_id++;
  232. }
  233. ASSERT_EQ(15, cache_simulator->miss_ratio_stats().total_accesses());
  234. ASSERT_EQ(66, static_cast<uint64_t>(
  235. cache_simulator->miss_ratio_stats().miss_ratio()));
  236. ASSERT_EQ(15, cache_simulator->miss_ratio_stats().user_accesses());
  237. ASSERT_EQ(66, static_cast<uint64_t>(
  238. cache_simulator->miss_ratio_stats().user_miss_ratio()));
  239. handle =
  240. sim_cache->Lookup(std::to_string(second_get.sst_fd_number) + "_" +
  241. ExtractUserKey(second_get.referenced_key).ToString());
  242. ASSERT_NE(nullptr, handle);
  243. sim_cache->Release(handle);
  244. for (uint32_t i = 100; i < block_id; i++) {
  245. handle = sim_cache->Lookup(kBlockKeyPrefix + std::to_string(i));
  246. if (i < 110) {
  247. ASSERT_NE(nullptr, handle) << i;
  248. sim_cache->Release(handle);
  249. } else {
  250. ASSERT_EQ(nullptr, handle) << i;
  251. }
  252. }
  253. // The third get on a different key and does not have a size.
  254. // This key should not be inserted into the cache.
  255. for (uint32_t i = 0; i < 5; i++) {
  256. third_get.block_key = kBlockKeyPrefix + std::to_string(block_id);
  257. cache_simulator->Access(third_get);
  258. block_id++;
  259. }
  260. ASSERT_EQ(20, cache_simulator->miss_ratio_stats().total_accesses());
  261. ASSERT_EQ(75, static_cast<uint64_t>(
  262. cache_simulator->miss_ratio_stats().miss_ratio()));
  263. ASSERT_EQ(20, cache_simulator->miss_ratio_stats().user_accesses());
  264. ASSERT_EQ(75, static_cast<uint64_t>(
  265. cache_simulator->miss_ratio_stats().user_miss_ratio()));
  266. // Assert that the third key is not inserted into the cache.
  267. handle = sim_cache->Lookup(std::to_string(third_get.sst_fd_number) + "_" +
  268. third_get.referenced_key);
  269. ASSERT_EQ(nullptr, handle);
  270. for (uint32_t i = 100; i < block_id; i++) {
  271. if (i < 110 || i >= 115) {
  272. handle = sim_cache->Lookup(kBlockKeyPrefix + std::to_string(i));
  273. ASSERT_NE(nullptr, handle) << i;
  274. sim_cache->Release(handle);
  275. } else {
  276. handle = sim_cache->Lookup(kBlockKeyPrefix + std::to_string(i));
  277. ASSERT_EQ(nullptr, handle) << i;
  278. }
  279. }
  280. }
  281. TEST_F(CacheSimulatorTest, HybridRowBlockCacheSimulatorGetTest) {
  282. BlockCacheTraceRecord get = GenerateGetRecord(kGetId);
  283. get.block_size = 1;
  284. get.referenced_data_size = 0;
  285. get.access_timestamp = 0;
  286. get.block_key = "1";
  287. get.get_id = 1;
  288. get.get_from_user_specified_snapshot = false;
  289. get.referenced_key =
  290. kRefKeyPrefix + std::to_string(1) + kRefKeySequenceNumber;
  291. get.no_insert = false;
  292. get.sst_fd_number = 0;
  293. get.get_from_user_specified_snapshot = false;
  294. LRUCacheOptions co;
  295. co.capacity = 16;
  296. co.num_shard_bits = 1;
  297. co.strict_capacity_limit = false;
  298. co.high_pri_pool_ratio = 0;
  299. co.metadata_charge_policy = kDontChargeCacheMetadata;
  300. std::shared_ptr<Cache> sim_cache = NewLRUCache(co);
  301. std::unique_ptr<HybridRowBlockCacheSimulator> cache_simulator(
  302. new HybridRowBlockCacheSimulator(
  303. nullptr, sim_cache, /*insert_blocks_row_kvpair_misses=*/true));
  304. // Expect a miss and does not insert the row key-value pair since it does not
  305. // have size.
  306. cache_simulator->Access(get);
  307. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 1, 1, 1, {"1"},
  308. {});
  309. get.access_timestamp += 1;
  310. get.referenced_data_size = 1;
  311. get.block_key = "2";
  312. cache_simulator->Access(get);
  313. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 3, 2, 2,
  314. {"1", "2"}, {"1"});
  315. get.access_timestamp += 1;
  316. get.block_key = "3";
  317. // K1 should not inserted again.
  318. cache_simulator->Access(get);
  319. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 4, 3, 3,
  320. {"1", "2", "3"}, {"1"});
  321. // A second get request referencing the same key.
  322. get.access_timestamp += 1;
  323. get.get_id = 2;
  324. get.block_key = "4";
  325. get.referenced_data_size = 0;
  326. cache_simulator->Access(get);
  327. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 4, 4, 3,
  328. {"1", "2", "3"}, {"1"});
  329. // A third get request searches three files, three different keys.
  330. // And the second key observes a hit.
  331. get.access_timestamp += 1;
  332. get.referenced_data_size = 1;
  333. get.get_id = 3;
  334. get.block_key = "3";
  335. get.referenced_key = kRefKeyPrefix + "2" + kRefKeySequenceNumber;
  336. // K2 should observe a miss. Block 3 observes a hit.
  337. cache_simulator->Access(get);
  338. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 5, 5, 3,
  339. {"1", "2", "3"}, {"1", "2"});
  340. get.access_timestamp += 1;
  341. get.referenced_data_size = 1;
  342. get.get_id = 3;
  343. get.block_key = "4";
  344. get.referenced_data_size = 1;
  345. get.referenced_key = kRefKeyPrefix + "1" + kRefKeySequenceNumber;
  346. // K1 should observe a hit.
  347. cache_simulator->Access(get);
  348. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 5, 6, 3,
  349. {"1", "2", "3"}, {"1", "2"});
  350. get.access_timestamp += 1;
  351. get.referenced_data_size = 1;
  352. get.get_id = 3;
  353. get.block_key = "4";
  354. get.referenced_data_size = 1;
  355. get.referenced_key = kRefKeyPrefix + "3" + kRefKeySequenceNumber;
  356. // K3 should observe a miss.
  357. // However, as the get already complete, we should not access k3 any more.
  358. cache_simulator->Access(get);
  359. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 5, 7, 3,
  360. {"1", "2", "3"}, {"1", "2"});
  361. // A fourth get request searches one file and two blocks. One row key.
  362. get.access_timestamp += 1;
  363. get.get_id = 4;
  364. get.block_key = "5";
  365. get.referenced_key = kRefKeyPrefix + "4" + kRefKeySequenceNumber;
  366. get.referenced_data_size = 1;
  367. cache_simulator->Access(get);
  368. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 7, 8, 4,
  369. {"1", "2", "3", "5"}, {"1", "2", "4"});
  370. for (auto const& key : {"1", "2", "4"}) {
  371. auto handle = sim_cache->Lookup("0_" + kRefKeyPrefix + key);
  372. ASSERT_NE(nullptr, handle);
  373. sim_cache->Release(handle);
  374. }
  375. // A bunch of insertions which evict cached row keys.
  376. for (uint32_t i = 6; i < 100; i++) {
  377. get.access_timestamp += 1;
  378. get.get_id = 0;
  379. get.block_key = std::to_string(i);
  380. cache_simulator->Access(get);
  381. }
  382. get.get_id = 4;
  383. // A different block.
  384. get.block_key = "100";
  385. // Same row key and should not be inserted again.
  386. get.referenced_key = kRefKeyPrefix + "4" + kRefKeySequenceNumber;
  387. get.referenced_data_size = 1;
  388. cache_simulator->Access(get);
  389. AssertCache(sim_cache, cache_simulator->miss_ratio_stats(), 16, 103, 99, {},
  390. {});
  391. for (auto const& key : {"1", "2", "4"}) {
  392. auto handle = sim_cache->Lookup("0_" + kRefKeyPrefix + key);
  393. ASSERT_EQ(nullptr, handle);
  394. }
  395. }
  396. TEST_F(CacheSimulatorTest, HybridRowBlockNoInsertCacheSimulator) {
  397. uint64_t block_id = 100;
  398. BlockCacheTraceRecord first_get = GenerateGetRecord(kGetId);
  399. std::shared_ptr<Cache> sim_cache =
  400. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  401. /*strict_capacity_limit=*/false,
  402. /*high_pri_pool_ratio=*/0);
  403. std::unique_ptr<HybridRowBlockCacheSimulator> cache_simulator(
  404. new HybridRowBlockCacheSimulator(
  405. nullptr, sim_cache, /*insert_blocks_row_kvpair_misses=*/false));
  406. for (uint32_t i = 0; i < 9; i++) {
  407. first_get.block_key = kBlockKeyPrefix + std::to_string(block_id);
  408. cache_simulator->Access(first_get);
  409. block_id++;
  410. }
  411. auto handle =
  412. sim_cache->Lookup(std::to_string(first_get.sst_fd_number) + "_" +
  413. ExtractUserKey(first_get.referenced_key).ToString());
  414. ASSERT_NE(nullptr, handle);
  415. sim_cache->Release(handle);
  416. // All blocks are missing from the cache since insert_blocks_row_kvpair_misses
  417. // is set to false.
  418. for (uint32_t i = 100; i < block_id; i++) {
  419. handle = sim_cache->Lookup(kBlockKeyPrefix + std::to_string(i));
  420. ASSERT_EQ(nullptr, handle);
  421. }
  422. }
  423. TEST_F(CacheSimulatorTest, GhostHybridRowBlockCacheSimulator) {
  424. std::unique_ptr<GhostCache> ghost_cache(new GhostCache(
  425. NewLRUCache(/*capacity=*/kGhostCacheSize, /*num_shard_bits=*/1,
  426. /*strict_capacity_limit=*/false,
  427. /*high_pri_pool_ratio=*/0)));
  428. const BlockCacheTraceRecord& first_get = GenerateGetRecord(kGetId);
  429. const BlockCacheTraceRecord& second_get = GenerateGetRecord(kGetId + 1);
  430. const BlockCacheTraceRecord& third_get = GenerateGetRecord(kGetId + 2);
  431. std::unique_ptr<HybridRowBlockCacheSimulator> cache_simulator(
  432. new HybridRowBlockCacheSimulator(
  433. std::move(ghost_cache),
  434. NewLRUCache(/*capacity=*/kCacheSize, /*num_shard_bits=*/1,
  435. /*strict_capacity_limit=*/false,
  436. /*high_pri_pool_ratio=*/0),
  437. /*insert_blocks_row_kvpair_misses=*/false));
  438. // Two get requests access the same key.
  439. cache_simulator->Access(first_get);
  440. cache_simulator->Access(second_get);
  441. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().total_accesses());
  442. ASSERT_EQ(100, cache_simulator->miss_ratio_stats().miss_ratio());
  443. ASSERT_EQ(2, cache_simulator->miss_ratio_stats().user_accesses());
  444. ASSERT_EQ(100, cache_simulator->miss_ratio_stats().user_miss_ratio());
  445. // We insert the key-value pair upon the second get request. A third get
  446. // request should observe a hit.
  447. for (uint32_t i = 0; i < 10; i++) {
  448. cache_simulator->Access(third_get);
  449. }
  450. ASSERT_EQ(12, cache_simulator->miss_ratio_stats().total_accesses());
  451. ASSERT_EQ(16, static_cast<uint64_t>(
  452. cache_simulator->miss_ratio_stats().miss_ratio()));
  453. ASSERT_EQ(12, cache_simulator->miss_ratio_stats().user_accesses());
  454. ASSERT_EQ(16, static_cast<uint64_t>(
  455. cache_simulator->miss_ratio_stats().user_miss_ratio()));
  456. }
  457. } // namespace ROCKSDB_NAMESPACE
  458. int main(int argc, char** argv) {
  459. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  460. ::testing::InitGoogleTest(&argc, argv);
  461. return RUN_ALL_TESTS();
  462. }