| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- #include "db/blob/blob_file_builder.h"
- #include <cassert>
- #include <cinttypes>
- #include <string>
- #include <utility>
- #include <vector>
- #include "db/blob/blob_file_addition.h"
- #include "db/blob/blob_index.h"
- #include "db/blob/blob_log_format.h"
- #include "db/blob/blob_log_sequential_reader.h"
- #include "env/mock_env.h"
- #include "file/filename.h"
- #include "file/random_access_file_reader.h"
- #include "options/cf_options.h"
- #include "rocksdb/env.h"
- #include "rocksdb/file_checksum.h"
- #include "rocksdb/options.h"
- #include "test_util/sync_point.h"
- #include "test_util/testharness.h"
- #include "util/compression.h"
- #include "utilities/fault_injection_env.h"
- namespace ROCKSDB_NAMESPACE {
- class TestFileNumberGenerator {
- public:
- uint64_t operator()() { return ++next_file_number_; }
- private:
- uint64_t next_file_number_ = 1;
- };
- class BlobFileBuilderTest : public testing::Test {
- protected:
- BlobFileBuilderTest() {
- mock_env_.reset(MockEnv::Create(Env::Default()));
- fs_ = mock_env_->GetFileSystem().get();
- clock_ = mock_env_->GetSystemClock().get();
- write_options_.rate_limiter_priority = Env::IO_HIGH;
- }
- void VerifyBlobFile(uint64_t blob_file_number,
- const std::string& blob_file_path,
- uint32_t column_family_id,
- CompressionType blob_compression_type,
- const std::vector<std::pair<std::string, std::string>>&
- expected_key_value_pairs,
- const std::vector<std::string>& blob_indexes) {
- assert(expected_key_value_pairs.size() == blob_indexes.size());
- std::unique_ptr<FSRandomAccessFile> file;
- constexpr IODebugContext* dbg = nullptr;
- ASSERT_OK(
- fs_->NewRandomAccessFile(blob_file_path, file_options_, &file, dbg));
- std::unique_ptr<RandomAccessFileReader> file_reader(
- new RandomAccessFileReader(std::move(file), blob_file_path, clock_));
- constexpr Statistics* statistics = nullptr;
- BlobLogSequentialReader blob_log_reader(std::move(file_reader), clock_,
- statistics);
- BlobLogHeader header;
- ASSERT_OK(blob_log_reader.ReadHeader(&header));
- ASSERT_EQ(header.version, kVersion1);
- ASSERT_EQ(header.column_family_id, column_family_id);
- ASSERT_EQ(header.compression, blob_compression_type);
- ASSERT_FALSE(header.has_ttl);
- ASSERT_EQ(header.expiration_range, ExpirationRange());
- for (size_t i = 0; i < expected_key_value_pairs.size(); ++i) {
- BlobLogRecord record;
- uint64_t blob_offset = 0;
- ASSERT_OK(blob_log_reader.ReadRecord(
- &record, BlobLogSequentialReader::kReadHeaderKeyBlob, &blob_offset));
- // Check the contents of the blob file
- const auto& expected_key_value = expected_key_value_pairs[i];
- const auto& key = expected_key_value.first;
- const auto& value = expected_key_value.second;
- ASSERT_EQ(record.key_size, key.size());
- ASSERT_EQ(record.value_size, value.size());
- ASSERT_EQ(record.expiration, 0);
- ASSERT_EQ(record.key, key);
- ASSERT_EQ(record.value, value);
- // Make sure the blob reference returned by the builder points to the
- // right place
- BlobIndex blob_index;
- ASSERT_OK(blob_index.DecodeFrom(blob_indexes[i]));
- ASSERT_FALSE(blob_index.IsInlined());
- ASSERT_FALSE(blob_index.HasTTL());
- ASSERT_EQ(blob_index.file_number(), blob_file_number);
- ASSERT_EQ(blob_index.offset(), blob_offset);
- ASSERT_EQ(blob_index.size(), value.size());
- }
- BlobLogFooter footer;
- ASSERT_OK(blob_log_reader.ReadFooter(&footer));
- ASSERT_EQ(footer.blob_count, expected_key_value_pairs.size());
- ASSERT_EQ(footer.expiration_range, ExpirationRange());
- }
- std::unique_ptr<Env> mock_env_;
- FileSystem* fs_;
- SystemClock* clock_;
- FileOptions file_options_;
- WriteOptions write_options_;
- };
- TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
- // Build a single blob file
- constexpr size_t number_of_blobs = 10;
- constexpr size_t key_size = 1;
- constexpr size_t value_size = 4;
- constexpr size_t value_offset = 1234;
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(),
- "BlobFileBuilderTest_BuildAndCheckOneFile"),
- 0);
- options.enable_blob_files = true;
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
- number_of_blobs);
- std::vector<std::string> blob_indexes(number_of_blobs);
- for (size_t i = 0; i < number_of_blobs; ++i) {
- auto& expected_key_value = expected_key_value_pairs[i];
- auto& key = expected_key_value.first;
- key = std::to_string(i);
- assert(key.size() == key_size);
- auto& value = expected_key_value.second;
- value = std::to_string(i + value_offset);
- assert(value.size() == value_size);
- auto& blob_index = blob_indexes[i];
- ASSERT_OK(builder.Add(key, value, &blob_index));
- ASSERT_FALSE(blob_index.empty());
- }
- ASSERT_OK(builder.Finish());
- // Check the metadata generated
- constexpr uint64_t blob_file_number = 2;
- ASSERT_EQ(blob_file_paths.size(), 1);
- const std::string& blob_file_path = blob_file_paths[0];
- ASSERT_EQ(
- blob_file_path,
- BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
- ASSERT_EQ(blob_file_additions.size(), 1);
- const auto& blob_file_addition = blob_file_additions[0];
- ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
- ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), number_of_blobs);
- ASSERT_EQ(
- blob_file_addition.GetTotalBlobBytes(),
- number_of_blobs * (BlobLogRecord::kHeaderSize + key_size + value_size));
- // Verify the contents of the new blob file as well as the blob references
- VerifyBlobFile(blob_file_number, blob_file_path, column_family_id,
- kNoCompression, expected_key_value_pairs, blob_indexes);
- }
- TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
- // Build multiple blob files: file size limit is set to the size of a single
- // value, so each blob ends up in a file of its own
- constexpr size_t number_of_blobs = 10;
- constexpr size_t key_size = 1;
- constexpr size_t value_size = 10;
- constexpr size_t value_offset = 1234567890;
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(),
- "BlobFileBuilderTest_BuildAndCheckMultipleFiles"),
- 0);
- options.enable_blob_files = true;
- options.blob_file_size = value_size;
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
- number_of_blobs);
- std::vector<std::string> blob_indexes(number_of_blobs);
- for (size_t i = 0; i < number_of_blobs; ++i) {
- auto& expected_key_value = expected_key_value_pairs[i];
- auto& key = expected_key_value.first;
- key = std::to_string(i);
- assert(key.size() == key_size);
- auto& value = expected_key_value.second;
- value = std::to_string(i + value_offset);
- assert(value.size() == value_size);
- auto& blob_index = blob_indexes[i];
- ASSERT_OK(builder.Add(key, value, &blob_index));
- ASSERT_FALSE(blob_index.empty());
- }
- ASSERT_OK(builder.Finish());
- // Check the metadata generated
- ASSERT_EQ(blob_file_paths.size(), number_of_blobs);
- ASSERT_EQ(blob_file_additions.size(), number_of_blobs);
- for (size_t i = 0; i < number_of_blobs; ++i) {
- const uint64_t blob_file_number = i + 2;
- ASSERT_EQ(blob_file_paths[i],
- BlobFileName(immutable_options.cf_paths.front().path,
- blob_file_number));
- const auto& blob_file_addition = blob_file_additions[i];
- ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
- ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
- ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(),
- BlobLogRecord::kHeaderSize + key_size + value_size);
- }
- // Verify the contents of the new blob files as well as the blob references
- for (size_t i = 0; i < number_of_blobs; ++i) {
- std::vector<std::pair<std::string, std::string>> expected_key_value_pair{
- expected_key_value_pairs[i]};
- std::vector<std::string> blob_index{blob_indexes[i]};
- VerifyBlobFile(i + 2, blob_file_paths[i], column_family_id, kNoCompression,
- expected_key_value_pair, blob_index);
- }
- }
- TEST_F(BlobFileBuilderTest, InlinedValues) {
- // All values are below the min_blob_size threshold; no blob files get written
- constexpr size_t number_of_blobs = 10;
- constexpr size_t key_size = 1;
- constexpr size_t value_size = 10;
- constexpr size_t value_offset = 1234567890;
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(),
- "BlobFileBuilderTest_InlinedValues"),
- 0);
- options.enable_blob_files = true;
- options.min_blob_size = 1024;
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- for (size_t i = 0; i < number_of_blobs; ++i) {
- const std::string key = std::to_string(i);
- assert(key.size() == key_size);
- const std::string value = std::to_string(i + value_offset);
- assert(value.size() == value_size);
- std::string blob_index;
- ASSERT_OK(builder.Add(key, value, &blob_index));
- ASSERT_TRUE(blob_index.empty());
- }
- ASSERT_OK(builder.Finish());
- // Check the metadata generated
- ASSERT_TRUE(blob_file_paths.empty());
- ASSERT_TRUE(blob_file_additions.empty());
- }
- TEST_F(BlobFileBuilderTest, Compression) {
- // Build a blob file with a compressed blob
- if (!Snappy_Supported()) {
- return;
- }
- constexpr size_t key_size = 1;
- constexpr size_t value_size = 100;
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(), "BlobFileBuilderTest_Compression"),
- 0);
- options.enable_blob_files = true;
- options.blob_compression_type = kSnappyCompression;
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- const std::string key("1");
- const std::string uncompressed_value(value_size, 'x');
- std::string blob_index;
- ASSERT_OK(builder.Add(key, uncompressed_value, &blob_index));
- ASSERT_FALSE(blob_index.empty());
- ASSERT_OK(builder.Finish());
- // Check the metadata generated
- constexpr uint64_t blob_file_number = 2;
- ASSERT_EQ(blob_file_paths.size(), 1);
- const std::string& blob_file_path = blob_file_paths[0];
- ASSERT_EQ(
- blob_file_path,
- BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
- ASSERT_EQ(blob_file_additions.size(), 1);
- const auto& blob_file_addition = blob_file_additions[0];
- ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
- ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
- CompressionOptions opts;
- CompressionContext context(kSnappyCompression, opts);
- CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
- kSnappyCompression);
- std::string compressed_value;
- ASSERT_TRUE(Snappy_Compress(info, uncompressed_value.data(),
- uncompressed_value.size(), &compressed_value));
- ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(),
- BlobLogRecord::kHeaderSize + key_size + compressed_value.size());
- // Verify the contents of the new blob file as well as the blob reference
- std::vector<std::pair<std::string, std::string>> expected_key_value_pairs{
- {key, compressed_value}};
- std::vector<std::string> blob_indexes{blob_index};
- VerifyBlobFile(blob_file_number, blob_file_path, column_family_id,
- kSnappyCompression, expected_key_value_pairs, blob_indexes);
- }
- TEST_F(BlobFileBuilderTest, CompressionError) {
- // Simulate an error during compression
- if (!Snappy_Supported()) {
- return;
- }
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(),
- "BlobFileBuilderTest_CompressionError"),
- 0);
- options.enable_blob_files = true;
- options.blob_compression_type = kSnappyCompression;
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- SyncPoint::GetInstance()->SetCallBack("CompressData:TamperWithReturnValue",
- [](void* arg) {
- bool* ret = static_cast<bool*>(arg);
- *ret = false;
- });
- SyncPoint::GetInstance()->EnableProcessing();
- constexpr char key[] = "1";
- constexpr char value[] = "deadbeef";
- std::string blob_index;
- ASSERT_TRUE(builder.Add(key, value, &blob_index).IsCorruption());
- SyncPoint::GetInstance()->DisableProcessing();
- SyncPoint::GetInstance()->ClearAllCallBacks();
- constexpr uint64_t blob_file_number = 2;
- ASSERT_EQ(blob_file_paths.size(), 1);
- ASSERT_EQ(
- blob_file_paths[0],
- BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
- ASSERT_TRUE(blob_file_additions.empty());
- }
- TEST_F(BlobFileBuilderTest, Checksum) {
- // Build a blob file with checksum
- class DummyFileChecksumGenerator : public FileChecksumGenerator {
- public:
- void Update(const char* /* data */, size_t /* n */) override {}
- void Finalize() override {}
- std::string GetChecksum() const override { return std::string("dummy"); }
- const char* Name() const override { return "DummyFileChecksum"; }
- };
- class DummyFileChecksumGenFactory : public FileChecksumGenFactory {
- public:
- std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
- const FileChecksumGenContext& /* context */) override {
- return std::unique_ptr<FileChecksumGenerator>(
- new DummyFileChecksumGenerator);
- }
- const char* Name() const override { return "DummyFileChecksumGenFactory"; }
- };
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(), "BlobFileBuilderTest_Checksum"),
- 0);
- options.enable_blob_files = true;
- options.file_checksum_gen_factory =
- std::make_shared<DummyFileChecksumGenFactory>();
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- const std::string key("1");
- const std::string value("deadbeef");
- std::string blob_index;
- ASSERT_OK(builder.Add(key, value, &blob_index));
- ASSERT_FALSE(blob_index.empty());
- ASSERT_OK(builder.Finish());
- // Check the metadata generated
- constexpr uint64_t blob_file_number = 2;
- ASSERT_EQ(blob_file_paths.size(), 1);
- const std::string& blob_file_path = blob_file_paths[0];
- ASSERT_EQ(
- blob_file_path,
- BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
- ASSERT_EQ(blob_file_additions.size(), 1);
- const auto& blob_file_addition = blob_file_additions[0];
- ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
- ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
- ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(),
- BlobLogRecord::kHeaderSize + key.size() + value.size());
- ASSERT_EQ(blob_file_addition.GetChecksumMethod(), "DummyFileChecksum");
- ASSERT_EQ(blob_file_addition.GetChecksumValue(), "dummy");
- // Verify the contents of the new blob file as well as the blob reference
- std::vector<std::pair<std::string, std::string>> expected_key_value_pairs{
- {key, value}};
- std::vector<std::string> blob_indexes{blob_index};
- VerifyBlobFile(blob_file_number, blob_file_path, column_family_id,
- kNoCompression, expected_key_value_pairs, blob_indexes);
- }
- class BlobFileBuilderIOErrorTest
- : public testing::Test,
- public testing::WithParamInterface<std::string> {
- protected:
- BlobFileBuilderIOErrorTest() : sync_point_(GetParam()) {
- mock_env_.reset(MockEnv::Create(Env::Default()));
- fs_ = mock_env_->GetFileSystem().get();
- write_options_.rate_limiter_priority = Env::IO_HIGH;
- }
- std::unique_ptr<Env> mock_env_;
- FileSystem* fs_;
- FileOptions file_options_;
- WriteOptions write_options_;
- std::string sync_point_;
- };
- INSTANTIATE_TEST_CASE_P(
- BlobFileBuilderTest, BlobFileBuilderIOErrorTest,
- ::testing::ValuesIn(std::vector<std::string>{
- "BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile",
- "BlobFileBuilder::OpenBlobFileIfNeeded:WriteHeader",
- "BlobFileBuilder::WriteBlobToFile:AddRecord",
- "BlobFileBuilder::WriteBlobToFile:AppendFooter"}));
- TEST_P(BlobFileBuilderIOErrorTest, IOError) {
- // Simulate an I/O error during the specified step of Add()
- // Note: blob_file_size will be set to value_size in order for the first blob
- // to trigger close
- constexpr size_t value_size = 8;
- Options options;
- options.cf_paths.emplace_back(
- test::PerThreadDBPath(mock_env_.get(),
- "BlobFileBuilderIOErrorTest_IOError"),
- 0);
- options.enable_blob_files = true;
- options.blob_file_size = value_size;
- options.env = mock_env_.get();
- ImmutableOptions immutable_options(options);
- MutableCFOptions mutable_cf_options(options);
- constexpr int job_id = 1;
- constexpr uint32_t column_family_id = 123;
- constexpr char column_family_name[] = "foobar";
- constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
- std::vector<std::string> blob_file_paths;
- std::vector<BlobFileAddition> blob_file_additions;
- BlobFileBuilder builder(
- TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
- &file_options_, &write_options_, "" /*db_id*/, "" /*db_session_id*/,
- job_id, column_family_id, column_family_name, write_hint,
- nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
- BlobFileCreationReason::kFlush, &blob_file_paths, &blob_file_additions);
- SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
- Status* const s = static_cast<Status*>(arg);
- assert(s);
- (*s) = Status::IOError(sync_point_);
- });
- SyncPoint::GetInstance()->EnableProcessing();
- constexpr char key[] = "1";
- constexpr char value[] = "deadbeef";
- std::string blob_index;
- ASSERT_TRUE(builder.Add(key, value, &blob_index).IsIOError());
- SyncPoint::GetInstance()->DisableProcessing();
- SyncPoint::GetInstance()->ClearAllCallBacks();
- if (sync_point_ == "BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile") {
- ASSERT_TRUE(blob_file_paths.empty());
- } else {
- constexpr uint64_t blob_file_number = 2;
- ASSERT_EQ(blob_file_paths.size(), 1);
- ASSERT_EQ(blob_file_paths[0],
- BlobFileName(immutable_options.cf_paths.front().path,
- blob_file_number));
- }
- ASSERT_TRUE(blob_file_additions.empty());
- }
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|