From c72ee4531b288bf08b9414155fafb86cc4378fb4 Mon Sep 17 00:00:00 2001 From: Andrew Kryczka Date: Wed, 22 May 2024 15:34:37 -0700 Subject: [PATCH] Fix recycled WAL detection when wal_compression is enabled (#12643) Summary: I think the point of the `if (end_of_buffer_offset_ - buffer_.size() == 0)` was to only set `recycled_` when the first record was read. However, the condition was false when reading the first record when the WAL began with a `kSetCompressionType` record because we had already dropped the `kSetCompressionType` record from `buffer_`. To fix this, I used `first_record_read_` instead. Also, it was pretty confusing to treat the WAL as non-recycled when a recyclable record first appeared in a non-first record. I changed it to return an error if that happens. Pull Request resolved: https://github.com/facebook/rocksdb/pull/12643 Reviewed By: hx235 Differential Revision: D57238099 Pulled By: ajkr fbshipit-source-id: e20a2a0c9cf0c9510a7b6af463650a05d559239e --- db/log_reader.cc | 13 ++++--- db/log_test.cc | 36 +++++++++++++++++++ .../bug_fixes/fix_compressed_wal_recycling.md | 1 + 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 unreleased_history/bug_fixes/fix_compressed_wal_recycling.md diff --git a/db/log_reader.cc b/db/log_reader.cc index 110eb2c27c8..d6eefec05c5 100644 --- a/db/log_reader.cc +++ b/db/log_reader.cc @@ -487,9 +487,11 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size, type == kRecyclableUserDefinedTimestampSizeType); if (is_recyclable_type) { header_size = kRecyclableHeaderSize; - if (end_of_buffer_offset_ - buffer_.size() == 0) { - recycled_ = true; + if (first_record_read_ && !recycled_) { + // A recycled log should have started with a recycled record + return kBadRecord; } + recycled_ = true; // We need enough for the larger header if (buffer_.size() < static_cast(kRecyclableHeaderSize)) { int r = kEof; @@ -867,9 +869,12 @@ bool FragmentBufferedReader::TryReadFragment( int header_size = kHeaderSize; if ((type >= kRecyclableFullType && type <= kRecyclableLastType) || type == kRecyclableUserDefinedTimestampSizeType) { - if (end_of_buffer_offset_ - buffer_.size() == 0) { - recycled_ = true; + if (first_record_read_ && !recycled_) { + // A recycled log should have started with a recycled record + *fragment_type_or_err = kBadRecord; + return true; } + recycled_ = true; header_size = kRecyclableHeaderSize; while (buffer_.size() < static_cast(kRecyclableHeaderSize)) { size_t old_size = buffer_.size(); diff --git a/db/log_test.cc b/db/log_test.cc index 57b6f64faa3..51f88ac5b7d 100644 --- a/db/log_test.cc +++ b/db/log_test.cc @@ -1157,6 +1157,42 @@ TEST_P(CompressionLogTest, AlignedFragmentation) { ASSERT_EQ("EOF", Read()); } +TEST_P(CompressionLogTest, ChecksumMismatch) { + const CompressionType kCompressionType = std::get<2>(GetParam()); + const bool kCompressionEnabled = kCompressionType != kNoCompression; + const bool kRecyclableLog = (std::get<0>(GetParam()) != 0); + if (!StreamingCompressionTypeSupported(kCompressionType)) { + ROCKSDB_GTEST_SKIP("Test requires support for compression type"); + return; + } + ASSERT_OK(SetupTestEnv()); + + Write("foooooo"); + int header_len; + if (kRecyclableLog) { + header_len = kRecyclableHeaderSize; + } else { + header_len = kHeaderSize; + } + int compression_record_len; + if (kCompressionEnabled) { + compression_record_len = header_len + 4; + } else { + compression_record_len = 0; + } + IncrementByte(compression_record_len + header_len /* offset */, + 14 /* delta */); + + ASSERT_EQ("EOF", Read()); + if (!kRecyclableLog) { + ASSERT_GT(DroppedBytes(), 0U); + ASSERT_EQ("OK", MatchError("checksum mismatch")); + } else { + ASSERT_EQ(0U, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); + } +} + INSTANTIATE_TEST_CASE_P( Compression, CompressionLogTest, ::testing::Combine(::testing::Values(0, 1), ::testing::Bool(), diff --git a/unreleased_history/bug_fixes/fix_compressed_wal_recycling.md b/unreleased_history/bug_fixes/fix_compressed_wal_recycling.md new file mode 100644 index 00000000000..88c4b7706d4 --- /dev/null +++ b/unreleased_history/bug_fixes/fix_compressed_wal_recycling.md @@ -0,0 +1 @@ +* Fixed a false positive `Status::Corruption` reported when reopening a DB that used `DBOptions::recycle_log_file_num > 0` and `DBOptions::wal_compression != kNoCompression`.