Call ValidateOptions from SetOptions (#5368)

Summary:
Currently we validate options in DB::Open. However the validation step is missing when options are dynamically updated in ::SetOptions. The patch fixes that.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5368

Differential Revision: D15540101

Pulled By: maysamyabandeh

fbshipit-source-id: d27bbffd8f0252d1b50bcf59e0a70a278ed937f4
This commit is contained in:
Maysam Yabandeh 2019-06-03 19:47:02 -07:00 committed by Facebook Github Bot
parent 5851cb7fdb
commit ae05a83e19
12 changed files with 114 additions and 58 deletions

View File

@ -1148,13 +1148,60 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
} }
} }
Status ColumnFamilyData::ValidateOptions(
const DBOptions& db_options, const ColumnFamilyOptions& cf_options) {
Status s;
s = CheckCompressionSupported(cf_options);
if (s.ok() && db_options.allow_concurrent_memtable_write) {
s = CheckConcurrentWritesSupported(cf_options);
}
if (s.ok()) {
s = CheckCFPathsSupported(db_options, cf_options);
}
if (!s.ok()) {
return s;
}
if (cf_options.ttl > 0) {
if (db_options.max_open_files != -1) {
return Status::NotSupported(
"TTL is only supported when files are always "
"kept open (set max_open_files = -1). ");
}
if (cf_options.table_factory->Name() != BlockBasedTableFactory().Name()) {
return Status::NotSupported(
"TTL is only supported in Block-Based Table format. ");
}
}
if (cf_options.periodic_compaction_seconds > 0) {
if (db_options.max_open_files != -1) {
return Status::NotSupported(
"Periodic Compaction is only supported when files are always "
"kept open (set max_open_files = -1). ");
}
if (cf_options.table_factory->Name() != BlockBasedTableFactory().Name()) {
return Status::NotSupported(
"Periodic Compaction is only supported in "
"Block-Based Table format. ");
}
}
return s;
}
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
Status ColumnFamilyData::SetOptions( Status ColumnFamilyData::SetOptions(
const std::unordered_map<std::string, std::string>& options_map) { const DBOptions& db_options,
const std::unordered_map<std::string, std::string>& options_map) {
MutableCFOptions new_mutable_cf_options; MutableCFOptions new_mutable_cf_options;
Status s = Status s =
GetMutableOptionsFromStrings(mutable_cf_options_, options_map, GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
ioptions_.info_log, &new_mutable_cf_options); ioptions_.info_log, &new_mutable_cf_options);
if (s.ok()) {
ColumnFamilyOptions cf_options =
BuildColumnFamilyOptions(initial_cf_options_, new_mutable_cf_options);
s = ValidateOptions(db_options, cf_options);
}
if (s.ok()) { if (s.ok()) {
mutable_cf_options_ = new_mutable_cf_options; mutable_cf_options_ = new_mutable_cf_options;
mutable_cf_options_.RefreshDerivedOptions(ioptions_); mutable_cf_options_.RefreshDerivedOptions(ioptions_);

View File

@ -338,9 +338,13 @@ class ColumnFamilyData {
bool is_delete_range_supported() { return is_delete_range_supported_; } bool is_delete_range_supported() { return is_delete_range_supported_; }
// Validate CF options against DB options
static Status ValidateOptions(const DBOptions& db_options,
const ColumnFamilyOptions& cf_options);
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
// REQUIRES: DB mutex held // REQUIRES: DB mutex held
Status SetOptions( Status SetOptions(
const DBOptions& db_options,
const std::unordered_map<std::string, std::string>& options_map); const std::unordered_map<std::string, std::string>& options_map);
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE

View File

@ -848,8 +848,9 @@ Status DBImpl::SetOptions(
Status persist_options_status; Status persist_options_status;
SuperVersionContext sv_context(/* create_superversion */ true); SuperVersionContext sv_context(/* create_superversion */ true);
{ {
auto db_options = GetDBOptions();
InstrumentedMutexLock l(&mutex_); InstrumentedMutexLock l(&mutex_);
s = cfd->SetOptions(options_map); s = cfd->SetOptions(db_options, options_map);
if (s.ok()) { if (s.ok()) {
new_options = *cfd->GetLatestMutableCFOptions(); new_options = *cfd->GetLatestMutableCFOptions();
// Append new version to recompute compaction score. // Append new version to recompute compaction score.
@ -912,6 +913,25 @@ Status DBImpl::SetDBOptions(
InstrumentedMutexLock l(&mutex_); InstrumentedMutexLock l(&mutex_);
s = GetMutableDBOptionsFromStrings(mutable_db_options_, options_map, s = GetMutableDBOptionsFromStrings(mutable_db_options_, options_map,
&new_options); &new_options);
if (new_options.bytes_per_sync == 0) {
new_options.bytes_per_sync = 1024 * 1024;
}
DBOptions new_db_options =
BuildDBOptions(immutable_db_options_, new_options);
if (s.ok()) {
s = ValidateOptions(new_db_options);
}
if (s.ok()) {
for (auto c : *versions_->GetColumnFamilySet()) {
if (!c->IsDropped()) {
auto cf_options = c->GetLatestCFOptions();
s = ColumnFamilyData::ValidateOptions(new_db_options, cf_options);
if (!s.ok()) {
break;
}
}
}
}
if (s.ok()) { if (s.ok()) {
if (new_options.max_background_compactions > if (new_options.max_background_compactions >
mutable_db_options_.max_background_compactions) { mutable_db_options_.max_background_compactions) {
@ -956,15 +976,12 @@ Status DBImpl::SetDBOptions(
: new_options.max_open_files - 10); : new_options.max_open_files - 10);
wal_changed = mutable_db_options_.wal_bytes_per_sync != wal_changed = mutable_db_options_.wal_bytes_per_sync !=
new_options.wal_bytes_per_sync; new_options.wal_bytes_per_sync;
if (new_options.bytes_per_sync == 0) {
new_options.bytes_per_sync = 1024 * 1024;
}
mutable_db_options_ = new_options; mutable_db_options_ = new_options;
env_options_for_compaction_ = EnvOptions( env_options_for_compaction_ = EnvOptions(new_db_options);
BuildDBOptions(immutable_db_options_, mutable_db_options_));
env_options_for_compaction_ = env_->OptimizeForCompactionTableWrite( env_options_for_compaction_ = env_->OptimizeForCompactionTableWrite(
env_options_for_compaction_, immutable_db_options_); env_options_for_compaction_, immutable_db_options_);
versions_->ChangeEnvOptions(mutable_db_options_); versions_->ChangeEnvOptions(mutable_db_options_);
//TODO(xiez): clarify why apply optimize for read to write options
env_options_for_compaction_ = env_->OptimizeForCompactionTableRead( env_options_for_compaction_ = env_->OptimizeForCompactionTableRead(
env_options_for_compaction_, immutable_db_options_); env_options_for_compaction_, immutable_db_options_);
env_options_for_compaction_.compaction_readahead_size = env_options_for_compaction_.compaction_readahead_size =

View File

@ -1501,6 +1501,13 @@ class DBImpl : public DB {
Status CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number, Status CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
size_t preallocate_block_size, log::Writer** new_log); size_t preallocate_block_size, log::Writer** new_log);
// Validate self-consistency of DB options
static Status ValidateOptions(const DBOptions& db_options);
// Validate self-consistency of DB options and its consistency with cf options
static Status ValidateOptions(
const DBOptions& db_options,
const std::vector<ColumnFamilyDescriptor>& column_families);
// table_cache_ provides its own synchronization // table_cache_ provides its own synchronization
std::shared_ptr<Cache> table_cache_; std::shared_ptr<Cache> table_cache_;

View File

@ -145,7 +145,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
} }
namespace { namespace {
Status SanitizeOptionsByTable( Status SanitizeOptionsByTable(
const DBOptions& db_opts, const DBOptions& db_opts,
const std::vector<ColumnFamilyDescriptor>& column_families) { const std::vector<ColumnFamilyDescriptor>& column_families) {
@ -158,52 +157,23 @@ Status SanitizeOptionsByTable(
} }
return Status::OK(); return Status::OK();
} }
} // namespace
static Status ValidateOptions( Status DBImpl::ValidateOptions(
const DBOptions& db_options, const DBOptions& db_options,
const std::vector<ColumnFamilyDescriptor>& column_families) { const std::vector<ColumnFamilyDescriptor>& column_families) {
Status s; Status s;
for (auto& cfd : column_families) { for (auto& cfd : column_families) {
s = CheckCompressionSupported(cfd.options); s = ColumnFamilyData::ValidateOptions(db_options, cfd.options);
if (s.ok() && db_options.allow_concurrent_memtable_write) {
s = CheckConcurrentWritesSupported(cfd.options);
}
if (s.ok()) {
s = CheckCFPathsSupported(db_options, cfd.options);
}
if (!s.ok()) { if (!s.ok()) {
return s; return s;
} }
if (cfd.options.ttl > 0) {
if (db_options.max_open_files != -1) {
return Status::NotSupported(
"TTL is only supported when files are always "
"kept open (set max_open_files = -1). ");
}
if (cfd.options.table_factory->Name() !=
BlockBasedTableFactory().Name()) {
return Status::NotSupported(
"TTL is only supported in Block-Based Table format. ");
}
}
if (cfd.options.periodic_compaction_seconds > 0) {
if (db_options.max_open_files != -1) {
return Status::NotSupported(
"Periodic Compaction is only supported when files are always "
"kept open (set max_open_files = -1). ");
}
if (cfd.options.table_factory->Name() !=
BlockBasedTableFactory().Name()) {
return Status::NotSupported(
"Periodic Compaction is only supported in "
"Block-Based Table format. ");
}
}
} }
s = ValidateOptions(db_options);
return s;
}
Status DBImpl::ValidateOptions(const DBOptions& db_options) {
if (db_options.db_paths.size() > 4) { if (db_options.db_paths.size() > 4) {
return Status::NotSupported( return Status::NotSupported(
"More than four DB paths are not supported yet. "); "More than four DB paths are not supported yet. ");
@ -241,7 +211,7 @@ static Status ValidateOptions(
return Status::OK(); return Status::OK();
} }
} // namespace
Status DBImpl::NewDB() { Status DBImpl::NewDB() {
VersionEdit new_db; VersionEdit new_db;
new_db.SetLogNumber(0); new_db.SetLogNumber(0);

View File

@ -66,10 +66,10 @@ class DBOptionsTest : public DBTestBase {
std::unordered_map<std::string, std::string> GetRandomizedMutableCFOptionsMap( std::unordered_map<std::string, std::string> GetRandomizedMutableCFOptionsMap(
Random* rnd) { Random* rnd) {
Options options; Options options = CurrentOptions();
options.env = env_; options.env = env_;
ImmutableDBOptions db_options(options); ImmutableDBOptions db_options(options);
test::RandomInitCFOptions(&options, rnd); test::RandomInitCFOptions(&options, options, rnd);
auto sanitized_options = SanitizeOptions(db_options, options); auto sanitized_options = SanitizeOptions(db_options, options);
auto opt_map = GetMutableCFOptionsMap(sanitized_options); auto opt_map = GetMutableCFOptionsMap(sanitized_options);
delete options.compaction_filter; delete options.compaction_filter;

View File

@ -4884,11 +4884,14 @@ TEST_F(DBTest, DynamicMiscOptions) {
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0], ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0],
&mutable_cf_options)); &mutable_cf_options));
ASSERT_EQ(CompressionType::kNoCompression, mutable_cf_options.compression); ASSERT_EQ(CompressionType::kNoCompression, mutable_cf_options.compression);
// Appveyor fails with: Compression type Snappy is not linked with the binary
#ifndef OS_WIN
ASSERT_OK(dbfull()->SetOptions({{"compression", "kSnappyCompression"}})); ASSERT_OK(dbfull()->SetOptions({{"compression", "kSnappyCompression"}}));
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0], ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0],
&mutable_cf_options)); &mutable_cf_options));
ASSERT_EQ(CompressionType::kSnappyCompression, ASSERT_EQ(CompressionType::kSnappyCompression,
mutable_cf_options.compression); mutable_cf_options.compression);
#endif
// Test paranoid_file_checks already done in db_block_cache_test // Test paranoid_file_checks already done in db_block_cache_test
ASSERT_OK( ASSERT_OK(
dbfull()->SetOptions(handles_[1], {{"paranoid_file_checks", "true"}})); dbfull()->SetOptions(handles_[1], {{"paranoid_file_checks", "true"}}));

View File

@ -842,7 +842,7 @@ TEST_F(OptionsTest, OptionsComposeDecompose) {
Random rnd(301); Random rnd(301);
test::RandomInitDBOptions(&base_db_opts, &rnd); test::RandomInitDBOptions(&base_db_opts, &rnd);
test::RandomInitCFOptions(&base_cf_opts, &rnd); test::RandomInitCFOptions(&base_cf_opts, base_db_opts, &rnd);
Options base_opts(base_db_opts, base_cf_opts); Options base_opts(base_db_opts, base_cf_opts);
DBOptions new_db_opts(base_opts); DBOptions new_db_opts(base_opts);
@ -854,11 +854,12 @@ TEST_F(OptionsTest, OptionsComposeDecompose) {
} }
TEST_F(OptionsTest, ColumnFamilyOptionsSerialization) { TEST_F(OptionsTest, ColumnFamilyOptionsSerialization) {
Options options;
ColumnFamilyOptions base_opt, new_opt; ColumnFamilyOptions base_opt, new_opt;
Random rnd(302); Random rnd(302);
// Phase 1: randomly assign base_opt // Phase 1: randomly assign base_opt
// custom type options // custom type options
test::RandomInitCFOptions(&base_opt, &rnd); test::RandomInitCFOptions(&base_opt, options, &rnd);
// Phase 2: obtain a string from base_opt // Phase 2: obtain a string from base_opt
std::string base_options_file_content; std::string base_options_file_content;
@ -1521,7 +1522,7 @@ TEST_F(OptionsParserTest, DumpAndParse) {
for (int c = 0; c < num_cf; ++c) { for (int c = 0; c < num_cf; ++c) {
ColumnFamilyOptions cf_opt; ColumnFamilyOptions cf_opt;
Random cf_rnd(0xFB + c); Random cf_rnd(0xFB + c);
test::RandomInitCFOptions(&cf_opt, &cf_rnd); test::RandomInitCFOptions(&cf_opt, base_db_opt, &cf_rnd);
if (c < 4) { if (c < 4) {
cf_opt.prefix_extractor.reset(test::RandomSliceTransform(&rnd, c)); cf_opt.prefix_extractor.reset(test::RandomSliceTransform(&rnd, c));
} }

View File

@ -162,7 +162,11 @@ std::string RandomName(Random* rnd, const size_t len) {
} }
CompressionType RandomCompressionType(Random* rnd) { CompressionType RandomCompressionType(Random* rnd) {
return static_cast<CompressionType>(rnd->Uniform(6)); auto ret = static_cast<CompressionType>(rnd->Uniform(6));
while (!CompressionTypeSupported(ret)) {
ret = static_cast<CompressionType>((static_cast<int>(ret) + 1) % 6);
}
return ret;
} }
void RandomCompressionTypeVector(const size_t count, void RandomCompressionTypeVector(const size_t count,
@ -293,7 +297,8 @@ void RandomInitDBOptions(DBOptions* db_opt, Random* rnd) {
db_opt->stats_dump_period_sec = rnd->Uniform(100000); db_opt->stats_dump_period_sec = rnd->Uniform(100000);
} }
void RandomInitCFOptions(ColumnFamilyOptions* cf_opt, Random* rnd) { void RandomInitCFOptions(ColumnFamilyOptions* cf_opt, DBOptions& db_options,
Random* rnd) {
cf_opt->compaction_style = (CompactionStyle)(rnd->Uniform(4)); cf_opt->compaction_style = (CompactionStyle)(rnd->Uniform(4));
// boolean options // boolean options
@ -345,8 +350,10 @@ void RandomInitCFOptions(ColumnFamilyOptions* cf_opt, Random* rnd) {
// uint64_t options // uint64_t options
static const uint64_t uint_max = static_cast<uint64_t>(UINT_MAX); static const uint64_t uint_max = static_cast<uint64_t>(UINT_MAX);
cf_opt->ttl = uint_max + rnd->Uniform(10000); cf_opt->ttl =
cf_opt->periodic_compaction_seconds = uint_max + rnd->Uniform(10000); db_options.max_open_files == -1 ? uint_max + rnd->Uniform(10000) : 0;
cf_opt->periodic_compaction_seconds =
db_options.max_open_files == -1 ? uint_max + rnd->Uniform(10000) : 0;
cf_opt->max_sequential_skip_in_iterations = uint_max + rnd->Uniform(10000); cf_opt->max_sequential_skip_in_iterations = uint_max + rnd->Uniform(10000);
cf_opt->target_file_size_base = uint_max + rnd->Uniform(10000); cf_opt->target_file_size_base = uint_max + rnd->Uniform(10000);
cf_opt->max_compaction_bytes = cf_opt->max_compaction_bytes =

View File

@ -657,7 +657,7 @@ void RandomInitDBOptions(DBOptions* db_opt, Random* rnd);
// Randomly initialize the given ColumnFamilyOptions // Randomly initialize the given ColumnFamilyOptions
// Note that the caller is responsible for releasing non-null // Note that the caller is responsible for releasing non-null
// cf_opt->compaction_filter. // cf_opt->compaction_filter.
void RandomInitCFOptions(ColumnFamilyOptions* cf_opt, Random* rnd); void RandomInitCFOptions(ColumnFamilyOptions* cf_opt, DBOptions&, Random* rnd);
// A dummy merge operator which can change its name // A dummy merge operator which can change its name
class ChanglingMergeOperator : public MergeOperator { class ChanglingMergeOperator : public MergeOperator {

View File

@ -58,7 +58,7 @@ TEST_F(OptionsUtilTest, SaveAndLoad) {
cf_names.push_back(i == 0 ? kDefaultColumnFamilyName cf_names.push_back(i == 0 ? kDefaultColumnFamilyName
: test::RandomName(&rnd_, 10)); : test::RandomName(&rnd_, 10));
cf_opts.emplace_back(); cf_opts.emplace_back();
test::RandomInitCFOptions(&cf_opts.back(), &rnd_); test::RandomInitCFOptions(&cf_opts.back(), db_opt, &rnd_);
} }
const std::string kFileName = "OPTIONS-123456"; const std::string kFileName = "OPTIONS-123456";
@ -82,7 +82,7 @@ TEST_F(OptionsUtilTest, SaveAndLoad) {
cf_opts[i].table_factory.get(), cf_opts[i].table_factory.get(),
loaded_cf_descs[i].options.table_factory.get())); loaded_cf_descs[i].options.table_factory.get()));
} }
test::RandomInitCFOptions(&cf_opts[i], &rnd_); test::RandomInitCFOptions(&cf_opts[i], db_opt, &rnd_);
ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions( ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
cf_opts[i], loaded_cf_descs[i].options)); cf_opts[i], loaded_cf_descs[i].options));
} }

View File

@ -210,7 +210,7 @@ Status WritePreparedTxnDB::WriteInternal(const WriteOptions& write_options_orig,
WriteBatch empty_batch; WriteBatch empty_batch;
write_options.disableWAL = true; write_options.disableWAL = true;
write_options.sync = false; write_options.sync = false;
const size_t ONE_BATCH = 1; // Just to inc the seq const size_t ONE_BATCH = 1; // Just to inc the seq
s = db_impl_->WriteImpl(write_options, &empty_batch, nullptr, nullptr, s = db_impl_->WriteImpl(write_options, &empty_batch, nullptr, nullptr,
no_log_ref, DISABLE_MEMTABLE, &seq_used, ONE_BATCH, no_log_ref, DISABLE_MEMTABLE, &seq_used, ONE_BATCH,
&update_commit_map_with_prepare); &update_commit_map_with_prepare);