]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/trace_replay/block_cache_tracer_test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / trace_replay / block_cache_tracer_test.cc
CommitLineData
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 "trace_replay/block_cache_tracer.h"
1e59de90
TL
7
8#include "rocksdb/db.h"
f67539c2
TL
9#include "rocksdb/env.h"
10#include "rocksdb/status.h"
11#include "rocksdb/trace_reader_writer.h"
1e59de90 12#include "rocksdb/trace_record.h"
f67539c2
TL
13#include "test_util/testharness.h"
14#include "test_util/testutil.h"
15
16namespace ROCKSDB_NAMESPACE {
17
18namespace {
19const uint64_t kBlockSize = 1024;
20const std::string kBlockKeyPrefix = "test-block-";
21const uint32_t kCFId = 0;
22const uint32_t kLevel = 1;
23const uint64_t kSSTFDNumber = 100;
24const std::string kRefKeyPrefix = "test-get-";
25const uint64_t kNumKeysInBlock = 1024;
26const uint64_t kReferencedDataSize = 10;
27} // namespace
28
29class BlockCacheTracerTest : public testing::Test {
30 public:
31 BlockCacheTracerTest() {
32 test_path_ = test::PerThreadDBPath("block_cache_tracer_test");
33 env_ = ROCKSDB_NAMESPACE::Env::Default();
1e59de90 34 clock_ = env_->GetSystemClock().get();
f67539c2
TL
35 EXPECT_OK(env_->CreateDir(test_path_));
36 trace_file_path_ = test_path_ + "/block_cache_trace";
37 }
38
39 ~BlockCacheTracerTest() override {
40 EXPECT_OK(env_->DeleteFile(trace_file_path_));
41 EXPECT_OK(env_->DeleteDir(test_path_));
42 }
43
44 TableReaderCaller GetCaller(uint32_t key_id) {
45 uint32_t n = key_id % 5;
46 switch (n) {
47 case 0:
48 return TableReaderCaller::kPrefetch;
49 case 1:
50 return TableReaderCaller::kCompaction;
51 case 2:
52 return TableReaderCaller::kUserGet;
53 case 3:
54 return TableReaderCaller::kUserMultiGet;
55 case 4:
56 return TableReaderCaller::kUserIterator;
57 }
58 assert(false);
1e59de90 59 return TableReaderCaller::kMaxBlockCacheLookupCaller;
f67539c2
TL
60 }
61
62 void WriteBlockAccess(BlockCacheTraceWriter* writer, uint32_t from_key_id,
63 TraceType block_type, uint32_t nblocks) {
64 assert(writer);
65 for (uint32_t i = 0; i < nblocks; i++) {
66 uint32_t key_id = from_key_id + i;
67 BlockCacheTraceRecord record;
68 record.block_type = block_type;
69 record.block_size = kBlockSize + key_id;
70 record.block_key = (kBlockKeyPrefix + std::to_string(key_id));
1e59de90 71 record.access_timestamp = clock_->NowMicros();
f67539c2
TL
72 record.cf_id = kCFId;
73 record.cf_name = kDefaultColumnFamilyName;
74 record.caller = GetCaller(key_id);
75 record.level = kLevel;
76 record.sst_fd_number = kSSTFDNumber + key_id;
1e59de90
TL
77 record.is_cache_hit = false;
78 record.no_insert = false;
f67539c2
TL
79 // Provide get_id for all callers. The writer should only write get_id
80 // when the caller is either GET or MGET.
81 record.get_id = key_id + 1;
1e59de90 82 record.get_from_user_specified_snapshot = true;
f67539c2
TL
83 // Provide these fields for all block types.
84 // The writer should only write these fields for data blocks and the
85 // caller is either GET or MGET.
86 record.referenced_key = (kRefKeyPrefix + std::to_string(key_id));
1e59de90 87 record.referenced_key_exist_in_block = true;
f67539c2
TL
88 record.num_keys_in_block = kNumKeysInBlock;
89 record.referenced_data_size = kReferencedDataSize + key_id;
90 ASSERT_OK(writer->WriteBlockAccess(
91 record, record.block_key, record.cf_name, record.referenced_key));
92 }
93 }
94
95 BlockCacheTraceRecord GenerateAccessRecord() {
96 uint32_t key_id = 0;
97 BlockCacheTraceRecord record;
98 record.block_type = TraceType::kBlockTraceDataBlock;
99 record.block_size = kBlockSize;
100 record.block_key = kBlockKeyPrefix + std::to_string(key_id);
1e59de90 101 record.access_timestamp = clock_->NowMicros();
f67539c2
TL
102 record.cf_id = kCFId;
103 record.cf_name = kDefaultColumnFamilyName;
104 record.caller = GetCaller(key_id);
105 record.level = kLevel;
106 record.sst_fd_number = kSSTFDNumber + key_id;
1e59de90
TL
107 record.is_cache_hit = false;
108 record.no_insert = false;
f67539c2 109 record.referenced_key = kRefKeyPrefix + std::to_string(key_id);
1e59de90 110 record.referenced_key_exist_in_block = true;
f67539c2
TL
111 record.num_keys_in_block = kNumKeysInBlock;
112 return record;
113 }
114
115 void VerifyAccess(BlockCacheTraceReader* reader, uint32_t from_key_id,
116 TraceType block_type, uint32_t nblocks) {
117 assert(reader);
118 for (uint32_t i = 0; i < nblocks; i++) {
119 uint32_t key_id = from_key_id + i;
120 BlockCacheTraceRecord record;
121 ASSERT_OK(reader->ReadAccess(&record));
122 ASSERT_EQ(block_type, record.block_type);
123 ASSERT_EQ(kBlockSize + key_id, record.block_size);
124 ASSERT_EQ(kBlockKeyPrefix + std::to_string(key_id), record.block_key);
125 ASSERT_EQ(kCFId, record.cf_id);
126 ASSERT_EQ(kDefaultColumnFamilyName, record.cf_name);
127 ASSERT_EQ(GetCaller(key_id), record.caller);
128 ASSERT_EQ(kLevel, record.level);
129 ASSERT_EQ(kSSTFDNumber + key_id, record.sst_fd_number);
1e59de90
TL
130 ASSERT_FALSE(record.is_cache_hit);
131 ASSERT_FALSE(record.no_insert);
f67539c2
TL
132 if (record.caller == TableReaderCaller::kUserGet ||
133 record.caller == TableReaderCaller::kUserMultiGet) {
134 ASSERT_EQ(key_id + 1, record.get_id);
1e59de90 135 ASSERT_TRUE(record.get_from_user_specified_snapshot);
f67539c2
TL
136 ASSERT_EQ(kRefKeyPrefix + std::to_string(key_id),
137 record.referenced_key);
138 } else {
139 ASSERT_EQ(BlockCacheTraceHelper::kReservedGetId, record.get_id);
1e59de90 140 ASSERT_FALSE(record.get_from_user_specified_snapshot);
f67539c2
TL
141 ASSERT_EQ("", record.referenced_key);
142 }
143 if (block_type == TraceType::kBlockTraceDataBlock &&
144 (record.caller == TableReaderCaller::kUserGet ||
145 record.caller == TableReaderCaller::kUserMultiGet)) {
1e59de90 146 ASSERT_TRUE(record.referenced_key_exist_in_block);
f67539c2
TL
147 ASSERT_EQ(kNumKeysInBlock, record.num_keys_in_block);
148 ASSERT_EQ(kReferencedDataSize + key_id, record.referenced_data_size);
149 continue;
150 }
1e59de90 151 ASSERT_FALSE(record.referenced_key_exist_in_block);
f67539c2
TL
152 ASSERT_EQ(0, record.num_keys_in_block);
153 ASSERT_EQ(0, record.referenced_data_size);
154 }
155 }
156
157 Env* env_;
1e59de90 158 SystemClock* clock_;
f67539c2
TL
159 EnvOptions env_options_;
160 std::string trace_file_path_;
161 std::string test_path_;
162};
163
164TEST_F(BlockCacheTracerTest, AtomicWriteBeforeStartTrace) {
165 BlockCacheTraceRecord record = GenerateAccessRecord();
166 {
167 std::unique_ptr<TraceWriter> trace_writer;
168 ASSERT_OK(NewFileTraceWriter(env_, env_options_, trace_file_path_,
169 &trace_writer));
170 BlockCacheTracer writer;
171 // The record should be written to the trace_file since StartTrace is not
172 // called.
173 ASSERT_OK(writer.WriteBlockAccess(record, record.block_key, record.cf_name,
174 record.referenced_key));
175 ASSERT_OK(env_->FileExists(trace_file_path_));
176 }
177 {
178 // Verify trace file contains nothing.
179 std::unique_ptr<TraceReader> trace_reader;
180 ASSERT_OK(NewFileTraceReader(env_, env_options_, trace_file_path_,
181 &trace_reader));
182 BlockCacheTraceReader reader(std::move(trace_reader));
183 BlockCacheTraceHeader header;
184 ASSERT_NOK(reader.ReadHeader(&header));
185 }
186}
187
188TEST_F(BlockCacheTracerTest, AtomicWrite) {
189 BlockCacheTraceRecord record = GenerateAccessRecord();
190 {
1e59de90
TL
191 BlockCacheTraceWriterOptions trace_writer_opt;
192 BlockCacheTraceOptions trace_opt;
f67539c2
TL
193 std::unique_ptr<TraceWriter> trace_writer;
194 ASSERT_OK(NewFileTraceWriter(env_, env_options_, trace_file_path_,
195 &trace_writer));
1e59de90
TL
196 std::unique_ptr<BlockCacheTraceWriter> block_cache_trace_writer =
197 NewBlockCacheTraceWriter(env_->GetSystemClock().get(), trace_writer_opt,
198 std::move(trace_writer));
199 ASSERT_NE(block_cache_trace_writer, nullptr);
f67539c2 200 BlockCacheTracer writer;
1e59de90
TL
201 ASSERT_OK(
202 writer.StartTrace(trace_opt, std::move(block_cache_trace_writer)));
f67539c2
TL
203 ASSERT_OK(writer.WriteBlockAccess(record, record.block_key, record.cf_name,
204 record.referenced_key));
205 ASSERT_OK(env_->FileExists(trace_file_path_));
206 }
207 {
208 // Verify trace file contains one record.
209 std::unique_ptr<TraceReader> trace_reader;
210 ASSERT_OK(NewFileTraceReader(env_, env_options_, trace_file_path_,
211 &trace_reader));
212 BlockCacheTraceReader reader(std::move(trace_reader));
213 BlockCacheTraceHeader header;
214 ASSERT_OK(reader.ReadHeader(&header));
1e59de90
TL
215 ASSERT_EQ(kMajorVersion, static_cast<int>(header.rocksdb_major_version));
216 ASSERT_EQ(kMinorVersion, static_cast<int>(header.rocksdb_minor_version));
f67539c2
TL
217 VerifyAccess(&reader, 0, TraceType::kBlockTraceDataBlock, 1);
218 ASSERT_NOK(reader.ReadAccess(&record));
219 }
220}
221
222TEST_F(BlockCacheTracerTest, ConsecutiveStartTrace) {
1e59de90
TL
223 BlockCacheTraceWriterOptions trace_writer_opt;
224 BlockCacheTraceOptions trace_opt;
f67539c2
TL
225 std::unique_ptr<TraceWriter> trace_writer;
226 ASSERT_OK(
227 NewFileTraceWriter(env_, env_options_, trace_file_path_, &trace_writer));
1e59de90
TL
228 std::unique_ptr<BlockCacheTraceWriter> block_cache_trace_writer =
229 NewBlockCacheTraceWriter(env_->GetSystemClock().get(), trace_writer_opt,
230 std::move(trace_writer));
231 ASSERT_NE(block_cache_trace_writer, nullptr);
f67539c2 232 BlockCacheTracer writer;
1e59de90
TL
233 ASSERT_OK(writer.StartTrace(trace_opt, std::move(block_cache_trace_writer)));
234 ASSERT_NOK(writer.StartTrace(trace_opt, std::move(block_cache_trace_writer)));
f67539c2
TL
235 ASSERT_OK(env_->FileExists(trace_file_path_));
236}
237
238TEST_F(BlockCacheTracerTest, AtomicNoWriteAfterEndTrace) {
239 BlockCacheTraceRecord record = GenerateAccessRecord();
240 {
1e59de90
TL
241 BlockCacheTraceWriterOptions trace_writer_opt;
242 BlockCacheTraceOptions trace_opt;
f67539c2
TL
243 std::unique_ptr<TraceWriter> trace_writer;
244 ASSERT_OK(NewFileTraceWriter(env_, env_options_, trace_file_path_,
245 &trace_writer));
1e59de90
TL
246 std::unique_ptr<BlockCacheTraceWriter> block_cache_trace_writer =
247 NewBlockCacheTraceWriter(env_->GetSystemClock().get(), trace_writer_opt,
248 std::move(trace_writer));
249 ASSERT_NE(block_cache_trace_writer, nullptr);
f67539c2 250 BlockCacheTracer writer;
1e59de90
TL
251 ASSERT_OK(
252 writer.StartTrace(trace_opt, std::move(block_cache_trace_writer)));
f67539c2
TL
253 ASSERT_OK(writer.WriteBlockAccess(record, record.block_key, record.cf_name,
254 record.referenced_key));
255 writer.EndTrace();
256 // Write the record again. This time the record should not be written since
257 // EndTrace is called.
258 ASSERT_OK(writer.WriteBlockAccess(record, record.block_key, record.cf_name,
259 record.referenced_key));
260 ASSERT_OK(env_->FileExists(trace_file_path_));
261 }
262 {
263 // Verify trace file contains one record.
264 std::unique_ptr<TraceReader> trace_reader;
265 ASSERT_OK(NewFileTraceReader(env_, env_options_, trace_file_path_,
266 &trace_reader));
267 BlockCacheTraceReader reader(std::move(trace_reader));
268 BlockCacheTraceHeader header;
269 ASSERT_OK(reader.ReadHeader(&header));
1e59de90
TL
270 ASSERT_EQ(kMajorVersion, static_cast<int>(header.rocksdb_major_version));
271 ASSERT_EQ(kMinorVersion, static_cast<int>(header.rocksdb_minor_version));
f67539c2
TL
272 VerifyAccess(&reader, 0, TraceType::kBlockTraceDataBlock, 1);
273 ASSERT_NOK(reader.ReadAccess(&record));
274 }
275}
276
277TEST_F(BlockCacheTracerTest, NextGetId) {
278 BlockCacheTracer writer;
279 {
1e59de90
TL
280 BlockCacheTraceWriterOptions trace_writer_opt;
281 BlockCacheTraceOptions trace_opt;
f67539c2
TL
282 std::unique_ptr<TraceWriter> trace_writer;
283 ASSERT_OK(NewFileTraceWriter(env_, env_options_, trace_file_path_,
284 &trace_writer));
1e59de90
TL
285 std::unique_ptr<BlockCacheTraceWriter> block_cache_trace_writer =
286 NewBlockCacheTraceWriter(env_->GetSystemClock().get(), trace_writer_opt,
287 std::move(trace_writer));
288 ASSERT_NE(block_cache_trace_writer, nullptr);
f67539c2
TL
289 // next get id should always return 0 before we call StartTrace.
290 ASSERT_EQ(0, writer.NextGetId());
291 ASSERT_EQ(0, writer.NextGetId());
1e59de90
TL
292 ASSERT_OK(
293 writer.StartTrace(trace_opt, std::move(block_cache_trace_writer)));
f67539c2
TL
294 ASSERT_EQ(1, writer.NextGetId());
295 ASSERT_EQ(2, writer.NextGetId());
296 writer.EndTrace();
297 // next get id should return 0.
298 ASSERT_EQ(0, writer.NextGetId());
299 }
300
301 // Start trace again and next get id should return 1.
302 {
1e59de90
TL
303 BlockCacheTraceWriterOptions trace_writer_opt;
304 BlockCacheTraceOptions trace_opt;
f67539c2
TL
305 std::unique_ptr<TraceWriter> trace_writer;
306 ASSERT_OK(NewFileTraceWriter(env_, env_options_, trace_file_path_,
307 &trace_writer));
1e59de90
TL
308 std::unique_ptr<BlockCacheTraceWriter> block_cache_trace_writer =
309 NewBlockCacheTraceWriter(env_->GetSystemClock().get(), trace_writer_opt,
310 std::move(trace_writer));
311 ASSERT_NE(block_cache_trace_writer, nullptr);
312 ASSERT_OK(
313 writer.StartTrace(trace_opt, std::move(block_cache_trace_writer)));
f67539c2
TL
314 ASSERT_EQ(1, writer.NextGetId());
315 }
316}
317
318TEST_F(BlockCacheTracerTest, MixedBlocks) {
319 {
320 // Generate a trace file containing a mix of blocks.
1e59de90 321 BlockCacheTraceWriterOptions trace_writer_opt;
f67539c2
TL
322 std::unique_ptr<TraceWriter> trace_writer;
323 ASSERT_OK(NewFileTraceWriter(env_, env_options_, trace_file_path_,
324 &trace_writer));
1e59de90
TL
325 std::unique_ptr<BlockCacheTraceWriter> block_cache_trace_writer =
326 NewBlockCacheTraceWriter(env_->GetSystemClock().get(), trace_writer_opt,
327 std::move(trace_writer));
328 ASSERT_NE(block_cache_trace_writer, nullptr);
329 ASSERT_OK(block_cache_trace_writer->WriteHeader());
f67539c2 330 // Write blocks of different types.
1e59de90
TL
331 WriteBlockAccess(block_cache_trace_writer.get(), 0,
332 TraceType::kBlockTraceUncompressionDictBlock, 10);
333 WriteBlockAccess(block_cache_trace_writer.get(), 10,
334 TraceType::kBlockTraceDataBlock, 10);
335 WriteBlockAccess(block_cache_trace_writer.get(), 20,
336 TraceType::kBlockTraceFilterBlock, 10);
337 WriteBlockAccess(block_cache_trace_writer.get(), 30,
338 TraceType::kBlockTraceIndexBlock, 10);
339 WriteBlockAccess(block_cache_trace_writer.get(), 40,
340 TraceType::kBlockTraceRangeDeletionBlock, 10);
f67539c2
TL
341 ASSERT_OK(env_->FileExists(trace_file_path_));
342 }
343
344 {
345 // Verify trace file is generated correctly.
346 std::unique_ptr<TraceReader> trace_reader;
347 ASSERT_OK(NewFileTraceReader(env_, env_options_, trace_file_path_,
348 &trace_reader));
349 BlockCacheTraceReader reader(std::move(trace_reader));
350 BlockCacheTraceHeader header;
351 ASSERT_OK(reader.ReadHeader(&header));
1e59de90
TL
352 ASSERT_EQ(kMajorVersion, static_cast<int>(header.rocksdb_major_version));
353 ASSERT_EQ(kMinorVersion, static_cast<int>(header.rocksdb_minor_version));
f67539c2
TL
354 // Read blocks.
355 VerifyAccess(&reader, 0, TraceType::kBlockTraceUncompressionDictBlock, 10);
356 VerifyAccess(&reader, 10, TraceType::kBlockTraceDataBlock, 10);
357 VerifyAccess(&reader, 20, TraceType::kBlockTraceFilterBlock, 10);
358 VerifyAccess(&reader, 30, TraceType::kBlockTraceIndexBlock, 10);
359 VerifyAccess(&reader, 40, TraceType::kBlockTraceRangeDeletionBlock, 10);
360 // Read one more record should report an error.
361 BlockCacheTraceRecord record;
362 ASSERT_NOK(reader.ReadAccess(&record));
363 }
364}
365
366TEST_F(BlockCacheTracerTest, HumanReadableTrace) {
367 BlockCacheTraceRecord record = GenerateAccessRecord();
368 record.get_id = 1;
369 record.referenced_key = "";
370 record.caller = TableReaderCaller::kUserGet;
1e59de90 371 record.get_from_user_specified_snapshot = true;
f67539c2
TL
372 record.referenced_data_size = kReferencedDataSize;
373 PutFixed32(&record.referenced_key, 111);
374 PutLengthPrefixedSlice(&record.referenced_key, "get_key");
375 PutFixed64(&record.referenced_key, 2 << 8);
376 PutLengthPrefixedSlice(&record.block_key, "block_key");
377 PutVarint64(&record.block_key, 333);
378 {
379 // Generate a human readable trace file.
380 BlockCacheHumanReadableTraceWriter writer;
381 ASSERT_OK(writer.NewWritableFile(trace_file_path_, env_));
382 ASSERT_OK(writer.WriteHumanReadableTraceRecord(record, 1, 1));
383 ASSERT_OK(env_->FileExists(trace_file_path_));
384 }
385 {
386 BlockCacheHumanReadableTraceReader reader(trace_file_path_);
387 BlockCacheTraceHeader header;
388 BlockCacheTraceRecord read_record;
389 ASSERT_OK(reader.ReadHeader(&header));
390 ASSERT_OK(reader.ReadAccess(&read_record));
391 ASSERT_EQ(TraceType::kBlockTraceDataBlock, read_record.block_type);
392 ASSERT_EQ(kBlockSize, read_record.block_size);
393 ASSERT_EQ(kCFId, read_record.cf_id);
394 ASSERT_EQ(kDefaultColumnFamilyName, read_record.cf_name);
395 ASSERT_EQ(TableReaderCaller::kUserGet, read_record.caller);
396 ASSERT_EQ(kLevel, read_record.level);
397 ASSERT_EQ(kSSTFDNumber, read_record.sst_fd_number);
1e59de90
TL
398 ASSERT_FALSE(read_record.is_cache_hit);
399 ASSERT_FALSE(read_record.no_insert);
f67539c2 400 ASSERT_EQ(1, read_record.get_id);
1e59de90
TL
401 ASSERT_TRUE(read_record.get_from_user_specified_snapshot);
402 ASSERT_TRUE(read_record.referenced_key_exist_in_block);
f67539c2
TL
403 ASSERT_EQ(kNumKeysInBlock, read_record.num_keys_in_block);
404 ASSERT_EQ(kReferencedDataSize, read_record.referenced_data_size);
405 ASSERT_EQ(record.block_key.size(), read_record.block_key.size());
406 ASSERT_EQ(record.referenced_key.size(), record.referenced_key.size());
407 ASSERT_EQ(112, BlockCacheTraceHelper::GetTableId(read_record));
408 ASSERT_EQ(3, BlockCacheTraceHelper::GetSequenceNumber(read_record));
409 ASSERT_EQ(333, BlockCacheTraceHelper::GetBlockOffsetInFile(read_record));
410 // Read again should fail.
411 ASSERT_NOK(reader.ReadAccess(&read_record));
412 }
413}
414
415} // namespace ROCKSDB_NAMESPACE
416
417int main(int argc, char** argv) {
1e59de90 418 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
f67539c2
TL
419 ::testing::InitGoogleTest(&argc, argv);
420 return RUN_ALL_TESTS();
421}