block_cache_tracer_test.cc 15 KB

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