1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
7 #ifndef __STDC_FORMAT_MACROS
8 #define __STDC_FORMAT_MACROS
14 #include <unordered_map>
16 #include "options/options_parser.h"
17 #include "rocksdb/db.h"
18 #include "rocksdb/table.h"
19 #include "rocksdb/utilities/options_util.h"
20 #include "util/random.h"
21 #include "util/testharness.h"
22 #include "util/testutil.h"
25 bool FLAGS_enable_print
= false;
27 #include "util/gflags_compat.h"
28 using GFLAGS_NAMESPACE::ParseCommandLineFlags
;
29 DEFINE_bool(enable_print
, false, "Print options generated to console.");
33 class OptionsUtilTest
: public testing::Test
{
35 OptionsUtilTest() : rnd_(0xFB) {
36 env_
.reset(new test::StringEnv(Env::Default()));
37 dbname_
= test::PerThreadDBPath("options_util_test");
41 std::unique_ptr
<test::StringEnv
> env_
;
46 bool IsBlockBasedTableFactory(TableFactory
* tf
) {
47 return tf
->Name() == BlockBasedTableFactory().Name();
50 TEST_F(OptionsUtilTest
, SaveAndLoad
) {
51 const size_t kCFCount
= 5;
54 std::vector
<std::string
> cf_names
;
55 std::vector
<ColumnFamilyOptions
> cf_opts
;
56 test::RandomInitDBOptions(&db_opt
, &rnd_
);
57 for (size_t i
= 0; i
< kCFCount
; ++i
) {
58 cf_names
.push_back(i
== 0 ? kDefaultColumnFamilyName
59 : test::RandomName(&rnd_
, 10));
60 cf_opts
.emplace_back();
61 test::RandomInitCFOptions(&cf_opts
.back(), &rnd_
);
64 const std::string kFileName
= "OPTIONS-123456";
65 PersistRocksDBOptions(db_opt
, cf_names
, cf_opts
, kFileName
, env_
.get());
67 DBOptions loaded_db_opt
;
68 std::vector
<ColumnFamilyDescriptor
> loaded_cf_descs
;
69 ASSERT_OK(LoadOptionsFromFile(kFileName
, env_
.get(), &loaded_db_opt
,
72 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(db_opt
, loaded_db_opt
));
73 test::RandomInitDBOptions(&db_opt
, &rnd_
);
74 ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(db_opt
, loaded_db_opt
));
76 for (size_t i
= 0; i
< kCFCount
; ++i
) {
77 ASSERT_EQ(cf_names
[i
], loaded_cf_descs
[i
].name
);
78 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
79 cf_opts
[i
], loaded_cf_descs
[i
].options
));
80 if (IsBlockBasedTableFactory(cf_opts
[i
].table_factory
.get())) {
81 ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory(
82 cf_opts
[i
].table_factory
.get(),
83 loaded_cf_descs
[i
].options
.table_factory
.get()));
85 test::RandomInitCFOptions(&cf_opts
[i
], &rnd_
);
86 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
87 cf_opts
[i
], loaded_cf_descs
[i
].options
));
90 for (size_t i
= 0; i
< kCFCount
; ++i
) {
91 if (cf_opts
[i
].compaction_filter
) {
92 delete cf_opts
[i
].compaction_filter
;
97 TEST_F(OptionsUtilTest
, SaveAndLoadWithCacheCheck
) {
100 db_opt
.create_if_missing
= true;
101 // initialize BlockBasedTableOptions
102 std::shared_ptr
<Cache
> cache
= NewLRUCache(1 * 1024);
103 BlockBasedTableOptions bbt_opts
;
104 bbt_opts
.block_size
= 32 * 1024;
106 std::vector
<ColumnFamilyOptions
> cf_opts
;
107 ColumnFamilyOptions default_column_family_opt
= ColumnFamilyOptions();
108 default_column_family_opt
.table_factory
.reset(
109 NewBlockBasedTableFactory(bbt_opts
));
110 cf_opts
.push_back(default_column_family_opt
);
112 ColumnFamilyOptions cf_opt_sample
= ColumnFamilyOptions();
113 cf_opt_sample
.table_factory
.reset(NewBlockBasedTableFactory(bbt_opts
));
114 cf_opts
.push_back(cf_opt_sample
);
116 ColumnFamilyOptions cf_opt_plain_table_opt
= ColumnFamilyOptions();
117 cf_opt_plain_table_opt
.table_factory
.reset(NewPlainTableFactory());
118 cf_opts
.push_back(cf_opt_plain_table_opt
);
120 std::vector
<std::string
> cf_names
;
121 cf_names
.push_back(kDefaultColumnFamilyName
);
122 cf_names
.push_back("cf_sample");
123 cf_names
.push_back("cf_plain_table_sample");
125 const std::string kFileName
= "OPTIONS-LOAD_CACHE_123456";
126 PersistRocksDBOptions(db_opt
, cf_names
, cf_opts
, kFileName
, env_
.get());
127 DBOptions loaded_db_opt
;
128 std::vector
<ColumnFamilyDescriptor
> loaded_cf_descs
;
129 ASSERT_OK(LoadOptionsFromFile(kFileName
, env_
.get(), &loaded_db_opt
,
130 &loaded_cf_descs
, false, &cache
));
131 for (size_t i
= 0; i
< loaded_cf_descs
.size(); i
++) {
132 if (IsBlockBasedTableFactory(cf_opts
[i
].table_factory
.get())) {
133 auto* loaded_bbt_opt
= reinterpret_cast<BlockBasedTableOptions
*>(
134 loaded_cf_descs
[i
].options
.table_factory
->GetOptions());
135 // Expect the same cache will be loaded
136 if (loaded_bbt_opt
!= nullptr) {
137 ASSERT_EQ(loaded_bbt_opt
->block_cache
.get(), cache
.get());
144 class DummyTableFactory
: public TableFactory
{
146 DummyTableFactory() {}
147 ~DummyTableFactory() override
{}
149 const char* Name() const override
{ return "DummyTableFactory"; }
151 Status
NewTableReader(
152 const TableReaderOptions
& /*table_reader_options*/,
153 std::unique_ptr
<RandomAccessFileReader
>&& /*file*/,
154 uint64_t /*file_size*/, std::unique_ptr
<TableReader
>* /*table_reader*/,
155 bool /*prefetch_index_and_filter_in_cache*/) const override
{
156 return Status::NotSupported();
159 TableBuilder
* NewTableBuilder(
160 const TableBuilderOptions
& /*table_builder_options*/,
161 uint32_t /*column_family_id*/,
162 WritableFileWriter
* /*file*/) const override
{
166 Status
SanitizeOptions(
167 const DBOptions
& /*db_opts*/,
168 const ColumnFamilyOptions
& /*cf_opts*/) const override
{
169 return Status::NotSupported();
172 std::string
GetPrintableTableOptions() const override
{ return ""; }
174 Status
GetOptionString(std::string
* /*opt_string*/,
175 const std::string
& /*delimiter*/) const override
{
180 class DummyMergeOperator
: public MergeOperator
{
182 DummyMergeOperator() {}
183 ~DummyMergeOperator() override
{}
185 bool FullMergeV2(const MergeOperationInput
& /*merge_in*/,
186 MergeOperationOutput
* /*merge_out*/) const override
{
190 bool PartialMergeMulti(const Slice
& /*key*/,
191 const std::deque
<Slice
>& /*operand_list*/,
192 std::string
* /*new_value*/,
193 Logger
* /*logger*/) const override
{
197 const char* Name() const override
{ return "DummyMergeOperator"; }
200 class DummySliceTransform
: public SliceTransform
{
202 DummySliceTransform() {}
203 ~DummySliceTransform() override
{}
205 // Return the name of this transformation.
206 const char* Name() const override
{ return "DummySliceTransform"; }
208 // transform a src in domain to a dst in the range
209 Slice
Transform(const Slice
& src
) const override
{ return src
; }
211 // determine whether this is a valid src upon the function applies
212 bool InDomain(const Slice
& /*src*/) const override
{ return false; }
214 // determine whether dst=Transform(src) for some src
215 bool InRange(const Slice
& /*dst*/) const override
{ return false; }
220 TEST_F(OptionsUtilTest
, SanityCheck
) {
222 std::vector
<ColumnFamilyDescriptor
> cf_descs
;
223 const size_t kCFCount
= 5;
224 for (size_t i
= 0; i
< kCFCount
; ++i
) {
225 cf_descs
.emplace_back();
226 cf_descs
.back().name
=
227 (i
== 0) ? kDefaultColumnFamilyName
: test::RandomName(&rnd_
, 10);
229 cf_descs
.back().options
.table_factory
.reset(NewBlockBasedTableFactory());
230 // Assign non-null values to prefix_extractors except the first cf.
231 cf_descs
.back().options
.prefix_extractor
.reset(
232 i
!= 0 ? test::RandomSliceTransform(&rnd_
) : nullptr);
233 cf_descs
.back().options
.merge_operator
.reset(
234 test::RandomMergeOperator(&rnd_
));
237 db_opt
.create_missing_column_families
= true;
238 db_opt
.create_if_missing
= true;
240 DestroyDB(dbname_
, Options(db_opt
, cf_descs
[0].options
));
242 std::vector
<ColumnFamilyHandle
*> handles
;
243 // open and persist the options
244 ASSERT_OK(DB::Open(db_opt
, dbname_
, cf_descs
, &handles
, &db
));
247 for (auto* handle
: handles
) {
252 // perform sanity check
254 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
256 ASSERT_GE(kCFCount
, 5);
259 std::shared_ptr
<MergeOperator
> merge_op
=
260 cf_descs
[0].options
.merge_operator
;
262 ASSERT_NE(merge_op
.get(), nullptr);
263 cf_descs
[0].options
.merge_operator
.reset();
265 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
267 cf_descs
[0].options
.merge_operator
.reset(new DummyMergeOperator());
269 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
271 cf_descs
[0].options
.merge_operator
= merge_op
;
273 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
278 std::shared_ptr
<const SliceTransform
> prefix_extractor
=
279 cf_descs
[1].options
.prefix_extractor
;
281 // It's okay to set prefix_extractor to nullptr.
282 ASSERT_NE(prefix_extractor
, nullptr);
283 cf_descs
[1].options
.prefix_extractor
.reset();
285 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
287 cf_descs
[1].options
.prefix_extractor
.reset(new DummySliceTransform());
289 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
291 cf_descs
[1].options
.prefix_extractor
= prefix_extractor
;
293 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
296 // prefix extractor nullptr case
298 std::shared_ptr
<const SliceTransform
> prefix_extractor
=
299 cf_descs
[0].options
.prefix_extractor
;
301 // It's okay to set prefix_extractor to nullptr.
302 ASSERT_EQ(prefix_extractor
, nullptr);
303 cf_descs
[0].options
.prefix_extractor
.reset();
305 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
307 // It's okay to change prefix_extractor from nullptr to non-nullptr
308 cf_descs
[0].options
.prefix_extractor
.reset(new DummySliceTransform());
310 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
312 cf_descs
[0].options
.prefix_extractor
= prefix_extractor
;
314 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
319 test::SimpleSuffixReverseComparator comparator
;
321 auto* prev_comparator
= cf_descs
[2].options
.comparator
;
322 cf_descs
[2].options
.comparator
= &comparator
;
324 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
326 cf_descs
[2].options
.comparator
= prev_comparator
;
328 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
333 std::shared_ptr
<TableFactory
> table_factory
=
334 cf_descs
[3].options
.table_factory
;
336 ASSERT_NE(table_factory
, nullptr);
337 cf_descs
[3].options
.table_factory
.reset(new DummyTableFactory());
339 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
341 cf_descs
[3].options
.table_factory
= table_factory
;
343 CheckOptionsCompatibility(dbname_
, Env::Default(), db_opt
, cf_descs
));
347 } // namespace rocksdb
349 int main(int argc
, char** argv
) {
350 ::testing::InitGoogleTest(&argc
, argv
);
352 ParseCommandLineFlags(&argc
, &argv
, true);
354 return RUN_ALL_TESTS();
360 int main(int /*argc*/, char** /*argv*/) {
361 printf("Skipped in RocksDBLite as utilities are not supported.\n");
364 #endif // !ROCKSDB_LITE