Fix wrong padded bytes being used to generate file checksum (#12598)

Summary:
**Context/Summary:**

https://github.com/facebook/rocksdb/pull/12542 introduced a bug where wrong padded bytes used to generate file checksum if flush happens during padding. This PR fixed it along with an existing same bug for `perform_data_verification_=true`.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12598

Test Plan:
- New UT that failed before this fix (`db->VerifyFileChecksums: ...Corruption:  ...file checksum mismatch`) and passes after
- Benchmark
```
TEST_TMPDIR=/dev/shm  ./db_bench --benchmarks=fillseq[-X300] --num=100000 --block_align=1 --compression_type=none
```
Pre-PR:
fillseq [AVG    300 runs] : 421334 (± 4126) ops/sec;   46.6 (± 0.5) MB/sec
Post-PR: (no regression observed but a slight improvement)
fillseq [AVG    300 runs] : 425768 (± 4309) ops/sec;   47.1 (± 0.5) MB/sec

Reviewed By: ajkr, anand1976

Differential Revision: D56725688

Pulled By: hx235

fbshipit-source-id: c1a700a95def8c65c0a21e44f8c1966164925ad5
This commit is contained in:
Hui Xiao 2024-04-30 15:38:53 -07:00 committed by Facebook GitHub Bot
parent 2c02a9b76f
commit abd6751aba
3 changed files with 75 additions and 10 deletions

View File

@ -205,17 +205,24 @@ IOStatus WritableFileWriter::Pad(const IOOptions& opts,
assert(pad_bytes < kDefaultPageSize);
size_t left = pad_bytes;
size_t cap = buf_.Capacity() - buf_.CurrentSize();
const size_t pad_start = buf_.CurrentSize();
// Assume pad_bytes is small compared to buf_ capacity. So we always
// use buf_ rather than write directly to file in certain cases like
// Append() does.
size_t actual_pad_bytes = 0;
while (left) {
size_t append_bytes = std::min(cap, left);
buf_.PadWith(append_bytes, 0);
left -= append_bytes;
actual_pad_bytes += append_bytes;
Slice data(buf_.BufferStart() + buf_.CurrentSize() - append_bytes,
append_bytes);
UpdateFileChecksum(data);
if (perform_data_verification_) {
buffered_data_crc32c_checksum_ = crc32c::Extend(
buffered_data_crc32c_checksum_,
buf_.BufferStart() + buf_.CurrentSize() - append_bytes, append_bytes);
}
if (left > 0) {
IOStatus s = Flush(io_options);
if (!s.ok()) {
@ -229,13 +236,6 @@ IOStatus WritableFileWriter::Pad(const IOOptions& opts,
uint64_t cur_size = filesize_.load(std::memory_order_acquire);
filesize_.store(cur_size + pad_bytes, std::memory_order_release);
Slice data(buf_.BufferStart() + pad_start, actual_pad_bytes);
UpdateFileChecksum(data);
if (perform_data_verification_) {
buffered_data_crc32c_checksum_ =
crc32c::Extend(buffered_data_crc32c_checksum_,
buf_.BufferStart() + pad_start, actual_pad_bytes);
}
return IOStatus::OK();
}

View File

@ -5541,6 +5541,70 @@ TEST_P(BlockBasedTableTest, FixBlockAlignMismatchedFileChecksums) {
delete db;
}
class NoBufferAlignmenttWritableFile : public FSWritableFileOwnerWrapper {
public:
explicit NoBufferAlignmenttWritableFile(
std::unique_ptr<FSWritableFile>&& file)
: FSWritableFileOwnerWrapper(std::move(file)) {}
size_t GetRequiredBufferAlignment() const override { return 1; }
};
class NoBufferAlignmenttWritableFileFileSystem : public FileSystemWrapper {
public:
explicit NoBufferAlignmenttWritableFileFileSystem(
const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {}
static const char* kClassName() {
return "NoBufferAlignmenttWritableFileFileSystem";
}
const char* Name() const override { return kClassName(); }
IOStatus NewWritableFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
IOStatus s = target()->NewWritableFile(fname, file_opts, result, dbg);
EXPECT_OK(s);
result->reset(new NoBufferAlignmenttWritableFile(std::move(*result)));
return s;
}
};
TEST_P(BlockBasedTableTest,
FixBlockAlignFlushDuringPadMismatchedFileChecksums) {
Options options;
options.create_if_missing = true;
options.compression = kNoCompression;
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
// To force flush during pad by enforcing a small buffer size
options.writable_file_max_buffer_size = 1;
// To help enforce a small buffer size by removing buffer alignment
Env* raw_env = Env::Default();
std::shared_ptr<NoBufferAlignmenttWritableFileFileSystem> fs =
std::make_shared<NoBufferAlignmenttWritableFileFileSystem>(
raw_env->GetFileSystem());
std::unique_ptr<Env> env(new CompositeEnvWrapper(raw_env, fs));
options.env = env.get();
BlockBasedTableOptions bbto;
bbto.block_align = true;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
const std::string kDBPath = test::PerThreadDBPath(
"block_align_flush_during_flush_verify_file_checksums");
ASSERT_OK(DestroyDB(kDBPath, options));
DB* db;
ASSERT_OK(DB::Open(options, kDBPath, &db));
ASSERT_OK(db->Put(WriteOptions(), "k1", "k2"));
ASSERT_OK(db->Flush(FlushOptions()));
// Before the fix, VerifyFileChecksums() will fail as incorrect padded bytes
// were used to generate checksum upon file creation
ASSERT_OK(db->VerifyFileChecksums(ReadOptions()));
delete db;
}
TEST_P(BlockBasedTableTest, PropertiesBlockRestartPointTest) {
BlockBasedTableOptions bbto = GetBlockBasedTableOptions();
bbto.block_align = true;

View File

@ -0,0 +1 @@
Fixed a bug where wrong padded bytes are used to generate file checksum and `DataVerificationInfo::checksum` upon file creation