From 9d968febd407bf53797d0d7d6fd7a42901cebd0c Mon Sep 17 00:00:00 2001 From: Evan Jones Date: Tue, 30 Dec 2025 14:40:04 -0500 Subject: [PATCH] ldb dump_wal: add LOG_DATA, TIMED_PUT, PUT_BLOB_INDEX Previously, LOG_DATA would be ignored and would print like an empty record, and TIMED_PUT and PUT_BLOB_INDEX would cause ldb to exit with an unimplemented error. Add a unit test to cover these cases. --- tools/ldb_cmd.cc | 19 ++++++++++++++ tools/ldb_cmd_test.cc | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/tools/ldb_cmd.cc b/tools/ldb_cmd.cc index 328f7d875414..b6ba0721a24e 100644 --- a/tools/ldb_cmd.cc +++ b/tools/ldb_cmd.cc @@ -2942,6 +2942,20 @@ class InMemoryHandler : public WriteBatch::Handler { return Status::OK(); } + Status TimedPutCF(uint32_t cf, const Slice& key, const Slice& value, + uint64_t write_time) override { + row_ << "TIMED_PUT(" << cf << ", " << write_time << ") : "; + commonPutMerge(cf, key, value); + return Status::OK(); + } + + Status PutBlobIndexCF(uint32_t cf, const Slice& key, + const Slice& value) override { + row_ << "PUT_BLOB_INDEX(" << cf << ") : "; + commonPutMerge(cf, key, value); + return Status::OK(); + } + Status MergeCF(uint32_t cf, const Slice& key, const Slice& value) override { row_ << "MERGE(" << cf << ") : "; commonPutMerge(cf, key, value); @@ -2973,6 +2987,11 @@ class InMemoryHandler : public WriteBatch::Handler { return Status::OK(); } + void LogData(const Slice& blob) override { + row_ << "LOG_DATA : "; + row_ << LDBCommand::StringToHex(blob.ToString()) << " "; + } + Status MarkBeginPrepare(bool unprepare) override { row_ << "BEGIN_PREPARE("; row_ << (unprepare ? "true" : "false") << ") "; diff --git a/tools/ldb_cmd_test.cc b/tools/ldb_cmd_test.cc index 6943780f74cc..3c5083b8ee86 100644 --- a/tools/ldb_cmd_test.cc +++ b/tools/ldb_cmd_test.cc @@ -1255,6 +1255,66 @@ TEST_F(LdbCmdTest, CustomComparator) { LDBCommandRunner::RunCommand(4, argv, opts, LDBOptions(), &cfds)); } +TEST_F(LdbCmdTest, DumpWal) { + Env* base_env = TryLoadCustomOrDefaultEnv(); + std::unique_ptr env(NewMemEnv(base_env)); + Options opts; + opts.env = env.get(); + opts.create_if_missing = true; + + std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); + + DB* db = nullptr; + ASSERT_OK(DB::Open(opts, dbname, &db)); + + // PutLogData record + WriteBatch batch; + ASSERT_OK(batch.PutLogData("xxx")); + WriteOptions wopts; + ASSERT_OK(db->Write(wopts, &batch)); + batch.Clear(); + + // PutBlobIndex record copied from db_blob_basic_test.cc + std::string blob_index; + constexpr uint64_t blob_file_number = 1000; + constexpr uint64_t offset = 1234; + constexpr uint64_t size = 5678; + BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size, + kNoCompression); + constexpr uint64_t column_family_id = 0; + ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, column_family_id, + "blob_index_key", blob_index)); + ASSERT_OK(db->Write(wopts, &batch)); + batch.Clear(); + + // TimedPut record + constexpr uint64_t write_unix_time = 1767123301; // 2025-12-30T19:35:01Z + ASSERT_OK(batch.TimedPut(nullptr, "timed_put_key", "v", write_unix_time)); + ASSERT_OK(db->Write(wopts, &batch)); + batch.Clear(); + + ASSERT_OK(db->Close()); + delete db; + + string walfile_arg = string("--walfile=") + dbname; + const char* const argv[] = {"./ldb", "dump_wal", walfile_arg.c_str()}; + static const size_t argc = sizeof(argv) / sizeof(*argv); + + // capture cout while running the command + std::stringstream captured_cout; + std::streambuf* original_cout_buffer = std::cout.rdbuf(captured_cout.rdbuf()); + int result = + LDBCommandRunner::RunCommand(argc, argv, opts, LDBOptions(), nullptr); + std::cout.rdbuf(original_cout_buffer); + + // check the results of running dump_wal: log data must be included + string captured_output = captured_cout.str(); + ASSERT_EQ(0, result) << "ldb output:\n\n" << captured_output; + ASSERT_NE(std::string::npos, captured_output.find("LOG_DATA : 0x787878")) + << "ldb output:\n\n" + << captured_output; +} + } // namespace ROCKSDB_NAMESPACE int main(int argc, char** argv) {