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