]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
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). | |
7c673cae FG |
5 | |
6 | #ifndef ROCKSDB_LITE | |
7c673cae | 7 | |
f67539c2 | 8 | #include <cinttypes> |
7c673cae FG |
9 | |
10 | #include <cctype> | |
11 | #include <unordered_map> | |
12 | ||
13 | #include "options/options_parser.h" | |
14 | #include "rocksdb/db.h" | |
15 | #include "rocksdb/table.h" | |
16 | #include "rocksdb/utilities/options_util.h" | |
f67539c2 TL |
17 | #include "test_util/testharness.h" |
18 | #include "test_util/testutil.h" | |
7c673cae | 19 | #include "util/random.h" |
7c673cae FG |
20 | |
21 | #ifndef GFLAGS | |
22 | bool FLAGS_enable_print = false; | |
23 | #else | |
11fdf7f2 TL |
24 | #include "util/gflags_compat.h" |
25 | using GFLAGS_NAMESPACE::ParseCommandLineFlags; | |
7c673cae FG |
26 | DEFINE_bool(enable_print, false, "Print options generated to console."); |
27 | #endif // GFLAGS | |
28 | ||
f67539c2 | 29 | namespace ROCKSDB_NAMESPACE { |
7c673cae FG |
30 | class OptionsUtilTest : public testing::Test { |
31 | public: | |
32 | OptionsUtilTest() : rnd_(0xFB) { | |
33 | env_.reset(new test::StringEnv(Env::Default())); | |
f67539c2 | 34 | fs_.reset(new LegacyFileSystemWrapper(env_.get())); |
11fdf7f2 | 35 | dbname_ = test::PerThreadDBPath("options_util_test"); |
7c673cae FG |
36 | } |
37 | ||
38 | protected: | |
39 | std::unique_ptr<test::StringEnv> env_; | |
f67539c2 | 40 | std::unique_ptr<LegacyFileSystemWrapper> fs_; |
7c673cae FG |
41 | std::string dbname_; |
42 | Random rnd_; | |
43 | }; | |
44 | ||
45 | bool IsBlockBasedTableFactory(TableFactory* tf) { | |
46 | return tf->Name() == BlockBasedTableFactory().Name(); | |
47 | } | |
48 | ||
49 | TEST_F(OptionsUtilTest, SaveAndLoad) { | |
50 | const size_t kCFCount = 5; | |
51 | ||
52 | DBOptions db_opt; | |
53 | std::vector<std::string> cf_names; | |
54 | std::vector<ColumnFamilyOptions> cf_opts; | |
55 | test::RandomInitDBOptions(&db_opt, &rnd_); | |
56 | for (size_t i = 0; i < kCFCount; ++i) { | |
57 | cf_names.push_back(i == 0 ? kDefaultColumnFamilyName | |
58 | : test::RandomName(&rnd_, 10)); | |
59 | cf_opts.emplace_back(); | |
f67539c2 | 60 | test::RandomInitCFOptions(&cf_opts.back(), db_opt, &rnd_); |
7c673cae FG |
61 | } |
62 | ||
63 | const std::string kFileName = "OPTIONS-123456"; | |
f67539c2 | 64 | PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName, fs_.get()); |
7c673cae FG |
65 | |
66 | DBOptions loaded_db_opt; | |
67 | std::vector<ColumnFamilyDescriptor> loaded_cf_descs; | |
68 | ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt, | |
69 | &loaded_cf_descs)); | |
70 | ||
71 | ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt)); | |
72 | test::RandomInitDBOptions(&db_opt, &rnd_); | |
73 | ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt)); | |
74 | ||
75 | for (size_t i = 0; i < kCFCount; ++i) { | |
76 | ASSERT_EQ(cf_names[i], loaded_cf_descs[i].name); | |
77 | ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions( | |
78 | cf_opts[i], loaded_cf_descs[i].options)); | |
79 | if (IsBlockBasedTableFactory(cf_opts[i].table_factory.get())) { | |
80 | ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory( | |
81 | cf_opts[i].table_factory.get(), | |
82 | loaded_cf_descs[i].options.table_factory.get())); | |
83 | } | |
f67539c2 | 84 | test::RandomInitCFOptions(&cf_opts[i], db_opt, &rnd_); |
7c673cae FG |
85 | ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions( |
86 | cf_opts[i], loaded_cf_descs[i].options)); | |
87 | } | |
88 | ||
89 | for (size_t i = 0; i < kCFCount; ++i) { | |
90 | if (cf_opts[i].compaction_filter) { | |
91 | delete cf_opts[i].compaction_filter; | |
92 | } | |
93 | } | |
94 | } | |
95 | ||
494da23a TL |
96 | TEST_F(OptionsUtilTest, SaveAndLoadWithCacheCheck) { |
97 | // creating db | |
98 | DBOptions db_opt; | |
99 | db_opt.create_if_missing = true; | |
100 | // initialize BlockBasedTableOptions | |
101 | std::shared_ptr<Cache> cache = NewLRUCache(1 * 1024); | |
102 | BlockBasedTableOptions bbt_opts; | |
103 | bbt_opts.block_size = 32 * 1024; | |
104 | // saving cf options | |
105 | std::vector<ColumnFamilyOptions> cf_opts; | |
106 | ColumnFamilyOptions default_column_family_opt = ColumnFamilyOptions(); | |
107 | default_column_family_opt.table_factory.reset( | |
108 | NewBlockBasedTableFactory(bbt_opts)); | |
109 | cf_opts.push_back(default_column_family_opt); | |
110 | ||
111 | ColumnFamilyOptions cf_opt_sample = ColumnFamilyOptions(); | |
112 | cf_opt_sample.table_factory.reset(NewBlockBasedTableFactory(bbt_opts)); | |
113 | cf_opts.push_back(cf_opt_sample); | |
114 | ||
115 | ColumnFamilyOptions cf_opt_plain_table_opt = ColumnFamilyOptions(); | |
116 | cf_opt_plain_table_opt.table_factory.reset(NewPlainTableFactory()); | |
117 | cf_opts.push_back(cf_opt_plain_table_opt); | |
118 | ||
119 | std::vector<std::string> cf_names; | |
120 | cf_names.push_back(kDefaultColumnFamilyName); | |
121 | cf_names.push_back("cf_sample"); | |
122 | cf_names.push_back("cf_plain_table_sample"); | |
123 | // Saving DB in file | |
124 | const std::string kFileName = "OPTIONS-LOAD_CACHE_123456"; | |
f67539c2 | 125 | PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName, fs_.get()); |
494da23a TL |
126 | DBOptions loaded_db_opt; |
127 | std::vector<ColumnFamilyDescriptor> loaded_cf_descs; | |
128 | ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt, | |
129 | &loaded_cf_descs, false, &cache)); | |
130 | for (size_t i = 0; i < loaded_cf_descs.size(); i++) { | |
131 | if (IsBlockBasedTableFactory(cf_opts[i].table_factory.get())) { | |
132 | auto* loaded_bbt_opt = reinterpret_cast<BlockBasedTableOptions*>( | |
133 | loaded_cf_descs[i].options.table_factory->GetOptions()); | |
134 | // Expect the same cache will be loaded | |
135 | if (loaded_bbt_opt != nullptr) { | |
136 | ASSERT_EQ(loaded_bbt_opt->block_cache.get(), cache.get()); | |
137 | } | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
7c673cae FG |
142 | namespace { |
143 | class DummyTableFactory : public TableFactory { | |
144 | public: | |
145 | DummyTableFactory() {} | |
494da23a | 146 | ~DummyTableFactory() override {} |
7c673cae | 147 | |
494da23a | 148 | const char* Name() const override { return "DummyTableFactory"; } |
7c673cae | 149 | |
494da23a | 150 | Status NewTableReader( |
11fdf7f2 | 151 | const TableReaderOptions& /*table_reader_options*/, |
494da23a TL |
152 | std::unique_ptr<RandomAccessFileReader>&& /*file*/, |
153 | uint64_t /*file_size*/, std::unique_ptr<TableReader>* /*table_reader*/, | |
11fdf7f2 | 154 | bool /*prefetch_index_and_filter_in_cache*/) const override { |
7c673cae FG |
155 | return Status::NotSupported(); |
156 | } | |
157 | ||
494da23a | 158 | TableBuilder* NewTableBuilder( |
11fdf7f2 TL |
159 | const TableBuilderOptions& /*table_builder_options*/, |
160 | uint32_t /*column_family_id*/, | |
161 | WritableFileWriter* /*file*/) const override { | |
7c673cae FG |
162 | return nullptr; |
163 | } | |
164 | ||
494da23a | 165 | Status SanitizeOptions( |
11fdf7f2 TL |
166 | const DBOptions& /*db_opts*/, |
167 | const ColumnFamilyOptions& /*cf_opts*/) const override { | |
7c673cae FG |
168 | return Status::NotSupported(); |
169 | } | |
170 | ||
494da23a | 171 | std::string GetPrintableTableOptions() const override { return ""; } |
11fdf7f2 TL |
172 | |
173 | Status GetOptionString(std::string* /*opt_string*/, | |
174 | const std::string& /*delimiter*/) const override { | |
175 | return Status::OK(); | |
176 | } | |
7c673cae FG |
177 | }; |
178 | ||
179 | class DummyMergeOperator : public MergeOperator { | |
180 | public: | |
181 | DummyMergeOperator() {} | |
494da23a | 182 | ~DummyMergeOperator() override {} |
7c673cae | 183 | |
494da23a TL |
184 | bool FullMergeV2(const MergeOperationInput& /*merge_in*/, |
185 | MergeOperationOutput* /*merge_out*/) const override { | |
7c673cae FG |
186 | return false; |
187 | } | |
188 | ||
494da23a TL |
189 | bool PartialMergeMulti(const Slice& /*key*/, |
190 | const std::deque<Slice>& /*operand_list*/, | |
191 | std::string* /*new_value*/, | |
192 | Logger* /*logger*/) const override { | |
7c673cae FG |
193 | return false; |
194 | } | |
195 | ||
494da23a | 196 | const char* Name() const override { return "DummyMergeOperator"; } |
7c673cae FG |
197 | }; |
198 | ||
199 | class DummySliceTransform : public SliceTransform { | |
200 | public: | |
201 | DummySliceTransform() {} | |
494da23a | 202 | ~DummySliceTransform() override {} |
7c673cae FG |
203 | |
204 | // Return the name of this transformation. | |
494da23a | 205 | const char* Name() const override { return "DummySliceTransform"; } |
7c673cae FG |
206 | |
207 | // transform a src in domain to a dst in the range | |
494da23a | 208 | Slice Transform(const Slice& src) const override { return src; } |
7c673cae FG |
209 | |
210 | // determine whether this is a valid src upon the function applies | |
494da23a | 211 | bool InDomain(const Slice& /*src*/) const override { return false; } |
7c673cae FG |
212 | |
213 | // determine whether dst=Transform(src) for some src | |
494da23a | 214 | bool InRange(const Slice& /*dst*/) const override { return false; } |
7c673cae FG |
215 | }; |
216 | ||
217 | } // namespace | |
218 | ||
219 | TEST_F(OptionsUtilTest, SanityCheck) { | |
220 | DBOptions db_opt; | |
221 | std::vector<ColumnFamilyDescriptor> cf_descs; | |
222 | const size_t kCFCount = 5; | |
223 | for (size_t i = 0; i < kCFCount; ++i) { | |
224 | cf_descs.emplace_back(); | |
225 | cf_descs.back().name = | |
226 | (i == 0) ? kDefaultColumnFamilyName : test::RandomName(&rnd_, 10); | |
227 | ||
228 | cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory()); | |
229 | // Assign non-null values to prefix_extractors except the first cf. | |
230 | cf_descs.back().options.prefix_extractor.reset( | |
231 | i != 0 ? test::RandomSliceTransform(&rnd_) : nullptr); | |
232 | cf_descs.back().options.merge_operator.reset( | |
233 | test::RandomMergeOperator(&rnd_)); | |
234 | } | |
235 | ||
236 | db_opt.create_missing_column_families = true; | |
237 | db_opt.create_if_missing = true; | |
238 | ||
239 | DestroyDB(dbname_, Options(db_opt, cf_descs[0].options)); | |
240 | DB* db; | |
241 | std::vector<ColumnFamilyHandle*> handles; | |
242 | // open and persist the options | |
243 | ASSERT_OK(DB::Open(db_opt, dbname_, cf_descs, &handles, &db)); | |
244 | ||
245 | // close the db | |
246 | for (auto* handle : handles) { | |
247 | delete handle; | |
248 | } | |
249 | delete db; | |
250 | ||
251 | // perform sanity check | |
252 | ASSERT_OK( | |
253 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
254 | ||
255 | ASSERT_GE(kCFCount, 5); | |
256 | // merge operator | |
257 | { | |
258 | std::shared_ptr<MergeOperator> merge_op = | |
259 | cf_descs[0].options.merge_operator; | |
260 | ||
261 | ASSERT_NE(merge_op.get(), nullptr); | |
262 | cf_descs[0].options.merge_operator.reset(); | |
263 | ASSERT_NOK( | |
264 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
265 | ||
266 | cf_descs[0].options.merge_operator.reset(new DummyMergeOperator()); | |
267 | ASSERT_NOK( | |
268 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
269 | ||
270 | cf_descs[0].options.merge_operator = merge_op; | |
271 | ASSERT_OK( | |
272 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
273 | } | |
274 | ||
275 | // prefix extractor | |
276 | { | |
277 | std::shared_ptr<const SliceTransform> prefix_extractor = | |
278 | cf_descs[1].options.prefix_extractor; | |
279 | ||
280 | // It's okay to set prefix_extractor to nullptr. | |
281 | ASSERT_NE(prefix_extractor, nullptr); | |
282 | cf_descs[1].options.prefix_extractor.reset(); | |
283 | ASSERT_OK( | |
284 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
285 | ||
286 | cf_descs[1].options.prefix_extractor.reset(new DummySliceTransform()); | |
11fdf7f2 | 287 | ASSERT_OK( |
7c673cae FG |
288 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); |
289 | ||
290 | cf_descs[1].options.prefix_extractor = prefix_extractor; | |
291 | ASSERT_OK( | |
292 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
293 | } | |
294 | ||
295 | // prefix extractor nullptr case | |
296 | { | |
297 | std::shared_ptr<const SliceTransform> prefix_extractor = | |
298 | cf_descs[0].options.prefix_extractor; | |
299 | ||
300 | // It's okay to set prefix_extractor to nullptr. | |
301 | ASSERT_EQ(prefix_extractor, nullptr); | |
302 | cf_descs[0].options.prefix_extractor.reset(); | |
303 | ASSERT_OK( | |
304 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
305 | ||
306 | // It's okay to change prefix_extractor from nullptr to non-nullptr | |
307 | cf_descs[0].options.prefix_extractor.reset(new DummySliceTransform()); | |
308 | ASSERT_OK( | |
309 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
310 | ||
311 | cf_descs[0].options.prefix_extractor = prefix_extractor; | |
312 | ASSERT_OK( | |
313 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
314 | } | |
315 | ||
316 | // comparator | |
317 | { | |
318 | test::SimpleSuffixReverseComparator comparator; | |
319 | ||
320 | auto* prev_comparator = cf_descs[2].options.comparator; | |
321 | cf_descs[2].options.comparator = &comparator; | |
322 | ASSERT_NOK( | |
323 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
324 | ||
325 | cf_descs[2].options.comparator = prev_comparator; | |
326 | ASSERT_OK( | |
327 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
328 | } | |
329 | ||
330 | // table factory | |
331 | { | |
332 | std::shared_ptr<TableFactory> table_factory = | |
333 | cf_descs[3].options.table_factory; | |
334 | ||
335 | ASSERT_NE(table_factory, nullptr); | |
336 | cf_descs[3].options.table_factory.reset(new DummyTableFactory()); | |
337 | ASSERT_NOK( | |
338 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
339 | ||
340 | cf_descs[3].options.table_factory = table_factory; | |
341 | ASSERT_OK( | |
342 | CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs)); | |
343 | } | |
344 | } | |
345 | ||
f67539c2 | 346 | } // namespace ROCKSDB_NAMESPACE |
7c673cae FG |
347 | |
348 | int main(int argc, char** argv) { | |
349 | ::testing::InitGoogleTest(&argc, argv); | |
350 | #ifdef GFLAGS | |
351 | ParseCommandLineFlags(&argc, &argv, true); | |
352 | #endif // GFLAGS | |
353 | return RUN_ALL_TESTS(); | |
354 | } | |
355 | ||
356 | #else | |
357 | #include <cstdio> | |
358 | ||
11fdf7f2 | 359 | int main(int /*argc*/, char** /*argv*/) { |
7c673cae FG |
360 | printf("Skipped in RocksDBLite as utilities are not supported.\n"); |
361 | return 0; | |
362 | } | |
363 | #endif // !ROCKSDB_LITE |