/src/rocksdb/trace_replay/block_cache_tracer.cc
Line | Count | Source |
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" |
7 | | |
8 | | #include <cinttypes> |
9 | | #include <cstdio> |
10 | | #include <cstdlib> |
11 | | #include <string> |
12 | | |
13 | | #include "db/db_impl/db_impl.h" |
14 | | #include "db/dbformat.h" |
15 | | #include "rocksdb/slice.h" |
16 | | #include "rocksdb/trace_record.h" |
17 | | #include "util/coding.h" |
18 | | #include "util/hash.h" |
19 | | #include "util/string_util.h" |
20 | | |
21 | | namespace ROCKSDB_NAMESPACE { |
22 | | |
23 | | namespace { |
24 | | bool ShouldTrace(const Slice& block_key, |
25 | 0 | const BlockCacheTraceOptions& trace_options) { |
26 | 0 | if (trace_options.sampling_frequency == 0 || |
27 | 0 | trace_options.sampling_frequency == 1) { |
28 | 0 | return true; |
29 | 0 | } |
30 | | // We use spatial downsampling so that we have a complete access history for a |
31 | | // block. |
32 | 0 | return 0 == GetSliceRangedNPHash(block_key, trace_options.sampling_frequency); |
33 | 0 | } |
34 | | } // namespace |
35 | | |
36 | | const uint64_t kMicrosInSecond = 1000 * 1000; |
37 | | const uint64_t kSecondInMinute = 60; |
38 | | const uint64_t kSecondInHour = 3600; |
39 | | const std::string BlockCacheTraceHelper::kUnknownColumnFamilyName = |
40 | | "UnknownColumnFamily"; |
41 | | const uint64_t BlockCacheTraceRecord::kReservedGetId = 0; |
42 | | const uint64_t BlockCacheTraceHelper::kReservedGetId = 0; |
43 | | |
44 | | bool BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock( |
45 | 0 | TraceType block_type, TableReaderCaller caller) { |
46 | 0 | return (block_type == TraceType::kBlockTraceDataBlock) && |
47 | 0 | IsGetOrMultiGet(caller); |
48 | 0 | } |
49 | | |
50 | 0 | bool BlockCacheTraceHelper::IsGetOrMultiGet(TableReaderCaller caller) { |
51 | 0 | return caller == TableReaderCaller::kUserGet || |
52 | 0 | caller == TableReaderCaller::kUserMultiGet; |
53 | 0 | } |
54 | | |
55 | 0 | bool BlockCacheTraceHelper::IsUserAccess(TableReaderCaller caller) { |
56 | 0 | return caller == TableReaderCaller::kUserGet || |
57 | 0 | caller == TableReaderCaller::kUserMultiGet || |
58 | 0 | caller == TableReaderCaller::kUserIterator || |
59 | 0 | caller == TableReaderCaller::kUserApproximateSize || |
60 | 0 | caller == TableReaderCaller::kUserVerifyChecksum; |
61 | 0 | } |
62 | | |
63 | | std::string BlockCacheTraceHelper::ComputeRowKey( |
64 | 0 | const BlockCacheTraceRecord& access) { |
65 | 0 | if (!IsGetOrMultiGet(access.caller)) { |
66 | 0 | return ""; |
67 | 0 | } |
68 | 0 | Slice key = ExtractUserKey(access.referenced_key); |
69 | 0 | return std::to_string(access.sst_fd_number) + "_" + key.ToString(); |
70 | 0 | } |
71 | | |
72 | | uint64_t BlockCacheTraceHelper::GetTableId( |
73 | 0 | const BlockCacheTraceRecord& access) { |
74 | 0 | if (!IsGetOrMultiGet(access.caller) || access.referenced_key.size() < 4) { |
75 | 0 | return 0; |
76 | 0 | } |
77 | 0 | return static_cast<uint64_t>(DecodeFixed32(access.referenced_key.data())) + 1; |
78 | 0 | } |
79 | | |
80 | | uint64_t BlockCacheTraceHelper::GetSequenceNumber( |
81 | 0 | const BlockCacheTraceRecord& access) { |
82 | 0 | if (!IsGetOrMultiGet(access.caller)) { |
83 | 0 | return 0; |
84 | 0 | } |
85 | 0 | if (access.caller == TableReaderCaller::kUserMultiGet && |
86 | 0 | access.referenced_key.size() < 4) { |
87 | 0 | return 0; |
88 | 0 | } |
89 | 0 | return access.get_from_user_specified_snapshot |
90 | 0 | ? 1 + GetInternalKeySeqno(access.referenced_key) |
91 | 0 | : 0; |
92 | 0 | } |
93 | | |
94 | | uint64_t BlockCacheTraceHelper::GetBlockOffsetInFile( |
95 | 0 | const BlockCacheTraceRecord& access) { |
96 | 0 | Slice input(access.block_key); |
97 | 0 | uint64_t offset = 0; |
98 | 0 | while (true) { |
99 | 0 | uint64_t tmp = 0; |
100 | 0 | if (GetVarint64(&input, &tmp)) { |
101 | 0 | offset = tmp; |
102 | 0 | } else { |
103 | 0 | break; |
104 | 0 | } |
105 | 0 | } |
106 | 0 | return offset; |
107 | 0 | } |
108 | | |
109 | | BlockCacheTraceWriterImpl::BlockCacheTraceWriterImpl( |
110 | | SystemClock* clock, const BlockCacheTraceWriterOptions& trace_options, |
111 | | std::unique_ptr<TraceWriter>&& trace_writer) |
112 | 0 | : clock_(clock), |
113 | 0 | trace_options_(trace_options), |
114 | 0 | trace_writer_(std::move(trace_writer)) {} |
115 | | |
116 | | Status BlockCacheTraceWriterImpl::WriteBlockAccess( |
117 | | const BlockCacheTraceRecord& record, const Slice& block_key, |
118 | 0 | const Slice& cf_name, const Slice& referenced_key) { |
119 | 0 | uint64_t trace_file_size = trace_writer_->GetFileSize(); |
120 | 0 | if (trace_file_size > trace_options_.max_trace_file_size) { |
121 | 0 | return Status::OK(); |
122 | 0 | } |
123 | 0 | Trace trace; |
124 | 0 | trace.ts = record.access_timestamp; |
125 | 0 | trace.type = record.block_type; |
126 | 0 | PutLengthPrefixedSlice(&trace.payload, block_key); |
127 | 0 | PutFixed64(&trace.payload, record.block_size); |
128 | 0 | PutFixed64(&trace.payload, record.cf_id); |
129 | 0 | PutLengthPrefixedSlice(&trace.payload, cf_name); |
130 | 0 | PutFixed32(&trace.payload, record.level); |
131 | 0 | PutFixed64(&trace.payload, record.sst_fd_number); |
132 | 0 | trace.payload.push_back(record.caller); |
133 | 0 | trace.payload.push_back(record.is_cache_hit); |
134 | 0 | trace.payload.push_back(record.no_insert); |
135 | 0 | if (BlockCacheTraceHelper::IsGetOrMultiGet(record.caller)) { |
136 | 0 | PutFixed64(&trace.payload, record.get_id); |
137 | 0 | trace.payload.push_back(record.get_from_user_specified_snapshot); |
138 | 0 | PutLengthPrefixedSlice(&trace.payload, referenced_key); |
139 | 0 | } |
140 | 0 | if (BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(record.block_type, |
141 | 0 | record.caller)) { |
142 | 0 | PutFixed64(&trace.payload, record.referenced_data_size); |
143 | 0 | PutFixed64(&trace.payload, record.num_keys_in_block); |
144 | 0 | trace.payload.push_back(record.referenced_key_exist_in_block); |
145 | 0 | } |
146 | 0 | std::string encoded_trace; |
147 | 0 | TracerHelper::EncodeTrace(trace, &encoded_trace); |
148 | 0 | return trace_writer_->Write(encoded_trace); |
149 | 0 | } |
150 | | |
151 | 0 | Status BlockCacheTraceWriterImpl::WriteHeader() { |
152 | 0 | Trace trace; |
153 | 0 | trace.ts = clock_->NowMicros(); |
154 | 0 | trace.type = TraceType::kTraceBegin; |
155 | 0 | PutLengthPrefixedSlice(&trace.payload, kTraceMagic); |
156 | 0 | PutFixed32(&trace.payload, kMajorVersion); |
157 | 0 | PutFixed32(&trace.payload, kMinorVersion); |
158 | 0 | std::string encoded_trace; |
159 | 0 | TracerHelper::EncodeTrace(trace, &encoded_trace); |
160 | 0 | return trace_writer_->Write(encoded_trace); |
161 | 0 | } |
162 | | |
163 | | BlockCacheTraceReader::BlockCacheTraceReader( |
164 | | std::unique_ptr<TraceReader>&& reader) |
165 | 0 | : trace_reader_(std::move(reader)) {} |
166 | | |
167 | 0 | Status BlockCacheTraceReader::ReadHeader(BlockCacheTraceHeader* header) { |
168 | 0 | assert(header != nullptr); |
169 | 0 | std::string encoded_trace; |
170 | 0 | Status s = trace_reader_->Read(&encoded_trace); |
171 | 0 | if (!s.ok()) { |
172 | 0 | return s; |
173 | 0 | } |
174 | 0 | Trace trace; |
175 | 0 | s = TracerHelper::DecodeTrace(encoded_trace, &trace); |
176 | 0 | if (!s.ok()) { |
177 | 0 | return s; |
178 | 0 | } |
179 | 0 | header->start_time = trace.ts; |
180 | 0 | Slice enc_slice = Slice(trace.payload); |
181 | 0 | Slice magnic_number; |
182 | 0 | if (!GetLengthPrefixedSlice(&enc_slice, &magnic_number)) { |
183 | 0 | return Status::Corruption( |
184 | 0 | "Corrupted header in the trace file: Failed to read the magic number."); |
185 | 0 | } |
186 | 0 | if (magnic_number.ToString() != kTraceMagic) { |
187 | 0 | return Status::Corruption( |
188 | 0 | "Corrupted header in the trace file: Magic number does not match."); |
189 | 0 | } |
190 | 0 | if (!GetFixed32(&enc_slice, &header->rocksdb_major_version)) { |
191 | 0 | return Status::Corruption( |
192 | 0 | "Corrupted header in the trace file: Failed to read rocksdb major " |
193 | 0 | "version number."); |
194 | 0 | } |
195 | 0 | if (!GetFixed32(&enc_slice, &header->rocksdb_minor_version)) { |
196 | 0 | return Status::Corruption( |
197 | 0 | "Corrupted header in the trace file: Failed to read rocksdb minor " |
198 | 0 | "version number."); |
199 | 0 | } |
200 | | // We should have retrieved all information in the header. |
201 | 0 | if (!enc_slice.empty()) { |
202 | 0 | return Status::Corruption( |
203 | 0 | "Corrupted header in the trace file: The length of header is too " |
204 | 0 | "long."); |
205 | 0 | } |
206 | 0 | return Status::OK(); |
207 | 0 | } |
208 | | |
209 | 0 | Status BlockCacheTraceReader::ReadAccess(BlockCacheTraceRecord* record) { |
210 | 0 | assert(record); |
211 | 0 | std::string encoded_trace; |
212 | 0 | Status s = trace_reader_->Read(&encoded_trace); |
213 | 0 | if (!s.ok()) { |
214 | 0 | return s; |
215 | 0 | } |
216 | 0 | Trace trace; |
217 | 0 | s = TracerHelper::DecodeTrace(encoded_trace, &trace); |
218 | 0 | if (!s.ok()) { |
219 | 0 | return s; |
220 | 0 | } |
221 | 0 | record->access_timestamp = trace.ts; |
222 | 0 | record->block_type = trace.type; |
223 | 0 | Slice enc_slice = Slice(trace.payload); |
224 | |
|
225 | 0 | const unsigned int kCharSize = 1; |
226 | |
|
227 | 0 | Slice block_key; |
228 | 0 | if (!GetLengthPrefixedSlice(&enc_slice, &block_key)) { |
229 | 0 | return Status::Incomplete( |
230 | 0 | "Incomplete access record: Failed to read block key."); |
231 | 0 | } |
232 | 0 | record->block_key = block_key.ToString(); |
233 | 0 | if (!GetFixed64(&enc_slice, &record->block_size)) { |
234 | 0 | return Status::Incomplete( |
235 | 0 | "Incomplete access record: Failed to read block size."); |
236 | 0 | } |
237 | 0 | if (!GetFixed64(&enc_slice, &record->cf_id)) { |
238 | 0 | return Status::Incomplete( |
239 | 0 | "Incomplete access record: Failed to read column family ID."); |
240 | 0 | } |
241 | 0 | Slice cf_name; |
242 | 0 | if (!GetLengthPrefixedSlice(&enc_slice, &cf_name)) { |
243 | 0 | return Status::Incomplete( |
244 | 0 | "Incomplete access record: Failed to read column family name."); |
245 | 0 | } |
246 | 0 | record->cf_name = cf_name.ToString(); |
247 | 0 | if (!GetFixed32(&enc_slice, &record->level)) { |
248 | 0 | return Status::Incomplete( |
249 | 0 | "Incomplete access record: Failed to read level."); |
250 | 0 | } |
251 | 0 | if (!GetFixed64(&enc_slice, &record->sst_fd_number)) { |
252 | 0 | return Status::Incomplete( |
253 | 0 | "Incomplete access record: Failed to read SST file number."); |
254 | 0 | } |
255 | 0 | if (enc_slice.empty()) { |
256 | 0 | return Status::Incomplete( |
257 | 0 | "Incomplete access record: Failed to read caller."); |
258 | 0 | } |
259 | 0 | record->caller = static_cast<TableReaderCaller>(enc_slice[0]); |
260 | 0 | enc_slice.remove_prefix(kCharSize); |
261 | 0 | if (enc_slice.empty()) { |
262 | 0 | return Status::Incomplete( |
263 | 0 | "Incomplete access record: Failed to read is_cache_hit."); |
264 | 0 | } |
265 | 0 | record->is_cache_hit = static_cast<char>(enc_slice[0]); |
266 | 0 | enc_slice.remove_prefix(kCharSize); |
267 | 0 | if (enc_slice.empty()) { |
268 | 0 | return Status::Incomplete( |
269 | 0 | "Incomplete access record: Failed to read no_insert."); |
270 | 0 | } |
271 | 0 | record->no_insert = static_cast<char>(enc_slice[0]); |
272 | 0 | enc_slice.remove_prefix(kCharSize); |
273 | 0 | if (BlockCacheTraceHelper::IsGetOrMultiGet(record->caller)) { |
274 | 0 | if (!GetFixed64(&enc_slice, &record->get_id)) { |
275 | 0 | return Status::Incomplete( |
276 | 0 | "Incomplete access record: Failed to read the get id."); |
277 | 0 | } |
278 | 0 | if (enc_slice.empty()) { |
279 | 0 | return Status::Incomplete( |
280 | 0 | "Incomplete access record: Failed to read " |
281 | 0 | "get_from_user_specified_snapshot."); |
282 | 0 | } |
283 | 0 | record->get_from_user_specified_snapshot = static_cast<char>(enc_slice[0]); |
284 | 0 | enc_slice.remove_prefix(kCharSize); |
285 | 0 | Slice referenced_key; |
286 | 0 | if (!GetLengthPrefixedSlice(&enc_slice, &referenced_key)) { |
287 | 0 | return Status::Incomplete( |
288 | 0 | "Incomplete access record: Failed to read the referenced key."); |
289 | 0 | } |
290 | 0 | record->referenced_key = referenced_key.ToString(); |
291 | 0 | } |
292 | 0 | if (BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(record->block_type, |
293 | 0 | record->caller)) { |
294 | 0 | if (!GetFixed64(&enc_slice, &record->referenced_data_size)) { |
295 | 0 | return Status::Incomplete( |
296 | 0 | "Incomplete access record: Failed to read the referenced data size."); |
297 | 0 | } |
298 | 0 | if (!GetFixed64(&enc_slice, &record->num_keys_in_block)) { |
299 | 0 | return Status::Incomplete( |
300 | 0 | "Incomplete access record: Failed to read the number of keys in the " |
301 | 0 | "block."); |
302 | 0 | } |
303 | 0 | if (enc_slice.empty()) { |
304 | 0 | return Status::Incomplete( |
305 | 0 | "Incomplete access record: Failed to read " |
306 | 0 | "referenced_key_exist_in_block."); |
307 | 0 | } |
308 | 0 | record->referenced_key_exist_in_block = static_cast<char>(enc_slice[0]); |
309 | 0 | } |
310 | 0 | return Status::OK(); |
311 | 0 | } |
312 | | |
313 | 0 | BlockCacheHumanReadableTraceWriter::~BlockCacheHumanReadableTraceWriter() { |
314 | 0 | if (human_readable_trace_file_writer_) { |
315 | 0 | human_readable_trace_file_writer_->Flush().PermitUncheckedError(); |
316 | 0 | human_readable_trace_file_writer_->Close().PermitUncheckedError(); |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | Status BlockCacheHumanReadableTraceWriter::NewWritableFile( |
321 | | const std::string& human_readable_trace_file_path, |
322 | 0 | ROCKSDB_NAMESPACE::Env* env) { |
323 | 0 | if (human_readable_trace_file_path.empty()) { |
324 | 0 | return Status::InvalidArgument( |
325 | 0 | "The provided human_readable_trace_file_path is null."); |
326 | 0 | } |
327 | 0 | return env->NewWritableFile(human_readable_trace_file_path, |
328 | 0 | &human_readable_trace_file_writer_, EnvOptions()); |
329 | 0 | } |
330 | | |
331 | | Status BlockCacheHumanReadableTraceWriter::WriteHumanReadableTraceRecord( |
332 | | const BlockCacheTraceRecord& access, uint64_t block_id, |
333 | 0 | uint64_t get_key_id) { |
334 | 0 | if (!human_readable_trace_file_writer_) { |
335 | 0 | return Status::OK(); |
336 | 0 | } |
337 | 0 | int ret = snprintf( |
338 | 0 | trace_record_buffer_, sizeof(trace_record_buffer_), |
339 | 0 | "%" PRIu64 ",%" PRIu64 ",%u,%" PRIu64 ",%" PRIu64 ",%s,%" PRIu32 |
340 | 0 | ",%" PRIu64 ",%u,%u,%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%u,%u,%" PRIu64 |
341 | 0 | ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "\n", |
342 | 0 | access.access_timestamp, block_id, access.block_type, access.block_size, |
343 | 0 | access.cf_id, access.cf_name.c_str(), access.level, access.sst_fd_number, |
344 | 0 | access.caller, access.no_insert, access.get_id, get_key_id, |
345 | 0 | access.referenced_data_size, access.is_cache_hit, |
346 | 0 | access.referenced_key_exist_in_block, access.num_keys_in_block, |
347 | 0 | BlockCacheTraceHelper::GetTableId(access), |
348 | 0 | BlockCacheTraceHelper::GetSequenceNumber(access), |
349 | 0 | static_cast<uint64_t>(access.block_key.size()), |
350 | 0 | static_cast<uint64_t>(access.referenced_key.size()), |
351 | 0 | BlockCacheTraceHelper::GetBlockOffsetInFile(access)); |
352 | 0 | if (ret < 0) { |
353 | 0 | return Status::IOError("failed to format the output"); |
354 | 0 | } |
355 | 0 | std::string printout(trace_record_buffer_); |
356 | 0 | return human_readable_trace_file_writer_->Append(printout); |
357 | 0 | } |
358 | | |
359 | | BlockCacheHumanReadableTraceReader::BlockCacheHumanReadableTraceReader( |
360 | | const std::string& trace_file_path) |
361 | 0 | : BlockCacheTraceReader(/*trace_reader=*/nullptr) { |
362 | 0 | human_readable_trace_reader_.open(trace_file_path, std::ifstream::in); |
363 | 0 | } |
364 | | |
365 | 0 | BlockCacheHumanReadableTraceReader::~BlockCacheHumanReadableTraceReader() { |
366 | 0 | human_readable_trace_reader_.close(); |
367 | 0 | } |
368 | | |
369 | | Status BlockCacheHumanReadableTraceReader::ReadHeader( |
370 | 0 | BlockCacheTraceHeader* /*header*/) { |
371 | 0 | return Status::OK(); |
372 | 0 | } |
373 | | |
374 | | Status BlockCacheHumanReadableTraceReader::ReadAccess( |
375 | 0 | BlockCacheTraceRecord* record) { |
376 | 0 | std::string line; |
377 | 0 | if (!std::getline(human_readable_trace_reader_, line)) { |
378 | 0 | return Status::Incomplete("No more records to read."); |
379 | 0 | } |
380 | 0 | std::stringstream ss(line); |
381 | 0 | std::vector<std::string> record_strs; |
382 | 0 | while (ss.good()) { |
383 | 0 | std::string substr; |
384 | 0 | getline(ss, substr, ','); |
385 | 0 | record_strs.push_back(substr); |
386 | 0 | } |
387 | 0 | if (record_strs.size() != 21) { |
388 | 0 | return Status::Incomplete("Records format is wrong."); |
389 | 0 | } |
390 | | |
391 | 0 | record->access_timestamp = ParseUint64(record_strs[0]); |
392 | 0 | uint64_t block_key = ParseUint64(record_strs[1]); |
393 | 0 | record->block_type = static_cast<TraceType>(ParseUint64(record_strs[2])); |
394 | 0 | record->block_size = ParseUint64(record_strs[3]); |
395 | 0 | record->cf_id = ParseUint64(record_strs[4]); |
396 | 0 | record->cf_name = record_strs[5]; |
397 | 0 | record->level = static_cast<uint32_t>(ParseUint64(record_strs[6])); |
398 | 0 | record->sst_fd_number = ParseUint64(record_strs[7]); |
399 | 0 | record->caller = static_cast<TableReaderCaller>(ParseUint64(record_strs[8])); |
400 | 0 | record->no_insert = static_cast<char>(ParseUint64(record_strs[9])); |
401 | 0 | record->get_id = ParseUint64(record_strs[10]); |
402 | 0 | uint64_t get_key_id = ParseUint64(record_strs[11]); |
403 | |
|
404 | 0 | record->referenced_data_size = ParseUint64(record_strs[12]); |
405 | 0 | record->is_cache_hit = static_cast<char>(ParseUint64(record_strs[13])); |
406 | 0 | record->referenced_key_exist_in_block = |
407 | 0 | static_cast<char>(ParseUint64(record_strs[14])); |
408 | 0 | record->num_keys_in_block = ParseUint64(record_strs[15]); |
409 | 0 | uint64_t table_id = ParseUint64(record_strs[16]); |
410 | 0 | if (table_id > 0) { |
411 | | // Decrement since valid table id in the trace file equals traced table id |
412 | | // + 1. |
413 | 0 | table_id -= 1; |
414 | 0 | } |
415 | 0 | uint64_t get_sequence_number = ParseUint64(record_strs[17]); |
416 | 0 | if (get_sequence_number > 0) { |
417 | 0 | record->get_from_user_specified_snapshot = true; |
418 | | // Decrement since valid seq number in the trace file equals traced seq |
419 | | // number + 1. |
420 | 0 | get_sequence_number -= 1; |
421 | 0 | } |
422 | 0 | uint64_t block_key_size = ParseUint64(record_strs[18]); |
423 | 0 | uint64_t get_key_size = ParseUint64(record_strs[19]); |
424 | 0 | uint64_t block_offset = ParseUint64(record_strs[20]); |
425 | |
|
426 | 0 | std::string tmp_block_key; |
427 | 0 | PutVarint64(&tmp_block_key, block_key); |
428 | 0 | PutVarint64(&tmp_block_key, block_offset); |
429 | | // Append 1 until the size is the same as traced block key size. |
430 | 0 | while (record->block_key.size() < block_key_size - tmp_block_key.size()) { |
431 | 0 | record->block_key += "1"; |
432 | 0 | } |
433 | 0 | record->block_key += tmp_block_key; |
434 | |
|
435 | 0 | if (get_key_id != 0) { |
436 | 0 | std::string tmp_get_key; |
437 | 0 | PutFixed64(&tmp_get_key, get_key_id); |
438 | 0 | PutFixed64(&tmp_get_key, get_sequence_number << 8); |
439 | 0 | PutFixed32(&record->referenced_key, static_cast<uint32_t>(table_id)); |
440 | | // Append 1 until the size is the same as traced key size. |
441 | 0 | while (record->referenced_key.size() < get_key_size - tmp_get_key.size()) { |
442 | 0 | record->referenced_key += "1"; |
443 | 0 | } |
444 | 0 | record->referenced_key += tmp_get_key; |
445 | 0 | } |
446 | 0 | return Status::OK(); |
447 | 0 | } |
448 | | |
449 | 49.7k | BlockCacheTracer::BlockCacheTracer() { writer_.store(nullptr); } |
450 | | |
451 | 49.7k | BlockCacheTracer::~BlockCacheTracer() { EndTrace(); } |
452 | | |
453 | | Status BlockCacheTracer::StartTrace( |
454 | | const BlockCacheTraceOptions& trace_options, |
455 | 0 | std::unique_ptr<BlockCacheTraceWriter>&& trace_writer) { |
456 | 0 | InstrumentedMutexLock lock_guard(&trace_writer_mutex_); |
457 | 0 | if (writer_.load()) { |
458 | 0 | return Status::Busy(); |
459 | 0 | } |
460 | 0 | get_id_counter_.store(1); |
461 | 0 | trace_options_ = trace_options; |
462 | 0 | writer_.store(trace_writer.release()); |
463 | 0 | return writer_.load()->WriteHeader(); |
464 | 0 | } |
465 | | |
466 | 49.7k | void BlockCacheTracer::EndTrace() { |
467 | 49.7k | InstrumentedMutexLock lock_guard(&trace_writer_mutex_); |
468 | 49.7k | if (!writer_.load()) { |
469 | 49.7k | return; |
470 | 49.7k | } |
471 | 0 | delete writer_.load(); |
472 | 0 | writer_.store(nullptr); |
473 | 0 | } |
474 | | |
475 | | Status BlockCacheTracer::WriteBlockAccess(const BlockCacheTraceRecord& record, |
476 | | const Slice& block_key, |
477 | | const Slice& cf_name, |
478 | 0 | const Slice& referenced_key) { |
479 | 0 | if (!writer_.load() || !ShouldTrace(block_key, trace_options_)) { |
480 | 0 | return Status::OK(); |
481 | 0 | } |
482 | 0 | InstrumentedMutexLock lock_guard(&trace_writer_mutex_); |
483 | 0 | if (!writer_.load()) { |
484 | 0 | return Status::OK(); |
485 | 0 | } |
486 | 0 | return writer_.load()->WriteBlockAccess(record, block_key, cf_name, |
487 | 0 | referenced_key); |
488 | 0 | } |
489 | | |
490 | 0 | uint64_t BlockCacheTracer::NextGetId() { |
491 | 0 | if (!writer_.load(std::memory_order_relaxed)) { |
492 | 0 | return BlockCacheTraceHelper::kReservedGetId; |
493 | 0 | } |
494 | 0 | uint64_t prev_value = get_id_counter_.fetch_add(1); |
495 | 0 | if (prev_value == BlockCacheTraceHelper::kReservedGetId) { |
496 | | // fetch and add again. |
497 | 0 | return get_id_counter_.fetch_add(1); |
498 | 0 | } |
499 | 0 | return prev_value; |
500 | 0 | } |
501 | | |
502 | | std::unique_ptr<BlockCacheTraceWriter> NewBlockCacheTraceWriter( |
503 | | SystemClock* clock, const BlockCacheTraceWriterOptions& trace_options, |
504 | 0 | std::unique_ptr<TraceWriter>&& trace_writer) { |
505 | 0 | return std::unique_ptr<BlockCacheTraceWriter>(new BlockCacheTraceWriterImpl( |
506 | 0 | clock, trace_options, std::move(trace_writer))); |
507 | 0 | } |
508 | | |
509 | | } // namespace ROCKSDB_NAMESPACE |