mirror of https://github.com/facebook/rocksdb.git
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:
parent
2c02a9b76f
commit
abd6751aba
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Fixed a bug where wrong padded bytes are used to generate file checksum and `DataVerificationInfo::checksum` upon file creation
|
Loading…
Reference in New Issue