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