block_cache_tracer_test.cc 18 KB

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