Explicitly free allocated buffer when status is not ok (#6903)

Summary:
Currently we rely on `BlockContents` to implicitly free the allocated scratch buffer, but when IO error happens, it doesn't make sense to construct the `BlockContents` which might be corrupted. In the stress test, we find that `assert(req.result.size() == block_size(handle));` fails because of potential IO errors.

In this PR, we explicitly free the scratch buffer on error without constructing `BlockContents`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/6903

Test Plan: watch stress test

Reviewed By: anand1976

Differential Revision: D21823869

Pulled By: cheng-chang

fbshipit-source-id: 5603fc80e9bf3f44a9d7250ddebd871afe1eb89f
main
Cheng Chang 5 years ago committed by Facebook GitHub Bot
parent 038e02d8d9
commit bcb9e41080
  1. 62
      table/block_based/block_based_table_reader.cc

@ -1727,39 +1727,43 @@ void BlockBasedTable::RetrieveMultipleBlocks(
} }
BlockContents raw_block_contents; BlockContents raw_block_contents;
if (!use_shared_buffer) { if (s.ok()) {
// We allocated a buffer for this block. Give ownership of it to if (!use_shared_buffer) {
// BlockContents so it can free the memory // We allocated a buffer for this block. Give ownership of it to
assert(req.result.data() == req.scratch); // BlockContents so it can free the memory
assert(req.result.size() == block_size(handle)); assert(req.result.data() == req.scratch);
assert(req_offset == 0); assert(req.result.size() == block_size(handle));
std::unique_ptr<char[]> raw_block(req.scratch); assert(req_offset == 0);
raw_block_contents = BlockContents(std::move(raw_block), handle.size()); std::unique_ptr<char[]> raw_block(req.scratch);
} else { raw_block_contents = BlockContents(std::move(raw_block), handle.size());
// We used the scratch buffer or direct io buffer } else {
// which are shared by the blocks. // We used the scratch buffer or direct io buffer
// raw_block_contents does not have the ownership. // which are shared by the blocks.
raw_block_contents = // raw_block_contents does not have the ownership.
BlockContents(Slice(req.result.data() + req_offset, handle.size())); raw_block_contents =
} BlockContents(Slice(req.result.data() + req_offset, handle.size()));
}
#ifndef NDEBUG #ifndef NDEBUG
raw_block_contents.is_raw_block = true; raw_block_contents.is_raw_block = true;
#endif #endif
if (s.ok() && options.verify_checksums) { if (options.verify_checksums) {
PERF_TIMER_GUARD(block_checksum_time); PERF_TIMER_GUARD(block_checksum_time);
const char* data = req.result.data(); const char* data = req.result.data();
uint32_t expected = uint32_t expected =
DecodeFixed32(data + req_offset + handle.size() + 1); DecodeFixed32(data + req_offset + handle.size() + 1);
// Since the scratch might be shared. the offset of the data block in // Since the scratch might be shared. the offset of the data block in
// the buffer might not be 0. req.result.data() only point to the // the buffer might not be 0. req.result.data() only point to the
// begin address of each read request, we need to add the offset // begin address of each read request, we need to add the offset
// in each read request. Checksum is stored in the block trailer, // in each read request. Checksum is stored in the block trailer,
// which is handle.size() + 1. // which is handle.size() + 1.
s = ROCKSDB_NAMESPACE::VerifyChecksum(footer.checksum(), s = ROCKSDB_NAMESPACE::VerifyChecksum(
data + req_offset, footer.checksum(), data + req_offset, handle.size() + 1, expected);
handle.size() + 1, expected); TEST_SYNC_POINT_CALLBACK("RetrieveMultipleBlocks:VerifyChecksum", &s);
TEST_SYNC_POINT_CALLBACK("RetrieveMultipleBlocks:VerifyChecksum", &s); }
} else if (!use_shared_buffer) {
// Free the allocated scratch buffer.
delete[] req.scratch;
} }
if (s.ok()) { if (s.ok()) {

Loading…
Cancel
Save