cache_simulator_test.cc 20 KB

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