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).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10 #include <cctype>
11 #include <cinttypes>
12 #include <cstring>
13 #include <unordered_map>
14
15 #include "cache/lru_cache.h"
16 #include "cache/sharded_cache.h"
17 #include "options/options_helper.h"
18 #include "options/options_parser.h"
19 #include "port/port.h"
20 #include "rocksdb/cache.h"
21 #include "rocksdb/convenience.h"
22 #include "rocksdb/memtablerep.h"
23 #include "rocksdb/utilities/leveldb_options.h"
24 #include "rocksdb/utilities/object_registry.h"
25 #include "rocksdb/utilities/options_type.h"
26 #include "table/block_based/filter_policy_internal.h"
27 #include "test_util/testharness.h"
28 #include "test_util/testutil.h"
29 #include "util/random.h"
30 #include "util/stderr_logger.h"
31 #include "util/string_util.h"
32 #include "utilities/merge_operators/bytesxor.h"
33
34 #ifndef GFLAGS
35 bool FLAGS_enable_print = false;
36 #else
37 #include "util/gflags_compat.h"
38 using GFLAGS_NAMESPACE::ParseCommandLineFlags;
39 DEFINE_bool(enable_print, false, "Print options generated to console.");
40 #endif // GFLAGS
41
42 namespace ROCKSDB_NAMESPACE {
43
44 class OptionsTest : public testing::Test {};
45
46 #ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
TEST_F(OptionsTest,GetOptionsFromMapTest)47 TEST_F(OptionsTest, GetOptionsFromMapTest) {
48 std::unordered_map<std::string, std::string> cf_options_map = {
49 {"write_buffer_size", "1"},
50 {"max_write_buffer_number", "2"},
51 {"min_write_buffer_number_to_merge", "3"},
52 {"max_write_buffer_number_to_maintain", "99"},
53 {"max_write_buffer_size_to_maintain", "-99999"},
54 {"compression", "kSnappyCompression"},
55 {"compression_per_level",
56 "kNoCompression:"
57 "kSnappyCompression:"
58 "kZlibCompression:"
59 "kBZip2Compression:"
60 "kLZ4Compression:"
61 "kLZ4HCCompression:"
62 "kXpressCompression:"
63 "kZSTD:"
64 "kZSTDNotFinalCompression"},
65 {"bottommost_compression", "kLZ4Compression"},
66 {"bottommost_compression_opts", "5:6:7:8:10:true"},
67 {"compression_opts", "4:5:6:7:8:true"},
68 {"num_levels", "8"},
69 {"level0_file_num_compaction_trigger", "8"},
70 {"level0_slowdown_writes_trigger", "9"},
71 {"level0_stop_writes_trigger", "10"},
72 {"target_file_size_base", "12"},
73 {"target_file_size_multiplier", "13"},
74 {"max_bytes_for_level_base", "14"},
75 {"level_compaction_dynamic_level_bytes", "true"},
76 {"max_bytes_for_level_multiplier", "15.0"},
77 {"max_bytes_for_level_multiplier_additional", "16:17:18"},
78 {"max_compaction_bytes", "21"},
79 {"soft_rate_limit", "1.1"},
80 {"hard_rate_limit", "2.1"},
81 {"hard_pending_compaction_bytes_limit", "211"},
82 {"arena_block_size", "22"},
83 {"disable_auto_compactions", "true"},
84 {"compaction_style", "kCompactionStyleLevel"},
85 {"compaction_pri", "kOldestSmallestSeqFirst"},
86 {"verify_checksums_in_compaction", "false"},
87 {"compaction_options_fifo", "23"},
88 {"max_sequential_skip_in_iterations", "24"},
89 {"inplace_update_support", "true"},
90 {"report_bg_io_stats", "true"},
91 {"compaction_measure_io_stats", "false"},
92 {"inplace_update_num_locks", "25"},
93 {"memtable_prefix_bloom_size_ratio", "0.26"},
94 {"memtable_whole_key_filtering", "true"},
95 {"memtable_huge_page_size", "28"},
96 {"bloom_locality", "29"},
97 {"max_successive_merges", "30"},
98 {"min_partial_merge_operands", "31"},
99 {"prefix_extractor", "fixed:31"},
100 {"optimize_filters_for_hits", "true"},
101 {"enable_blob_files", "true"},
102 {"min_blob_size", "1K"},
103 {"blob_file_size", "1G"},
104 {"blob_compression_type", "kZSTD"},
105 {"enable_blob_garbage_collection", "true"},
106 {"blob_garbage_collection_age_cutoff", "0.5"},
107 };
108
109 std::unordered_map<std::string, std::string> db_options_map = {
110 {"create_if_missing", "false"},
111 {"create_missing_column_families", "true"},
112 {"error_if_exists", "false"},
113 {"paranoid_checks", "true"},
114 {"track_and_verify_wals_in_manifest", "true"},
115 {"max_open_files", "32"},
116 {"max_total_wal_size", "33"},
117 {"use_fsync", "true"},
118 {"db_log_dir", "/db_log_dir"},
119 {"wal_dir", "/wal_dir"},
120 {"delete_obsolete_files_period_micros", "34"},
121 {"max_background_compactions", "35"},
122 {"max_background_flushes", "36"},
123 {"max_log_file_size", "37"},
124 {"log_file_time_to_roll", "38"},
125 {"keep_log_file_num", "39"},
126 {"recycle_log_file_num", "5"},
127 {"max_manifest_file_size", "40"},
128 {"table_cache_numshardbits", "41"},
129 {"WAL_ttl_seconds", "43"},
130 {"WAL_size_limit_MB", "44"},
131 {"manifest_preallocation_size", "45"},
132 {"allow_mmap_reads", "true"},
133 {"allow_mmap_writes", "false"},
134 {"use_direct_reads", "false"},
135 {"use_direct_io_for_flush_and_compaction", "false"},
136 {"is_fd_close_on_exec", "true"},
137 {"skip_log_error_on_recovery", "false"},
138 {"stats_dump_period_sec", "46"},
139 {"stats_persist_period_sec", "57"},
140 {"persist_stats_to_disk", "false"},
141 {"stats_history_buffer_size", "69"},
142 {"advise_random_on_open", "true"},
143 {"use_adaptive_mutex", "false"},
144 {"new_table_reader_for_compaction_inputs", "true"},
145 {"compaction_readahead_size", "100"},
146 {"random_access_max_buffer_size", "3145728"},
147 {"writable_file_max_buffer_size", "314159"},
148 {"bytes_per_sync", "47"},
149 {"wal_bytes_per_sync", "48"},
150 {"strict_bytes_per_sync", "true"},
151 };
152
153 ColumnFamilyOptions base_cf_opt;
154 ColumnFamilyOptions new_cf_opt;
155 ConfigOptions exact, loose;
156 exact.input_strings_escaped = false;
157 exact.ignore_unknown_options = false;
158 exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
159 loose.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
160
161 loose.input_strings_escaped = false;
162 loose.ignore_unknown_options = true;
163 ASSERT_OK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
164 &new_cf_opt));
165 ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
166 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
167 ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
168 ASSERT_EQ(new_cf_opt.max_write_buffer_number_to_maintain, 99);
169 ASSERT_EQ(new_cf_opt.max_write_buffer_size_to_maintain, -99999);
170 ASSERT_EQ(new_cf_opt.compression, kSnappyCompression);
171 ASSERT_EQ(new_cf_opt.compression_per_level.size(), 9U);
172 ASSERT_EQ(new_cf_opt.compression_per_level[0], kNoCompression);
173 ASSERT_EQ(new_cf_opt.compression_per_level[1], kSnappyCompression);
174 ASSERT_EQ(new_cf_opt.compression_per_level[2], kZlibCompression);
175 ASSERT_EQ(new_cf_opt.compression_per_level[3], kBZip2Compression);
176 ASSERT_EQ(new_cf_opt.compression_per_level[4], kLZ4Compression);
177 ASSERT_EQ(new_cf_opt.compression_per_level[5], kLZ4HCCompression);
178 ASSERT_EQ(new_cf_opt.compression_per_level[6], kXpressCompression);
179 ASSERT_EQ(new_cf_opt.compression_per_level[7], kZSTD);
180 ASSERT_EQ(new_cf_opt.compression_per_level[8], kZSTDNotFinalCompression);
181 ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
182 ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
183 ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
184 ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7u);
185 ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
186 ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads,
187 CompressionOptions().parallel_threads);
188 ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
189 ASSERT_EQ(new_cf_opt.bottommost_compression, kLZ4Compression);
190 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 5);
191 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
192 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
193 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
194 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 10u);
195 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
196 CompressionOptions().parallel_threads);
197 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, true);
198 ASSERT_EQ(new_cf_opt.num_levels, 8);
199 ASSERT_EQ(new_cf_opt.level0_file_num_compaction_trigger, 8);
200 ASSERT_EQ(new_cf_opt.level0_slowdown_writes_trigger, 9);
201 ASSERT_EQ(new_cf_opt.level0_stop_writes_trigger, 10);
202 ASSERT_EQ(new_cf_opt.target_file_size_base, static_cast<uint64_t>(12));
203 ASSERT_EQ(new_cf_opt.target_file_size_multiplier, 13);
204 ASSERT_EQ(new_cf_opt.max_bytes_for_level_base, 14U);
205 ASSERT_EQ(new_cf_opt.level_compaction_dynamic_level_bytes, true);
206 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier, 15.0);
207 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional.size(), 3U);
208 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[0], 16);
209 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[1], 17);
210 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[2], 18);
211 ASSERT_EQ(new_cf_opt.max_compaction_bytes, 21);
212 ASSERT_EQ(new_cf_opt.hard_pending_compaction_bytes_limit, 211);
213 ASSERT_EQ(new_cf_opt.arena_block_size, 22U);
214 ASSERT_EQ(new_cf_opt.disable_auto_compactions, true);
215 ASSERT_EQ(new_cf_opt.compaction_style, kCompactionStyleLevel);
216 ASSERT_EQ(new_cf_opt.compaction_pri, kOldestSmallestSeqFirst);
217 ASSERT_EQ(new_cf_opt.compaction_options_fifo.max_table_files_size,
218 static_cast<uint64_t>(23));
219 ASSERT_EQ(new_cf_opt.max_sequential_skip_in_iterations,
220 static_cast<uint64_t>(24));
221 ASSERT_EQ(new_cf_opt.inplace_update_support, true);
222 ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 25U);
223 ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_size_ratio, 0.26);
224 ASSERT_EQ(new_cf_opt.memtable_whole_key_filtering, true);
225 ASSERT_EQ(new_cf_opt.memtable_huge_page_size, 28U);
226 ASSERT_EQ(new_cf_opt.bloom_locality, 29U);
227 ASSERT_EQ(new_cf_opt.max_successive_merges, 30U);
228 ASSERT_TRUE(new_cf_opt.prefix_extractor != nullptr);
229 ASSERT_EQ(new_cf_opt.optimize_filters_for_hits, true);
230 ASSERT_EQ(std::string(new_cf_opt.prefix_extractor->Name()),
231 "rocksdb.FixedPrefix.31");
232 ASSERT_EQ(new_cf_opt.enable_blob_files, true);
233 ASSERT_EQ(new_cf_opt.min_blob_size, 1ULL << 10);
234 ASSERT_EQ(new_cf_opt.blob_file_size, 1ULL << 30);
235 ASSERT_EQ(new_cf_opt.blob_compression_type, kZSTD);
236 ASSERT_EQ(new_cf_opt.enable_blob_garbage_collection, true);
237 ASSERT_EQ(new_cf_opt.blob_garbage_collection_age_cutoff, 0.5);
238
239 cf_options_map["write_buffer_size"] = "hello";
240 ASSERT_NOK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
241 &new_cf_opt));
242 ASSERT_OK(
243 RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
244
245 cf_options_map["write_buffer_size"] = "1";
246 ASSERT_OK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
247 &new_cf_opt));
248
249 cf_options_map["unknown_option"] = "1";
250 ASSERT_NOK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
251 &new_cf_opt));
252 ASSERT_OK(
253 RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
254
255 // ignore_unknown_options=true;input_strings_escaped=false
256 ASSERT_OK(GetColumnFamilyOptionsFromMap(loose, base_cf_opt, cf_options_map,
257 &new_cf_opt));
258 ASSERT_OK(
259 RocksDBOptionsParser::VerifyCFOptions(loose, base_cf_opt, new_cf_opt));
260 ASSERT_NOK(
261 RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
262
263 DBOptions base_db_opt;
264 DBOptions new_db_opt;
265 ASSERT_OK(
266 GetDBOptionsFromMap(exact, base_db_opt, db_options_map, &new_db_opt));
267 ASSERT_EQ(new_db_opt.create_if_missing, false);
268 ASSERT_EQ(new_db_opt.create_missing_column_families, true);
269 ASSERT_EQ(new_db_opt.error_if_exists, false);
270 ASSERT_EQ(new_db_opt.paranoid_checks, true);
271 ASSERT_EQ(new_db_opt.track_and_verify_wals_in_manifest, true);
272 ASSERT_EQ(new_db_opt.max_open_files, 32);
273 ASSERT_EQ(new_db_opt.max_total_wal_size, static_cast<uint64_t>(33));
274 ASSERT_EQ(new_db_opt.use_fsync, true);
275 ASSERT_EQ(new_db_opt.db_log_dir, "/db_log_dir");
276 ASSERT_EQ(new_db_opt.wal_dir, "/wal_dir");
277 ASSERT_EQ(new_db_opt.delete_obsolete_files_period_micros,
278 static_cast<uint64_t>(34));
279 ASSERT_EQ(new_db_opt.max_background_compactions, 35);
280 ASSERT_EQ(new_db_opt.max_background_flushes, 36);
281 ASSERT_EQ(new_db_opt.max_log_file_size, 37U);
282 ASSERT_EQ(new_db_opt.log_file_time_to_roll, 38U);
283 ASSERT_EQ(new_db_opt.keep_log_file_num, 39U);
284 ASSERT_EQ(new_db_opt.recycle_log_file_num, 5U);
285 ASSERT_EQ(new_db_opt.max_manifest_file_size, static_cast<uint64_t>(40));
286 ASSERT_EQ(new_db_opt.table_cache_numshardbits, 41);
287 ASSERT_EQ(new_db_opt.WAL_ttl_seconds, static_cast<uint64_t>(43));
288 ASSERT_EQ(new_db_opt.WAL_size_limit_MB, static_cast<uint64_t>(44));
289 ASSERT_EQ(new_db_opt.manifest_preallocation_size, 45U);
290 ASSERT_EQ(new_db_opt.allow_mmap_reads, true);
291 ASSERT_EQ(new_db_opt.allow_mmap_writes, false);
292 ASSERT_EQ(new_db_opt.use_direct_reads, false);
293 ASSERT_EQ(new_db_opt.use_direct_io_for_flush_and_compaction, false);
294 ASSERT_EQ(new_db_opt.is_fd_close_on_exec, true);
295 ASSERT_EQ(new_db_opt.skip_log_error_on_recovery, false);
296 ASSERT_EQ(new_db_opt.stats_dump_period_sec, 46U);
297 ASSERT_EQ(new_db_opt.stats_persist_period_sec, 57U);
298 ASSERT_EQ(new_db_opt.persist_stats_to_disk, false);
299 ASSERT_EQ(new_db_opt.stats_history_buffer_size, 69U);
300 ASSERT_EQ(new_db_opt.advise_random_on_open, true);
301 ASSERT_EQ(new_db_opt.use_adaptive_mutex, false);
302 ASSERT_EQ(new_db_opt.new_table_reader_for_compaction_inputs, true);
303 ASSERT_EQ(new_db_opt.compaction_readahead_size, 100);
304 ASSERT_EQ(new_db_opt.random_access_max_buffer_size, 3145728);
305 ASSERT_EQ(new_db_opt.writable_file_max_buffer_size, 314159);
306 ASSERT_EQ(new_db_opt.bytes_per_sync, static_cast<uint64_t>(47));
307 ASSERT_EQ(new_db_opt.wal_bytes_per_sync, static_cast<uint64_t>(48));
308 ASSERT_EQ(new_db_opt.strict_bytes_per_sync, true);
309
310 db_options_map["max_open_files"] = "hello";
311 Status s =
312 GetDBOptionsFromMap(exact, base_db_opt, db_options_map, &new_db_opt);
313 ASSERT_NOK(s);
314 ASSERT_TRUE(s.IsInvalidArgument());
315
316 ASSERT_OK(
317 RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
318 ASSERT_OK(
319 RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
320
321 // unknow options should fail parsing without ignore_unknown_options = true
322 db_options_map["unknown_db_option"] = "1";
323 s = GetDBOptionsFromMap(exact, base_db_opt, db_options_map, &new_db_opt);
324 ASSERT_NOK(s);
325 ASSERT_TRUE(s.IsInvalidArgument());
326 ASSERT_OK(
327 RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
328
329 ASSERT_OK(
330 GetDBOptionsFromMap(loose, base_db_opt, db_options_map, &new_db_opt));
331 ASSERT_OK(
332 RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
333 ASSERT_NOK(
334 RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
335 }
336 #endif // !ROCKSDB_LITE
337
338 #ifndef ROCKSDB_LITE // GetColumnFamilyOptionsFromString is not supported in
339 // ROCKSDB_LITE
TEST_F(OptionsTest,GetColumnFamilyOptionsFromStringTest)340 TEST_F(OptionsTest, GetColumnFamilyOptionsFromStringTest) {
341 ColumnFamilyOptions base_cf_opt;
342 ColumnFamilyOptions new_cf_opt;
343 ConfigOptions config_options;
344 config_options.input_strings_escaped = false;
345 config_options.ignore_unknown_options = false;
346
347 base_cf_opt.table_factory.reset();
348 ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt, "",
349 &new_cf_opt));
350 ASSERT_OK(GetColumnFamilyOptionsFromString(
351 config_options, base_cf_opt, "write_buffer_size=5", &new_cf_opt));
352 ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
353 ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
354 ASSERT_OK(GetColumnFamilyOptionsFromString(
355 config_options, base_cf_opt, "write_buffer_size=6;", &new_cf_opt));
356 ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
357 ASSERT_OK(GetColumnFamilyOptionsFromString(
358 config_options, base_cf_opt, " write_buffer_size = 7 ", &new_cf_opt));
359 ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
360 ASSERT_OK(GetColumnFamilyOptionsFromString(
361 config_options, base_cf_opt, " write_buffer_size = 8 ; ", &new_cf_opt));
362 ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
363 ASSERT_OK(GetColumnFamilyOptionsFromString(
364 config_options, base_cf_opt,
365 "write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
366 ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
367 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
368 ASSERT_OK(GetColumnFamilyOptionsFromString(
369 config_options, base_cf_opt,
370 "write_buffer_size=11; max_write_buffer_number = 12 ;", &new_cf_opt));
371 ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
372 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
373 // Wrong name "max_write_buffer_number_"
374 ASSERT_NOK(GetColumnFamilyOptionsFromString(
375 config_options, base_cf_opt,
376 "write_buffer_size=13;max_write_buffer_number_=14;", &new_cf_opt));
377 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
378 new_cf_opt));
379
380 // Comparator from object registry
381 std::string kCompName = "reverse_comp";
382 ObjectLibrary::Default()->Register<const Comparator>(
383 kCompName,
384 [](const std::string& /*name*/,
385 std::unique_ptr<const Comparator>* /*guard*/,
386 std::string* /* errmsg */) { return ReverseBytewiseComparator(); });
387
388 ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
389 "comparator=" + kCompName + ";",
390 &new_cf_opt));
391 ASSERT_EQ(new_cf_opt.comparator, ReverseBytewiseComparator());
392
393 // MergeOperator from object registry
394 std::unique_ptr<BytesXOROperator> bxo(new BytesXOROperator());
395 std::string kMoName = bxo->Name();
396 ObjectLibrary::Default()->Register<MergeOperator>(
397 kMoName,
398 [](const std::string& /*name*/, std::unique_ptr<MergeOperator>* guard,
399 std::string* /* errmsg */) {
400 guard->reset(new BytesXOROperator());
401 return guard->get();
402 });
403
404 ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
405 "merge_operator=" + kMoName + ";",
406 &new_cf_opt));
407 ASSERT_EQ(kMoName, std::string(new_cf_opt.merge_operator->Name()));
408
409 // Wrong key/value pair
410 Status s = GetColumnFamilyOptionsFromString(
411 config_options, base_cf_opt,
412 "write_buffer_size=13;max_write_buffer_number;", &new_cf_opt);
413 ASSERT_NOK(s);
414 ASSERT_TRUE(s.IsInvalidArgument());
415
416 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
417 new_cf_opt));
418
419 // Error Parsing value
420 s = GetColumnFamilyOptionsFromString(
421 config_options, base_cf_opt,
422 "write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt);
423 ASSERT_NOK(s);
424 ASSERT_TRUE(s.IsInvalidArgument());
425 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
426 new_cf_opt));
427
428 // Missing option name
429 s = GetColumnFamilyOptionsFromString(
430 config_options, base_cf_opt, "write_buffer_size=13; =100;", &new_cf_opt);
431 ASSERT_NOK(s);
432 ASSERT_TRUE(s.IsInvalidArgument());
433 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
434 new_cf_opt));
435
436 const uint64_t kilo = 1024UL;
437 const uint64_t mega = 1024 * kilo;
438 const uint64_t giga = 1024 * mega;
439 const uint64_t tera = 1024 * giga;
440
441 // Units (k)
442 ASSERT_OK(GetColumnFamilyOptionsFromString(
443 config_options, base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
444 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 15 * kilo);
445 // Units (m)
446 ASSERT_OK(GetColumnFamilyOptionsFromString(
447 config_options, base_cf_opt,
448 "max_write_buffer_number=16m;inplace_update_num_locks=17M", &new_cf_opt));
449 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
450 ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17u * mega);
451 // Units (g)
452 ASSERT_OK(GetColumnFamilyOptionsFromString(
453 config_options, base_cf_opt,
454 "write_buffer_size=18g;prefix_extractor=capped:8;"
455 "arena_block_size=19G",
456 &new_cf_opt));
457
458 ASSERT_EQ(new_cf_opt.write_buffer_size, 18 * giga);
459 ASSERT_EQ(new_cf_opt.arena_block_size, 19 * giga);
460 ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
461 std::string prefix_name(new_cf_opt.prefix_extractor->Name());
462 ASSERT_EQ(prefix_name, "rocksdb.CappedPrefix.8");
463
464 // Units (t)
465 ASSERT_OK(GetColumnFamilyOptionsFromString(
466 config_options, base_cf_opt, "write_buffer_size=20t;arena_block_size=21T",
467 &new_cf_opt));
468 ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
469 ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
470
471 // Nested block based table options
472 // Empty
473 ASSERT_OK(GetColumnFamilyOptionsFromString(
474 config_options, base_cf_opt,
475 "write_buffer_size=10;max_write_buffer_number=16;"
476 "block_based_table_factory={};arena_block_size=1024",
477 &new_cf_opt));
478 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
479 // Non-empty
480 ASSERT_OK(GetColumnFamilyOptionsFromString(
481 config_options, base_cf_opt,
482 "write_buffer_size=10;max_write_buffer_number=16;"
483 "block_based_table_factory={block_cache=1M;block_size=4;};"
484 "arena_block_size=1024",
485 &new_cf_opt));
486 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
487 // Last one
488 ASSERT_OK(GetColumnFamilyOptionsFromString(
489 config_options, base_cf_opt,
490 "write_buffer_size=10;max_write_buffer_number=16;"
491 "block_based_table_factory={block_cache=1M;block_size=4;}",
492 &new_cf_opt));
493 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
494 // Mismatch curly braces
495 ASSERT_NOK(GetColumnFamilyOptionsFromString(
496 config_options, base_cf_opt,
497 "write_buffer_size=10;max_write_buffer_number=16;"
498 "block_based_table_factory={{{block_size=4;};"
499 "arena_block_size=1024",
500 &new_cf_opt));
501 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
502 new_cf_opt));
503
504 // Unexpected chars after closing curly brace
505 ASSERT_NOK(GetColumnFamilyOptionsFromString(
506 config_options, base_cf_opt,
507 "write_buffer_size=10;max_write_buffer_number=16;"
508 "block_based_table_factory={block_size=4;}};"
509 "arena_block_size=1024",
510 &new_cf_opt));
511 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
512 new_cf_opt));
513
514 ASSERT_NOK(GetColumnFamilyOptionsFromString(
515 config_options, base_cf_opt,
516 "write_buffer_size=10;max_write_buffer_number=16;"
517 "block_based_table_factory={block_size=4;}xdfa;"
518 "arena_block_size=1024",
519 &new_cf_opt));
520 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
521 new_cf_opt));
522
523 ASSERT_NOK(GetColumnFamilyOptionsFromString(
524 config_options, base_cf_opt,
525 "write_buffer_size=10;max_write_buffer_number=16;"
526 "block_based_table_factory={block_size=4;}xdfa",
527 &new_cf_opt));
528 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
529 new_cf_opt));
530
531 // Invalid block based table option
532 ASSERT_NOK(GetColumnFamilyOptionsFromString(
533 config_options, base_cf_opt,
534 "write_buffer_size=10;max_write_buffer_number=16;"
535 "block_based_table_factory={xx_block_size=4;}",
536 &new_cf_opt));
537 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
538 new_cf_opt));
539
540 ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
541 "optimize_filters_for_hits=true",
542 &new_cf_opt));
543 ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
544 "optimize_filters_for_hits=false",
545 &new_cf_opt));
546
547 ASSERT_NOK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
548 "optimize_filters_for_hits=junk",
549 &new_cf_opt));
550 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
551 new_cf_opt));
552
553 // Nested plain table options
554 // Empty
555 ASSERT_OK(GetColumnFamilyOptionsFromString(
556 config_options, base_cf_opt,
557 "write_buffer_size=10;max_write_buffer_number=16;"
558 "plain_table_factory={};arena_block_size=1024",
559 &new_cf_opt));
560 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
561 ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
562 // Non-empty
563 ASSERT_OK(GetColumnFamilyOptionsFromString(
564 config_options, base_cf_opt,
565 "write_buffer_size=10;max_write_buffer_number=16;"
566 "plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
567 "arena_block_size=1024",
568 &new_cf_opt));
569 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
570 ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
571
572 // memtable factory
573 ASSERT_OK(GetColumnFamilyOptionsFromString(
574 config_options, base_cf_opt,
575 "write_buffer_size=10;max_write_buffer_number=16;"
576 "memtable=skip_list:10;arena_block_size=1024",
577 &new_cf_opt));
578 ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
579 ASSERT_EQ(std::string(new_cf_opt.memtable_factory->Name()), "SkipListFactory");
580 }
581
TEST_F(OptionsTest,CompressionOptionsFromString)582 TEST_F(OptionsTest, CompressionOptionsFromString) {
583 ColumnFamilyOptions base_cf_opt;
584 ColumnFamilyOptions new_cf_opt;
585 ConfigOptions config_options;
586 std::string opts_str;
587 config_options.ignore_unknown_options = false;
588 CompressionOptions dflt;
589 // Test with some optional values removed....
590 ASSERT_OK(
591 GetColumnFamilyOptionsFromString(config_options, ColumnFamilyOptions(),
592 "compression_opts=3:4:5; "
593 "bottommost_compression_opts=4:5:6:7",
594 &base_cf_opt));
595 ASSERT_EQ(base_cf_opt.compression_opts.window_bits, 3);
596 ASSERT_EQ(base_cf_opt.compression_opts.level, 4);
597 ASSERT_EQ(base_cf_opt.compression_opts.strategy, 5);
598 ASSERT_EQ(base_cf_opt.compression_opts.max_dict_bytes, dflt.max_dict_bytes);
599 ASSERT_EQ(base_cf_opt.compression_opts.zstd_max_train_bytes,
600 dflt.zstd_max_train_bytes);
601 ASSERT_EQ(base_cf_opt.compression_opts.parallel_threads,
602 dflt.parallel_threads);
603 ASSERT_EQ(base_cf_opt.compression_opts.enabled, dflt.enabled);
604 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.window_bits, 4);
605 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.level, 5);
606 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.strategy, 6);
607 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.max_dict_bytes, 7u);
608 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.zstd_max_train_bytes,
609 dflt.zstd_max_train_bytes);
610 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.parallel_threads,
611 dflt.parallel_threads);
612 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.enabled, dflt.enabled);
613
614 ASSERT_OK(GetColumnFamilyOptionsFromString(
615 config_options, ColumnFamilyOptions(),
616 "compression_opts=4:5:6:7:8:9:true; "
617 "bottommost_compression_opts=5:6:7:8:9:false",
618 &base_cf_opt));
619 ASSERT_EQ(base_cf_opt.compression_opts.window_bits, 4);
620 ASSERT_EQ(base_cf_opt.compression_opts.level, 5);
621 ASSERT_EQ(base_cf_opt.compression_opts.strategy, 6);
622 ASSERT_EQ(base_cf_opt.compression_opts.max_dict_bytes, 7u);
623 ASSERT_EQ(base_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
624 ASSERT_EQ(base_cf_opt.compression_opts.parallel_threads, 9u);
625 ASSERT_EQ(base_cf_opt.compression_opts.enabled, true);
626 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.window_bits, 5);
627 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.level, 6);
628 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.strategy, 7);
629 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
630 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 9u);
631 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.parallel_threads,
632 dflt.parallel_threads);
633 ASSERT_EQ(base_cf_opt.bottommost_compression_opts.enabled, false);
634
635 ASSERT_OK(
636 GetStringFromColumnFamilyOptions(config_options, base_cf_opt, &opts_str));
637 ASSERT_OK(GetColumnFamilyOptionsFromString(
638 config_options, ColumnFamilyOptions(), opts_str, &new_cf_opt));
639 ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
640 ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
641 ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
642 ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7u);
643 ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
644 ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads, 9u);
645 ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
646 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 5);
647 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
648 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
649 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
650 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 9u);
651 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
652 dflt.parallel_threads);
653 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, false);
654
655 // Test as struct values
656 ASSERT_OK(GetColumnFamilyOptionsFromString(
657 config_options, ColumnFamilyOptions(),
658 "compression_opts={window_bits=5; level=6; strategy=7; max_dict_bytes=8;"
659 "zstd_max_train_bytes=9;parallel_threads=10;enabled=true}; "
660 "bottommost_compression_opts={window_bits=4; level=5; strategy=6;"
661 " max_dict_bytes=7;zstd_max_train_bytes=8;parallel_threads=9;"
662 "enabled=false}; ",
663 &new_cf_opt));
664 ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 5);
665 ASSERT_EQ(new_cf_opt.compression_opts.level, 6);
666 ASSERT_EQ(new_cf_opt.compression_opts.strategy, 7);
667 ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 8u);
668 ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 9u);
669 ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads, 10u);
670 ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
671 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 4);
672 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 5);
673 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 6);
674 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 7u);
675 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 8u);
676 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads, 9u);
677 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, false);
678
679 ASSERT_OK(GetColumnFamilyOptionsFromString(
680 config_options, base_cf_opt,
681 "compression_opts={window_bits=4; strategy=5;};"
682 "bottommost_compression_opts={level=6; strategy=7;}",
683 &new_cf_opt));
684 ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
685 ASSERT_EQ(new_cf_opt.compression_opts.strategy, 5);
686 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
687 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
688
689 ASSERT_EQ(new_cf_opt.compression_opts.level,
690 base_cf_opt.compression_opts.level);
691 ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes,
692 base_cf_opt.compression_opts.max_dict_bytes);
693 ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes,
694 base_cf_opt.compression_opts.zstd_max_train_bytes);
695 ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads,
696 base_cf_opt.compression_opts.parallel_threads);
697 ASSERT_EQ(new_cf_opt.compression_opts.enabled,
698 base_cf_opt.compression_opts.enabled);
699 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits,
700 base_cf_opt.bottommost_compression_opts.window_bits);
701 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes,
702 base_cf_opt.bottommost_compression_opts.max_dict_bytes);
703 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes,
704 base_cf_opt.bottommost_compression_opts.zstd_max_train_bytes);
705 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
706 base_cf_opt.bottommost_compression_opts.parallel_threads);
707 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled,
708 base_cf_opt.bottommost_compression_opts.enabled);
709
710 // Test a few individual struct values
711 ASSERT_OK(GetColumnFamilyOptionsFromString(
712 config_options, base_cf_opt,
713 "compression_opts.enabled=false; "
714 "bottommost_compression_opts.enabled=true; ",
715 &new_cf_opt));
716 ASSERT_EQ(new_cf_opt.compression_opts.enabled, false);
717 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, true);
718
719 // Now test some illegal values
720 ConfigOptions ignore;
721 ignore.ignore_unknown_options = true;
722 ASSERT_NOK(GetColumnFamilyOptionsFromString(
723 config_options, ColumnFamilyOptions(),
724 "compression_opts=5:6:7:8:9:x:false", &base_cf_opt));
725 ASSERT_OK(GetColumnFamilyOptionsFromString(
726 ignore, ColumnFamilyOptions(), "compression_opts=5:6:7:8:9:x:false",
727 &base_cf_opt));
728 ASSERT_OK(GetColumnFamilyOptionsFromString(
729 config_options, ColumnFamilyOptions(),
730 "compression_opts=1:2:3:4:5:6:true:8", &base_cf_opt));
731 ASSERT_OK(GetColumnFamilyOptionsFromString(
732 ignore, ColumnFamilyOptions(), "compression_opts=1:2:3:4:5:6:true:8",
733 &base_cf_opt));
734 ASSERT_NOK(GetColumnFamilyOptionsFromString(
735 config_options, ColumnFamilyOptions(),
736 "compression_opts=1:2:3:4:5:6:true:8:9", &base_cf_opt));
737 ASSERT_OK(GetColumnFamilyOptionsFromString(
738 ignore, ColumnFamilyOptions(), "compression_opts=1:2:3:4:5:6:true:8:9",
739 &base_cf_opt));
740 ASSERT_NOK(GetColumnFamilyOptionsFromString(
741 config_options, ColumnFamilyOptions(), "compression_opts={unknown=bad;}",
742 &base_cf_opt));
743 ASSERT_OK(GetColumnFamilyOptionsFromString(ignore, ColumnFamilyOptions(),
744 "compression_opts={unknown=bad;}",
745 &base_cf_opt));
746 ASSERT_NOK(GetColumnFamilyOptionsFromString(
747 config_options, ColumnFamilyOptions(), "compression_opts.unknown=bad",
748 &base_cf_opt));
749 ASSERT_OK(GetColumnFamilyOptionsFromString(ignore, ColumnFamilyOptions(),
750 "compression_opts.unknown=bad",
751 &base_cf_opt));
752 }
753
TEST_F(OptionsTest,OldInterfaceTest)754 TEST_F(OptionsTest, OldInterfaceTest) {
755 ColumnFamilyOptions base_cf_opt;
756 ColumnFamilyOptions new_cf_opt;
757 ConfigOptions exact;
758
759 ASSERT_OK(GetColumnFamilyOptionsFromString(
760 base_cf_opt,
761 "write_buffer_size=18;prefix_extractor=capped:8;"
762 "arena_block_size=19",
763 &new_cf_opt));
764
765 ASSERT_EQ(new_cf_opt.write_buffer_size, 18);
766 ASSERT_EQ(new_cf_opt.arena_block_size, 19);
767 ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
768
769 // And with a bad option
770 ASSERT_NOK(GetColumnFamilyOptionsFromString(
771 base_cf_opt,
772 "write_buffer_size=10;max_write_buffer_number=16;"
773 "block_based_table_factory={xx_block_size=4;}",
774 &new_cf_opt));
775 ASSERT_OK(
776 RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
777
778 std::unordered_map<std::string, std::string> cf_options_map = {
779 {"write_buffer_size", "1"},
780 {"max_write_buffer_number", "2"},
781 {"min_write_buffer_number_to_merge", "3"},
782 };
783 ASSERT_OK(
784 GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map, &new_cf_opt));
785 cf_options_map["unknown_option"] = "1";
786 ASSERT_NOK(
787 GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map, &new_cf_opt));
788 ASSERT_OK(
789 RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
790 ASSERT_OK(GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map,
791 &new_cf_opt, true, true));
792
793 DBOptions base_db_opt;
794 DBOptions new_db_opt;
795 std::unordered_map<std::string, std::string> db_options_map = {
796 {"create_if_missing", "false"},
797 {"create_missing_column_families", "true"},
798 {"error_if_exists", "false"},
799 {"paranoid_checks", "true"},
800 {"track_and_verify_wals_in_manifest", "true"},
801 {"max_open_files", "32"},
802 };
803 ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
804 ASSERT_EQ(new_db_opt.create_if_missing, false);
805 ASSERT_EQ(new_db_opt.create_missing_column_families, true);
806 ASSERT_EQ(new_db_opt.error_if_exists, false);
807 ASSERT_EQ(new_db_opt.paranoid_checks, true);
808 ASSERT_EQ(new_db_opt.track_and_verify_wals_in_manifest, true);
809 ASSERT_EQ(new_db_opt.max_open_files, 32);
810 db_options_map["unknown_option"] = "1";
811 Status s = GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt);
812 ASSERT_NOK(s);
813 ASSERT_TRUE(s.IsInvalidArgument());
814
815 ASSERT_OK(
816 RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
817 ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt, true,
818 true));
819 ASSERT_OK(GetDBOptionsFromString(
820 base_db_opt,
821 "create_if_missing=false;error_if_exists=false;max_open_files=42;",
822 &new_db_opt));
823 ASSERT_EQ(new_db_opt.create_if_missing, false);
824 ASSERT_EQ(new_db_opt.error_if_exists, false);
825 ASSERT_EQ(new_db_opt.max_open_files, 42);
826 s = GetDBOptionsFromString(
827 base_db_opt,
828 "create_if_missing=false;error_if_exists=false;max_open_files=42;"
829 "unknown_option=1;",
830 &new_db_opt);
831 ASSERT_NOK(s);
832 ASSERT_TRUE(s.IsInvalidArgument());
833 ASSERT_OK(
834 RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
835 }
836
837 #endif // !ROCKSDB_LITE
838
839 #ifndef ROCKSDB_LITE // GetBlockBasedTableOptionsFromString is not supported
TEST_F(OptionsTest,GetBlockBasedTableOptionsFromString)840 TEST_F(OptionsTest, GetBlockBasedTableOptionsFromString) {
841 BlockBasedTableOptions table_opt;
842 BlockBasedTableOptions new_opt;
843 ConfigOptions config_options;
844 config_options.input_strings_escaped = false;
845 config_options.ignore_unknown_options = false;
846
847 // make sure default values are overwritten by something else
848 ASSERT_OK(GetBlockBasedTableOptionsFromString(
849 config_options, table_opt,
850 "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
851 "checksum=kxxHash;hash_index_allow_collision=1;"
852 "block_cache=1M;block_cache_compressed=1k;block_size=1024;"
853 "block_size_deviation=8;block_restart_interval=4;"
854 "format_version=5;whole_key_filtering=1;"
855 "filter_policy=bloomfilter:4.567:false;"
856 // A bug caused read_amp_bytes_per_bit to be a large integer in OPTIONS
857 // file generated by 6.10 to 6.14. Though bug is fixed in these releases,
858 // we need to handle the case of loading OPTIONS file generated before the
859 // fix.
860 "read_amp_bytes_per_bit=17179869185;",
861 &new_opt));
862 ASSERT_TRUE(new_opt.cache_index_and_filter_blocks);
863 ASSERT_EQ(new_opt.index_type, BlockBasedTableOptions::kHashSearch);
864 ASSERT_EQ(new_opt.checksum, ChecksumType::kxxHash);
865 ASSERT_TRUE(new_opt.hash_index_allow_collision);
866 ASSERT_TRUE(new_opt.block_cache != nullptr);
867 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
868 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
869 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL);
870 ASSERT_EQ(new_opt.block_size, 1024UL);
871 ASSERT_EQ(new_opt.block_size_deviation, 8);
872 ASSERT_EQ(new_opt.block_restart_interval, 4);
873 ASSERT_EQ(new_opt.format_version, 5U);
874 ASSERT_EQ(new_opt.whole_key_filtering, true);
875 ASSERT_TRUE(new_opt.filter_policy != nullptr);
876 const BloomFilterPolicy* bfp =
877 dynamic_cast<const BloomFilterPolicy*>(new_opt.filter_policy.get());
878 EXPECT_EQ(bfp->GetMillibitsPerKey(), 4567);
879 EXPECT_EQ(bfp->GetWholeBitsPerKey(), 5);
880 EXPECT_EQ(bfp->GetMode(), BloomFilterPolicy::kAutoBloom);
881 // Verify that only the lower 32bits are stored in
882 // new_opt.read_amp_bytes_per_bit.
883 EXPECT_EQ(1U, new_opt.read_amp_bytes_per_bit);
884
885 // unknown option
886 Status s = GetBlockBasedTableOptionsFromString(
887 config_options, table_opt,
888 "cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
889 "bad_option=1",
890 &new_opt);
891 ASSERT_NOK(s);
892 ASSERT_TRUE(s.IsInvalidArgument());
893 ASSERT_EQ(static_cast<bool>(table_opt.cache_index_and_filter_blocks),
894 new_opt.cache_index_and_filter_blocks);
895 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
896
897 // unrecognized index type
898 s = GetBlockBasedTableOptionsFromString(
899 config_options, table_opt,
900 "cache_index_and_filter_blocks=1;index_type=kBinarySearchXX", &new_opt);
901 ASSERT_NOK(s);
902 ASSERT_TRUE(s.IsInvalidArgument());
903 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
904 new_opt.cache_index_and_filter_blocks);
905 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
906
907 // unrecognized checksum type
908 ASSERT_NOK(GetBlockBasedTableOptionsFromString(
909 config_options, table_opt,
910 "cache_index_and_filter_blocks=1;checksum=kxxHashXX", &new_opt));
911 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
912 new_opt.cache_index_and_filter_blocks);
913 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
914
915 // unrecognized filter policy name
916 s = GetBlockBasedTableOptionsFromString(config_options, table_opt,
917 "cache_index_and_filter_blocks=1;"
918 "filter_policy=bloomfilterxx:4:true",
919 &new_opt);
920 ASSERT_NOK(s);
921 ASSERT_TRUE(s.IsInvalidArgument());
922 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
923 new_opt.cache_index_and_filter_blocks);
924 ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
925
926 // unrecognized filter policy config
927 s = GetBlockBasedTableOptionsFromString(config_options, table_opt,
928 "cache_index_and_filter_blocks=1;"
929 "filter_policy=bloomfilter:4",
930 &new_opt);
931 ASSERT_NOK(s);
932 ASSERT_TRUE(s.IsInvalidArgument());
933 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
934 new_opt.cache_index_and_filter_blocks);
935 ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
936
937 // Experimental Ribbon filter policy
938 ASSERT_OK(GetBlockBasedTableOptionsFromString(
939 config_options, table_opt, "filter_policy=experimental_ribbon:5.678;",
940 &new_opt));
941 ASSERT_TRUE(new_opt.filter_policy != nullptr);
942 bfp = dynamic_cast<const BloomFilterPolicy*>(new_opt.filter_policy.get());
943 EXPECT_EQ(bfp->GetMillibitsPerKey(), 5678);
944 EXPECT_EQ(bfp->GetMode(), BloomFilterPolicy::kStandard128Ribbon);
945
946 // Check block cache options are overwritten when specified
947 // in new format as a struct.
948 ASSERT_OK(GetBlockBasedTableOptionsFromString(
949 config_options, table_opt,
950 "block_cache={capacity=1M;num_shard_bits=4;"
951 "strict_capacity_limit=true;high_pri_pool_ratio=0.5;};"
952 "block_cache_compressed={capacity=1M;num_shard_bits=4;"
953 "strict_capacity_limit=true;high_pri_pool_ratio=0.5;}",
954 &new_opt));
955 ASSERT_TRUE(new_opt.block_cache != nullptr);
956 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
957 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
958 new_opt.block_cache)->GetNumShardBits(), 4);
959 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
960 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
961 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
962 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
963 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
964 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
965 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
966 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
967 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
968 new_opt.block_cache_compressed)->GetHighPriPoolRatio(),
969 0.5);
970
971 // Set only block cache capacity. Check other values are
972 // reset to default values.
973 ASSERT_OK(GetBlockBasedTableOptionsFromString(
974 config_options, table_opt,
975 "block_cache={capacity=2M};"
976 "block_cache_compressed={capacity=2M}",
977 &new_opt));
978 ASSERT_TRUE(new_opt.block_cache != nullptr);
979 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 2*1024UL*1024UL);
980 // Default values
981 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
982 new_opt.block_cache)->GetNumShardBits(),
983 GetDefaultCacheShardBits(new_opt.block_cache->GetCapacity()));
984 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
985 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
986 ->GetHighPriPoolRatio(),
987 0.5);
988 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
989 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 2*1024UL*1024UL);
990 // Default values
991 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
992 new_opt.block_cache_compressed)->GetNumShardBits(),
993 GetDefaultCacheShardBits(
994 new_opt.block_cache_compressed->GetCapacity()));
995 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
996 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
997 ->GetHighPriPoolRatio(),
998 0.5);
999
1000 // Set couple of block cache options.
1001 ASSERT_OK(GetBlockBasedTableOptionsFromString(
1002 config_options, table_opt,
1003 "block_cache={num_shard_bits=5;high_pri_pool_ratio=0.5;};"
1004 "block_cache_compressed={num_shard_bits=5;"
1005 "high_pri_pool_ratio=0.0;}",
1006 &new_opt));
1007 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 0);
1008 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1009 new_opt.block_cache)->GetNumShardBits(), 5);
1010 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
1011 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
1012 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
1013 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
1014 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 0);
1015 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1016 new_opt.block_cache_compressed)->GetNumShardBits(), 5);
1017 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
1018 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
1019 ->GetHighPriPoolRatio(),
1020 0.0);
1021
1022 // Set couple of block cache options.
1023 ASSERT_OK(GetBlockBasedTableOptionsFromString(
1024 config_options, table_opt,
1025 "block_cache={capacity=1M;num_shard_bits=4;"
1026 "strict_capacity_limit=true;};"
1027 "block_cache_compressed={capacity=1M;num_shard_bits=4;"
1028 "strict_capacity_limit=true;}",
1029 &new_opt));
1030 ASSERT_TRUE(new_opt.block_cache != nullptr);
1031 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
1032 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1033 new_opt.block_cache)->GetNumShardBits(), 4);
1034 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
1035 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
1036 ->GetHighPriPoolRatio(),
1037 0.5);
1038 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
1039 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
1040 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1041 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
1042 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
1043 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
1044 ->GetHighPriPoolRatio(),
1045 0.5);
1046 }
1047 #endif // !ROCKSDB_LITE
1048
1049
1050 #ifndef ROCKSDB_LITE // GetPlainTableOptionsFromString is not supported
TEST_F(OptionsTest,GetPlainTableOptionsFromString)1051 TEST_F(OptionsTest, GetPlainTableOptionsFromString) {
1052 PlainTableOptions table_opt;
1053 PlainTableOptions new_opt;
1054 ConfigOptions config_options;
1055 config_options.input_strings_escaped = false;
1056 config_options.ignore_unknown_options = false;
1057 // make sure default values are overwritten by something else
1058 ASSERT_OK(GetPlainTableOptionsFromString(
1059 config_options, table_opt,
1060 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
1061 "index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
1062 "full_scan_mode=true;store_index_in_file=true",
1063 &new_opt));
1064 ASSERT_EQ(new_opt.user_key_len, 66u);
1065 ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
1066 ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
1067 ASSERT_EQ(new_opt.index_sparseness, 8);
1068 ASSERT_EQ(new_opt.huge_page_tlb_size, 4);
1069 ASSERT_EQ(new_opt.encoding_type, EncodingType::kPrefix);
1070 ASSERT_TRUE(new_opt.full_scan_mode);
1071 ASSERT_TRUE(new_opt.store_index_in_file);
1072
1073 // unknown option
1074 Status s = GetPlainTableOptionsFromString(
1075 config_options, table_opt,
1076 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
1077 "bad_option=1",
1078 &new_opt);
1079 ASSERT_NOK(s);
1080 ASSERT_TRUE(s.IsInvalidArgument());
1081
1082 // unrecognized EncodingType
1083 s = GetPlainTableOptionsFromString(
1084 config_options, table_opt,
1085 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
1086 "encoding_type=kPrefixXX",
1087 &new_opt);
1088 ASSERT_NOK(s);
1089 ASSERT_TRUE(s.IsInvalidArgument());
1090 }
1091 #endif // !ROCKSDB_LITE
1092
1093 #ifndef ROCKSDB_LITE // GetMemTableRepFactoryFromString is not supported
TEST_F(OptionsTest,GetMemTableRepFactoryFromString)1094 TEST_F(OptionsTest, GetMemTableRepFactoryFromString) {
1095 std::unique_ptr<MemTableRepFactory> new_mem_factory = nullptr;
1096
1097 ASSERT_OK(GetMemTableRepFactoryFromString("skip_list", &new_mem_factory));
1098 ASSERT_OK(GetMemTableRepFactoryFromString("skip_list:16", &new_mem_factory));
1099 ASSERT_EQ(std::string(new_mem_factory->Name()), "SkipListFactory");
1100 ASSERT_NOK(GetMemTableRepFactoryFromString("skip_list:16:invalid_opt",
1101 &new_mem_factory));
1102
1103 ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash", &new_mem_factory));
1104 ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash:1000",
1105 &new_mem_factory));
1106 ASSERT_EQ(std::string(new_mem_factory->Name()), "HashSkipListRepFactory");
1107 ASSERT_NOK(GetMemTableRepFactoryFromString("prefix_hash:1000:invalid_opt",
1108 &new_mem_factory));
1109
1110 ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist",
1111 &new_mem_factory));
1112 ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist:1000",
1113 &new_mem_factory));
1114 ASSERT_EQ(std::string(new_mem_factory->Name()), "HashLinkListRepFactory");
1115 ASSERT_NOK(GetMemTableRepFactoryFromString("hash_linkedlist:1000:invalid_opt",
1116 &new_mem_factory));
1117
1118 ASSERT_OK(GetMemTableRepFactoryFromString("vector", &new_mem_factory));
1119 ASSERT_OK(GetMemTableRepFactoryFromString("vector:1024", &new_mem_factory));
1120 ASSERT_EQ(std::string(new_mem_factory->Name()), "VectorRepFactory");
1121 ASSERT_NOK(GetMemTableRepFactoryFromString("vector:1024:invalid_opt",
1122 &new_mem_factory));
1123
1124 ASSERT_NOK(GetMemTableRepFactoryFromString("cuckoo", &new_mem_factory));
1125 // CuckooHash memtable is already removed.
1126 ASSERT_NOK(GetMemTableRepFactoryFromString("cuckoo:1024", &new_mem_factory));
1127
1128 ASSERT_NOK(GetMemTableRepFactoryFromString("bad_factory", &new_mem_factory));
1129 }
1130 #endif // !ROCKSDB_LITE
1131
1132 #ifndef ROCKSDB_LITE // GetOptionsFromString is not supported in RocksDB Lite
TEST_F(OptionsTest,GetOptionsFromStringTest)1133 TEST_F(OptionsTest, GetOptionsFromStringTest) {
1134 Options base_options, new_options;
1135 ConfigOptions config_options;
1136 config_options.input_strings_escaped = false;
1137 config_options.ignore_unknown_options = false;
1138
1139 base_options.write_buffer_size = 20;
1140 base_options.min_write_buffer_number_to_merge = 15;
1141 BlockBasedTableOptions block_based_table_options;
1142 block_based_table_options.cache_index_and_filter_blocks = true;
1143 base_options.table_factory.reset(
1144 NewBlockBasedTableFactory(block_based_table_options));
1145
1146 // Register an Env with object registry.
1147 const static char* kCustomEnvName = "CustomEnv";
1148 class CustomEnv : public EnvWrapper {
1149 public:
1150 explicit CustomEnv(Env* _target) : EnvWrapper(_target) {}
1151 };
1152
1153 ObjectLibrary::Default()->Register<Env>(
1154 kCustomEnvName,
1155 [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
1156 std::string* /* errmsg */) {
1157 static CustomEnv env(Env::Default());
1158 return &env;
1159 });
1160
1161 ASSERT_OK(GetOptionsFromString(
1162 config_options, base_options,
1163 "write_buffer_size=10;max_write_buffer_number=16;"
1164 "block_based_table_factory={block_cache=1M;block_size=4;};"
1165 "compression_opts=4:5:6;create_if_missing=true;max_open_files=1;"
1166 "bottommost_compression_opts=5:6:7;create_if_missing=true;max_open_files="
1167 "1;"
1168 "rate_limiter_bytes_per_sec=1024;env=CustomEnv",
1169 &new_options));
1170
1171 ASSERT_EQ(new_options.compression_opts.window_bits, 4);
1172 ASSERT_EQ(new_options.compression_opts.level, 5);
1173 ASSERT_EQ(new_options.compression_opts.strategy, 6);
1174 ASSERT_EQ(new_options.compression_opts.max_dict_bytes, 0u);
1175 ASSERT_EQ(new_options.compression_opts.zstd_max_train_bytes, 0u);
1176 ASSERT_EQ(new_options.compression_opts.parallel_threads, 1u);
1177 ASSERT_EQ(new_options.compression_opts.enabled, false);
1178 ASSERT_EQ(new_options.bottommost_compression, kDisableCompressionOption);
1179 ASSERT_EQ(new_options.bottommost_compression_opts.window_bits, 5);
1180 ASSERT_EQ(new_options.bottommost_compression_opts.level, 6);
1181 ASSERT_EQ(new_options.bottommost_compression_opts.strategy, 7);
1182 ASSERT_EQ(new_options.bottommost_compression_opts.max_dict_bytes, 0u);
1183 ASSERT_EQ(new_options.bottommost_compression_opts.zstd_max_train_bytes, 0u);
1184 ASSERT_EQ(new_options.bottommost_compression_opts.parallel_threads, 1u);
1185 ASSERT_EQ(new_options.bottommost_compression_opts.enabled, false);
1186 ASSERT_EQ(new_options.write_buffer_size, 10U);
1187 ASSERT_EQ(new_options.max_write_buffer_number, 16);
1188 const auto new_bbto =
1189 new_options.table_factory->GetOptions<BlockBasedTableOptions>();
1190 ASSERT_NE(new_bbto, nullptr);
1191 ASSERT_EQ(new_bbto->block_cache->GetCapacity(), 1U << 20);
1192 ASSERT_EQ(new_bbto->block_size, 4U);
1193 // don't overwrite block based table options
1194 ASSERT_TRUE(new_bbto->cache_index_and_filter_blocks);
1195
1196 ASSERT_EQ(new_options.create_if_missing, true);
1197 ASSERT_EQ(new_options.max_open_files, 1);
1198 ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
1199 Env* newEnv = new_options.env;
1200 ASSERT_OK(Env::LoadEnv(kCustomEnvName, &newEnv));
1201 ASSERT_EQ(newEnv, new_options.env);
1202
1203 config_options.ignore_unknown_options = false;
1204 // Test a bad value for a DBOption returns a failure
1205 base_options.dump_malloc_stats = false;
1206 base_options.write_buffer_size = 1024;
1207 Options bad_options = new_options;
1208 Status s = GetOptionsFromString(config_options, base_options,
1209 "create_if_missing=XX;dump_malloc_stats=true",
1210 &bad_options);
1211 ASSERT_NOK(s);
1212 ASSERT_TRUE(s.IsInvalidArgument());
1213 ASSERT_EQ(bad_options.dump_malloc_stats, false);
1214
1215 bad_options = new_options;
1216 s = GetOptionsFromString(config_options, base_options,
1217 "write_buffer_size=XX;dump_malloc_stats=true",
1218 &bad_options);
1219 ASSERT_NOK(s);
1220 ASSERT_TRUE(s.IsInvalidArgument());
1221
1222 ASSERT_EQ(bad_options.dump_malloc_stats, false);
1223
1224 // Test a bad value for a TableFactory Option returns a failure
1225 bad_options = new_options;
1226 s = GetOptionsFromString(config_options, base_options,
1227 "write_buffer_size=16;dump_malloc_stats=true"
1228 "block_based_table_factory={block_size=XX;};",
1229 &bad_options);
1230 ASSERT_TRUE(s.IsInvalidArgument());
1231 ASSERT_EQ(bad_options.dump_malloc_stats, false);
1232 ASSERT_EQ(bad_options.write_buffer_size, 1024);
1233
1234 config_options.ignore_unknown_options = true;
1235 ASSERT_OK(GetOptionsFromString(config_options, base_options,
1236 "create_if_missing=XX;dump_malloc_stats=true;"
1237 "write_buffer_size=XX;"
1238 "block_based_table_factory={block_size=XX;};",
1239 &bad_options));
1240 ASSERT_EQ(bad_options.create_if_missing, base_options.create_if_missing);
1241 ASSERT_EQ(bad_options.dump_malloc_stats, true);
1242 ASSERT_EQ(bad_options.write_buffer_size, base_options.write_buffer_size);
1243
1244 // Test the old interface
1245 ASSERT_OK(GetOptionsFromString(
1246 base_options,
1247 "write_buffer_size=22;max_write_buffer_number=33;max_open_files=44;",
1248 &new_options));
1249 ASSERT_EQ(new_options.write_buffer_size, 22U);
1250 ASSERT_EQ(new_options.max_write_buffer_number, 33);
1251 ASSERT_EQ(new_options.max_open_files, 44);
1252 }
1253
TEST_F(OptionsTest,DBOptionsSerialization)1254 TEST_F(OptionsTest, DBOptionsSerialization) {
1255 Options base_options, new_options;
1256 Random rnd(301);
1257 ConfigOptions config_options;
1258 config_options.input_strings_escaped = false;
1259 config_options.ignore_unknown_options = false;
1260
1261 // Phase 1: Make big change in base_options
1262 test::RandomInitDBOptions(&base_options, &rnd);
1263
1264 // Phase 2: obtain a string from base_option
1265 std::string base_options_file_content;
1266 ASSERT_OK(GetStringFromDBOptions(config_options, base_options,
1267 &base_options_file_content));
1268
1269 // Phase 3: Set new_options from the derived string and expect
1270 // new_options == base_options
1271 ASSERT_OK(GetDBOptionsFromString(config_options, DBOptions(),
1272 base_options_file_content, &new_options));
1273 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_options,
1274 new_options));
1275 }
1276
TEST_F(OptionsTest,OptionsComposeDecompose)1277 TEST_F(OptionsTest, OptionsComposeDecompose) {
1278 // build an Options from DBOptions + CFOptions, then decompose it to verify
1279 // we get same constituent options.
1280 DBOptions base_db_opts;
1281 ColumnFamilyOptions base_cf_opts;
1282 ConfigOptions
1283 config_options; // Use default for ignore(false) and check (exact)
1284 config_options.input_strings_escaped = false;
1285
1286 Random rnd(301);
1287 test::RandomInitDBOptions(&base_db_opts, &rnd);
1288 test::RandomInitCFOptions(&base_cf_opts, base_db_opts, &rnd);
1289
1290 Options base_opts(base_db_opts, base_cf_opts);
1291 DBOptions new_db_opts(base_opts);
1292 ColumnFamilyOptions new_cf_opts(base_opts);
1293
1294 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_db_opts,
1295 new_db_opts));
1296 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opts,
1297 new_cf_opts));
1298 delete new_cf_opts.compaction_filter;
1299 }
1300
TEST_F(OptionsTest,DBOptionsComposeImmutable)1301 TEST_F(OptionsTest, DBOptionsComposeImmutable) {
1302 // Build a DBOptions from an Immutable/Mutable one and verify that
1303 // we get same constituent options.
1304 ConfigOptions config_options;
1305 Random rnd(301);
1306 DBOptions base_opts, new_opts;
1307 test::RandomInitDBOptions(&base_opts, &rnd);
1308 MutableDBOptions m_opts(base_opts);
1309 ImmutableDBOptions i_opts(base_opts);
1310 new_opts = BuildDBOptions(i_opts, m_opts);
1311 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_opts,
1312 new_opts));
1313 }
1314
TEST_F(OptionsTest,GetMutableDBOptions)1315 TEST_F(OptionsTest, GetMutableDBOptions) {
1316 Random rnd(228);
1317 DBOptions base_opts;
1318 std::string opts_str;
1319 std::unordered_map<std::string, std::string> opts_map;
1320 ConfigOptions config_options;
1321
1322 test::RandomInitDBOptions(&base_opts, &rnd);
1323 ImmutableDBOptions i_opts(base_opts);
1324 MutableDBOptions m_opts(base_opts);
1325 MutableDBOptions new_opts;
1326 ASSERT_OK(GetStringFromMutableDBOptions(config_options, m_opts, &opts_str));
1327 ASSERT_OK(StringToMap(opts_str, &opts_map));
1328 ASSERT_OK(GetMutableDBOptionsFromStrings(m_opts, opts_map, &new_opts));
1329 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(
1330 config_options, base_opts, BuildDBOptions(i_opts, new_opts)));
1331 }
1332
TEST_F(OptionsTest,CFOptionsComposeImmutable)1333 TEST_F(OptionsTest, CFOptionsComposeImmutable) {
1334 // Build a DBOptions from an Immutable/Mutable one and verify that
1335 // we get same constituent options.
1336 ConfigOptions config_options;
1337 Random rnd(301);
1338 ColumnFamilyOptions base_opts, new_opts;
1339 DBOptions dummy; // Needed to create ImmutableCFOptions
1340 test::RandomInitCFOptions(&base_opts, dummy, &rnd);
1341 MutableCFOptions m_opts(base_opts);
1342 ImmutableCFOptions i_opts(base_opts);
1343 UpdateColumnFamilyOptions(i_opts, &new_opts);
1344 UpdateColumnFamilyOptions(m_opts, &new_opts);
1345 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_opts,
1346 new_opts));
1347 delete new_opts.compaction_filter;
1348 }
1349
TEST_F(OptionsTest,GetMutableCFOptions)1350 TEST_F(OptionsTest, GetMutableCFOptions) {
1351 Random rnd(228);
1352 ColumnFamilyOptions base, copy;
1353 std::string opts_str;
1354 std::unordered_map<std::string, std::string> opts_map;
1355 ConfigOptions config_options;
1356 DBOptions dummy; // Needed to create ImmutableCFOptions
1357
1358 test::RandomInitCFOptions(&base, dummy, &rnd);
1359 ColumnFamilyOptions result;
1360 MutableCFOptions m_opts(base), new_opts;
1361
1362 ASSERT_OK(GetStringFromMutableCFOptions(config_options, m_opts, &opts_str));
1363 ASSERT_OK(StringToMap(opts_str, &opts_map));
1364 ASSERT_OK(GetMutableOptionsFromStrings(m_opts, opts_map, nullptr, &new_opts));
1365 UpdateColumnFamilyOptions(ImmutableCFOptions(base), ©);
1366 UpdateColumnFamilyOptions(new_opts, ©);
1367
1368 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base, copy));
1369 delete copy.compaction_filter;
1370 }
1371
TEST_F(OptionsTest,ColumnFamilyOptionsSerialization)1372 TEST_F(OptionsTest, ColumnFamilyOptionsSerialization) {
1373 Options options;
1374 ColumnFamilyOptions base_opt, new_opt;
1375 Random rnd(302);
1376 ConfigOptions config_options;
1377 config_options.input_strings_escaped = false;
1378
1379 // Phase 1: randomly assign base_opt
1380 // custom type options
1381 test::RandomInitCFOptions(&base_opt, options, &rnd);
1382
1383 // Phase 2: obtain a string from base_opt
1384 std::string base_options_file_content;
1385 ASSERT_OK(GetStringFromColumnFamilyOptions(config_options, base_opt,
1386 &base_options_file_content));
1387
1388 // Phase 3: Set new_opt from the derived string and expect
1389 // new_opt == base_opt
1390 ASSERT_OK(
1391 GetColumnFamilyOptionsFromString(config_options, ColumnFamilyOptions(),
1392 base_options_file_content, &new_opt));
1393 ASSERT_OK(
1394 RocksDBOptionsParser::VerifyCFOptions(config_options, base_opt, new_opt));
1395 if (base_opt.compaction_filter) {
1396 delete base_opt.compaction_filter;
1397 }
1398 }
1399
TEST_F(OptionsTest,CheckBlockBasedTableOptions)1400 TEST_F(OptionsTest, CheckBlockBasedTableOptions) {
1401 ColumnFamilyOptions cf_opts;
1402 DBOptions db_opts;
1403 ConfigOptions config_opts;
1404
1405 ASSERT_OK(GetColumnFamilyOptionsFromString(
1406 config_opts, cf_opts, "prefix_extractor=capped:8", &cf_opts));
1407 ASSERT_OK(TableFactory::CreateFromString(config_opts, "BlockBasedTable",
1408 &cf_opts.table_factory));
1409 ASSERT_NE(cf_opts.table_factory.get(), nullptr);
1410 ASSERT_TRUE(cf_opts.table_factory->IsInstanceOf(
1411 TableFactory::kBlockBasedTableName()));
1412 auto bbto = cf_opts.table_factory->GetOptions<BlockBasedTableOptions>();
1413 ASSERT_OK(cf_opts.table_factory->ConfigureFromString(
1414 config_opts,
1415 "block_cache={capacity=1M;num_shard_bits=4;};"
1416 "block_size_deviation=101;"
1417 "block_restart_interval=0;"
1418 "index_block_restart_interval=5;"
1419 "partition_filters=true;"
1420 "index_type=kHashSearch;"
1421 "no_block_cache=1;"));
1422 ASSERT_NE(bbto, nullptr);
1423 ASSERT_EQ(bbto->block_cache.get(), nullptr);
1424 ASSERT_EQ(bbto->block_size_deviation, 0);
1425 ASSERT_EQ(bbto->block_restart_interval, 1);
1426 ASSERT_EQ(bbto->index_block_restart_interval, 1);
1427 ASSERT_FALSE(bbto->partition_filters);
1428 ASSERT_OK(TableFactory::CreateFromString(config_opts, "BlockBasedTable",
1429 &cf_opts.table_factory));
1430 bbto = cf_opts.table_factory->GetOptions<BlockBasedTableOptions>();
1431
1432 ASSERT_OK(cf_opts.table_factory->ConfigureFromString(config_opts,
1433 "no_block_cache=0;"));
1434 ASSERT_NE(bbto->block_cache.get(), nullptr);
1435 ASSERT_OK(cf_opts.table_factory->ValidateOptions(db_opts, cf_opts));
1436 }
1437
TEST_F(OptionsTest,MutableTableOptions)1438 TEST_F(OptionsTest, MutableTableOptions) {
1439 ConfigOptions config_options;
1440 std::shared_ptr<TableFactory> bbtf;
1441 bbtf.reset(NewBlockBasedTableFactory());
1442 auto bbto = bbtf->GetOptions<BlockBasedTableOptions>();
1443 ASSERT_NE(bbto, nullptr);
1444 ASSERT_FALSE(bbtf->IsPrepared());
1445 ASSERT_OK(bbtf->ConfigureOption(config_options, "block_align", "true"));
1446 ASSERT_OK(bbtf->ConfigureOption(config_options, "block_size", "1024"));
1447 ASSERT_EQ(bbto->block_align, true);
1448 ASSERT_EQ(bbto->block_size, 1024);
1449 ASSERT_OK(bbtf->PrepareOptions(config_options));
1450 ASSERT_TRUE(bbtf->IsPrepared());
1451 config_options.mutable_options_only = true;
1452 ASSERT_OK(bbtf->ConfigureOption(config_options, "block_size", "1024"));
1453 ASSERT_EQ(bbto->block_align, true);
1454 ASSERT_NOK(bbtf->ConfigureOption(config_options, "block_align", "false"));
1455 ASSERT_OK(bbtf->ConfigureOption(config_options, "block_size", "2048"));
1456 ASSERT_EQ(bbto->block_align, true);
1457 ASSERT_EQ(bbto->block_size, 2048);
1458
1459 ColumnFamilyOptions cf_opts;
1460 cf_opts.table_factory = bbtf;
1461 ASSERT_NOK(GetColumnFamilyOptionsFromString(
1462 config_options, cf_opts, "block_based_table_factory.block_align=false",
1463 &cf_opts));
1464 ASSERT_OK(GetColumnFamilyOptionsFromString(
1465 config_options, cf_opts, "block_based_table_factory.block_size=8192",
1466 &cf_opts));
1467 ASSERT_EQ(bbto->block_align, true);
1468 ASSERT_EQ(bbto->block_size, 8192);
1469 }
1470
TEST_F(OptionsTest,MutableCFOptions)1471 TEST_F(OptionsTest, MutableCFOptions) {
1472 ConfigOptions config_options;
1473 ColumnFamilyOptions cf_opts;
1474
1475 ASSERT_OK(GetColumnFamilyOptionsFromString(
1476 config_options, cf_opts,
1477 "paranoid_file_checks=true; block_based_table_factory.block_align=false; "
1478 "block_based_table_factory.block_size=8192;",
1479 &cf_opts));
1480 ASSERT_TRUE(cf_opts.paranoid_file_checks);
1481 ASSERT_NE(cf_opts.table_factory.get(), nullptr);
1482 const auto bbto = cf_opts.table_factory->GetOptions<BlockBasedTableOptions>();
1483 ASSERT_NE(bbto, nullptr);
1484 ASSERT_EQ(bbto->block_size, 8192);
1485 ASSERT_EQ(bbto->block_align, false);
1486 std::unordered_map<std::string, std::string> unused_opts;
1487 ASSERT_OK(GetColumnFamilyOptionsFromMap(
1488 config_options, cf_opts, {{"paranoid_file_checks", "false"}}, &cf_opts));
1489 ASSERT_EQ(cf_opts.paranoid_file_checks, false);
1490
1491 ASSERT_OK(GetColumnFamilyOptionsFromMap(
1492 config_options, cf_opts,
1493 {{"block_based_table_factory.block_size", "16384"}}, &cf_opts));
1494 ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1495 ASSERT_EQ(bbto->block_size, 16384);
1496
1497 config_options.mutable_options_only = true;
1498 // Force consistency checks is not mutable
1499 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1500 config_options, cf_opts, {{"force_consistency_checks", "true"}},
1501 &cf_opts));
1502
1503 // Attempt to change the table. It is not mutable, so this should fail and
1504 // leave the original intact
1505 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1506 config_options, cf_opts, {{"table_factory", "PlainTable"}}, &cf_opts));
1507 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1508 config_options, cf_opts, {{"table_factory.id", "PlainTable"}}, &cf_opts));
1509 ASSERT_NE(cf_opts.table_factory.get(), nullptr);
1510 ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1511
1512 // Change the block size. Should update the value in the current table
1513 ASSERT_OK(GetColumnFamilyOptionsFromMap(
1514 config_options, cf_opts,
1515 {{"block_based_table_factory.block_size", "8192"}}, &cf_opts));
1516 ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1517 ASSERT_EQ(bbto->block_size, 8192);
1518
1519 // Attempt to turn off block cache fails, as this option is not mutable
1520 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1521 config_options, cf_opts,
1522 {{"block_based_table_factory.no_block_cache", "true"}}, &cf_opts));
1523 ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1524
1525 // Attempt to change the block size via a config string/map. Should update
1526 // the current value
1527 ASSERT_OK(GetColumnFamilyOptionsFromMap(
1528 config_options, cf_opts,
1529 {{"block_based_table_factory", "{block_size=32768}"}}, &cf_opts));
1530 ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1531 ASSERT_EQ(bbto->block_size, 32768);
1532
1533 // Attempt to change the block size and no cache through the map. Should
1534 // fail, leaving the old values intact
1535 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1536 config_options, cf_opts,
1537 {{"block_based_table_factory",
1538 "{block_size=16384; no_block_cache=true}"}},
1539 &cf_opts));
1540 ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1541 ASSERT_EQ(bbto->block_size, 32768);
1542 }
1543
1544 #endif // !ROCKSDB_LITE
1545
1546 Status StringToMap(
1547 const std::string& opts_str,
1548 std::unordered_map<std::string, std::string>* opts_map);
1549
1550 #ifndef ROCKSDB_LITE // StringToMap is not supported in ROCKSDB_LITE
TEST_F(OptionsTest,StringToMapTest)1551 TEST_F(OptionsTest, StringToMapTest) {
1552 std::unordered_map<std::string, std::string> opts_map;
1553 // Regular options
1554 ASSERT_OK(StringToMap("k1=v1;k2=v2;k3=v3", &opts_map));
1555 ASSERT_EQ(opts_map["k1"], "v1");
1556 ASSERT_EQ(opts_map["k2"], "v2");
1557 ASSERT_EQ(opts_map["k3"], "v3");
1558 // Value with '='
1559 opts_map.clear();
1560 ASSERT_OK(StringToMap("k1==v1;k2=v2=;", &opts_map));
1561 ASSERT_EQ(opts_map["k1"], "=v1");
1562 ASSERT_EQ(opts_map["k2"], "v2=");
1563 // Overwrriten option
1564 opts_map.clear();
1565 ASSERT_OK(StringToMap("k1=v1;k1=v2;k3=v3", &opts_map));
1566 ASSERT_EQ(opts_map["k1"], "v2");
1567 ASSERT_EQ(opts_map["k3"], "v3");
1568 // Empty value
1569 opts_map.clear();
1570 ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4=", &opts_map));
1571 ASSERT_EQ(opts_map["k1"], "v1");
1572 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1573 ASSERT_EQ(opts_map["k2"], "");
1574 ASSERT_EQ(opts_map["k3"], "v3");
1575 ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
1576 ASSERT_EQ(opts_map["k4"], "");
1577 opts_map.clear();
1578 ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4= ", &opts_map));
1579 ASSERT_EQ(opts_map["k1"], "v1");
1580 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1581 ASSERT_EQ(opts_map["k2"], "");
1582 ASSERT_EQ(opts_map["k3"], "v3");
1583 ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
1584 ASSERT_EQ(opts_map["k4"], "");
1585 opts_map.clear();
1586 ASSERT_OK(StringToMap("k1=v1;k2=;k3=", &opts_map));
1587 ASSERT_EQ(opts_map["k1"], "v1");
1588 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1589 ASSERT_EQ(opts_map["k2"], "");
1590 ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
1591 ASSERT_EQ(opts_map["k3"], "");
1592 opts_map.clear();
1593 ASSERT_OK(StringToMap("k1=v1;k2=;k3=;", &opts_map));
1594 ASSERT_EQ(opts_map["k1"], "v1");
1595 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1596 ASSERT_EQ(opts_map["k2"], "");
1597 ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
1598 ASSERT_EQ(opts_map["k3"], "");
1599 // Regular nested options
1600 opts_map.clear();
1601 ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2=nv2};k3=v3", &opts_map));
1602 ASSERT_EQ(opts_map["k1"], "v1");
1603 ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2=nv2");
1604 ASSERT_EQ(opts_map["k3"], "v3");
1605 // Multi-level nested options
1606 opts_map.clear();
1607 ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2={nnk1=nnk2}};"
1608 "k3={nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}};k4=v4",
1609 &opts_map));
1610 ASSERT_EQ(opts_map["k1"], "v1");
1611 ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2={nnk1=nnk2}");
1612 ASSERT_EQ(opts_map["k3"], "nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}");
1613 ASSERT_EQ(opts_map["k4"], "v4");
1614 // Garbage inside curly braces
1615 opts_map.clear();
1616 ASSERT_OK(StringToMap("k1=v1;k2={dfad=};k3={=};k4=v4",
1617 &opts_map));
1618 ASSERT_EQ(opts_map["k1"], "v1");
1619 ASSERT_EQ(opts_map["k2"], "dfad=");
1620 ASSERT_EQ(opts_map["k3"], "=");
1621 ASSERT_EQ(opts_map["k4"], "v4");
1622 // Empty nested options
1623 opts_map.clear();
1624 ASSERT_OK(StringToMap("k1=v1;k2={};", &opts_map));
1625 ASSERT_EQ(opts_map["k1"], "v1");
1626 ASSERT_EQ(opts_map["k2"], "");
1627 opts_map.clear();
1628 ASSERT_OK(StringToMap("k1=v1;k2={{{{}}}{}{}};", &opts_map));
1629 ASSERT_EQ(opts_map["k1"], "v1");
1630 ASSERT_EQ(opts_map["k2"], "{{{}}}{}{}");
1631 // With random spaces
1632 opts_map.clear();
1633 ASSERT_OK(StringToMap(" k1 = v1 ; k2= {nk1=nv1; nk2={nnk1=nnk2}} ; "
1634 "k3={ { } }; k4= v4 ",
1635 &opts_map));
1636 ASSERT_EQ(opts_map["k1"], "v1");
1637 ASSERT_EQ(opts_map["k2"], "nk1=nv1; nk2={nnk1=nnk2}");
1638 ASSERT_EQ(opts_map["k3"], "{ }");
1639 ASSERT_EQ(opts_map["k4"], "v4");
1640
1641 // Empty key
1642 ASSERT_NOK(StringToMap("k1=v1;k2=v2;=", &opts_map));
1643 ASSERT_NOK(StringToMap("=v1;k2=v2", &opts_map));
1644 ASSERT_NOK(StringToMap("k1=v1;k2v2;", &opts_map));
1645 ASSERT_NOK(StringToMap("k1=v1;k2=v2;fadfa", &opts_map));
1646 ASSERT_NOK(StringToMap("k1=v1;k2=v2;;", &opts_map));
1647 // Mismatch curly braces
1648 ASSERT_NOK(StringToMap("k1=v1;k2={;k3=v3", &opts_map));
1649 ASSERT_NOK(StringToMap("k1=v1;k2={{};k3=v3", &opts_map));
1650 ASSERT_NOK(StringToMap("k1=v1;k2={}};k3=v3", &opts_map));
1651 ASSERT_NOK(StringToMap("k1=v1;k2={{}{}}};k3=v3", &opts_map));
1652 // However this is valid!
1653 opts_map.clear();
1654 ASSERT_OK(StringToMap("k1=v1;k2=};k3=v3", &opts_map));
1655 ASSERT_EQ(opts_map["k1"], "v1");
1656 ASSERT_EQ(opts_map["k2"], "}");
1657 ASSERT_EQ(opts_map["k3"], "v3");
1658
1659 // Invalid chars after closing curly brace
1660 ASSERT_NOK(StringToMap("k1=v1;k2={{}}{};k3=v3", &opts_map));
1661 ASSERT_NOK(StringToMap("k1=v1;k2={{}}cfda;k3=v3", &opts_map));
1662 ASSERT_NOK(StringToMap("k1=v1;k2={{}} cfda;k3=v3", &opts_map));
1663 ASSERT_NOK(StringToMap("k1=v1;k2={{}} cfda", &opts_map));
1664 ASSERT_NOK(StringToMap("k1=v1;k2={{}}{}", &opts_map));
1665 ASSERT_NOK(StringToMap("k1=v1;k2={{dfdl}adfa}{}", &opts_map));
1666 }
1667 #endif // ROCKSDB_LITE
1668
1669 #ifndef ROCKSDB_LITE // StringToMap is not supported in ROCKSDB_LITE
TEST_F(OptionsTest,StringToMapRandomTest)1670 TEST_F(OptionsTest, StringToMapRandomTest) {
1671 std::unordered_map<std::string, std::string> opts_map;
1672 // Make sure segfault is not hit by semi-random strings
1673
1674 std::vector<std::string> bases = {
1675 "a={aa={};tt={xxx={}}};c=defff",
1676 "a={aa={};tt={xxx={}}};c=defff;d={{}yxx{}3{xx}}",
1677 "abc={{}{}{}{{{}}}{{}{}{}{}{}{}{}"};
1678
1679 for (std::string base : bases) {
1680 for (int rand_seed = 301; rand_seed < 401; rand_seed++) {
1681 Random rnd(rand_seed);
1682 for (int attempt = 0; attempt < 10; attempt++) {
1683 std::string str = base;
1684 // Replace random position to space
1685 size_t pos = static_cast<size_t>(
1686 rnd.Uniform(static_cast<int>(base.size())));
1687 str[pos] = ' ';
1688 Status s = StringToMap(str, &opts_map);
1689 ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
1690 opts_map.clear();
1691 }
1692 }
1693 }
1694
1695 // Random Construct a string
1696 std::vector<char> chars = {'{', '}', ' ', '=', ';', 'c'};
1697 for (int rand_seed = 301; rand_seed < 1301; rand_seed++) {
1698 Random rnd(rand_seed);
1699 int len = rnd.Uniform(30);
1700 std::string str = "";
1701 for (int attempt = 0; attempt < len; attempt++) {
1702 // Add a random character
1703 size_t pos = static_cast<size_t>(
1704 rnd.Uniform(static_cast<int>(chars.size())));
1705 str.append(1, chars[pos]);
1706 }
1707 Status s = StringToMap(str, &opts_map);
1708 ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
1709 s = StringToMap("name=" + str, &opts_map);
1710 ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
1711 opts_map.clear();
1712 }
1713 }
1714
TEST_F(OptionsTest,GetStringFromCompressionType)1715 TEST_F(OptionsTest, GetStringFromCompressionType) {
1716 std::string res;
1717
1718 ASSERT_OK(GetStringFromCompressionType(&res, kNoCompression));
1719 ASSERT_EQ(res, "kNoCompression");
1720
1721 ASSERT_OK(GetStringFromCompressionType(&res, kSnappyCompression));
1722 ASSERT_EQ(res, "kSnappyCompression");
1723
1724 ASSERT_OK(GetStringFromCompressionType(&res, kDisableCompressionOption));
1725 ASSERT_EQ(res, "kDisableCompressionOption");
1726
1727 ASSERT_OK(GetStringFromCompressionType(&res, kLZ4Compression));
1728 ASSERT_EQ(res, "kLZ4Compression");
1729
1730 ASSERT_OK(GetStringFromCompressionType(&res, kZlibCompression));
1731 ASSERT_EQ(res, "kZlibCompression");
1732
1733 ASSERT_NOK(
1734 GetStringFromCompressionType(&res, static_cast<CompressionType>(-10)));
1735 }
1736
TEST_F(OptionsTest,OnlyMutableDBOptions)1737 TEST_F(OptionsTest, OnlyMutableDBOptions) {
1738 std::string opt_str;
1739 Random rnd(302);
1740 ConfigOptions cfg_opts;
1741 DBOptions db_opts;
1742 DBOptions mdb_opts;
1743 std::unordered_set<std::string> m_names;
1744 std::unordered_set<std::string> a_names;
1745
1746 test::RandomInitDBOptions(&db_opts, &rnd);
1747 auto db_config = DBOptionsAsConfigurable(db_opts);
1748
1749 // Get all of the DB Option names (mutable or not)
1750 ASSERT_OK(db_config->GetOptionNames(cfg_opts, &a_names));
1751
1752 // Get only the mutable options from db_opts and set those in mdb_opts
1753 cfg_opts.mutable_options_only = true;
1754
1755 // Get only the Mutable DB Option names
1756 ASSERT_OK(db_config->GetOptionNames(cfg_opts, &m_names));
1757 ASSERT_OK(GetStringFromDBOptions(cfg_opts, db_opts, &opt_str));
1758 ASSERT_OK(GetDBOptionsFromString(cfg_opts, mdb_opts, opt_str, &mdb_opts));
1759 std::string mismatch;
1760 // Comparing only the mutable options, the two are equivalent
1761 auto mdb_config = DBOptionsAsConfigurable(mdb_opts);
1762 ASSERT_TRUE(mdb_config->AreEquivalent(cfg_opts, db_config.get(), &mismatch));
1763 ASSERT_TRUE(db_config->AreEquivalent(cfg_opts, mdb_config.get(), &mismatch));
1764
1765 ASSERT_GT(a_names.size(), m_names.size());
1766 for (const auto& n : m_names) {
1767 std::string m, d;
1768 ASSERT_OK(mdb_config->GetOption(cfg_opts, n, &m));
1769 ASSERT_OK(db_config->GetOption(cfg_opts, n, &d));
1770 ASSERT_EQ(m, d);
1771 }
1772
1773 cfg_opts.mutable_options_only = false;
1774 // Comparing all of the options, the two are not equivalent
1775 ASSERT_FALSE(mdb_config->AreEquivalent(cfg_opts, db_config.get(), &mismatch));
1776 ASSERT_FALSE(db_config->AreEquivalent(cfg_opts, mdb_config.get(), &mismatch));
1777 }
1778
TEST_F(OptionsTest,OnlyMutableCFOptions)1779 TEST_F(OptionsTest, OnlyMutableCFOptions) {
1780 std::string opt_str;
1781 Random rnd(302);
1782 ConfigOptions cfg_opts;
1783 DBOptions db_opts;
1784 ColumnFamilyOptions mcf_opts;
1785 ColumnFamilyOptions cf_opts;
1786 std::unordered_set<std::string> m_names;
1787 std::unordered_set<std::string> a_names;
1788
1789 test::RandomInitCFOptions(&cf_opts, db_opts, &rnd);
1790 auto cf_config = CFOptionsAsConfigurable(cf_opts);
1791
1792 // Get all of the CF Option names (mutable or not)
1793 ASSERT_OK(cf_config->GetOptionNames(cfg_opts, &a_names));
1794
1795 // Get only the mutable options from cf_opts and set those in mcf_opts
1796 cfg_opts.mutable_options_only = true;
1797 // Get only the Mutable CF Option names
1798 ASSERT_OK(cf_config->GetOptionNames(cfg_opts, &m_names));
1799 ASSERT_OK(GetStringFromColumnFamilyOptions(cfg_opts, cf_opts, &opt_str));
1800 ASSERT_OK(
1801 GetColumnFamilyOptionsFromString(cfg_opts, mcf_opts, opt_str, &mcf_opts));
1802 std::string mismatch;
1803
1804 auto mcf_config = CFOptionsAsConfigurable(mcf_opts);
1805 // Comparing only the mutable options, the two are equivalent
1806 ASSERT_TRUE(mcf_config->AreEquivalent(cfg_opts, cf_config.get(), &mismatch));
1807 ASSERT_TRUE(cf_config->AreEquivalent(cfg_opts, mcf_config.get(), &mismatch));
1808
1809 ASSERT_GT(a_names.size(), m_names.size());
1810 for (const auto& n : m_names) {
1811 std::string m, d;
1812 ASSERT_OK(mcf_config->GetOption(cfg_opts, n, &m));
1813 ASSERT_OK(cf_config->GetOption(cfg_opts, n, &d));
1814 ASSERT_EQ(m, d);
1815 }
1816
1817 cfg_opts.mutable_options_only = false;
1818 // Comparing all of the options, the two are not equivalent
1819 ASSERT_FALSE(mcf_config->AreEquivalent(cfg_opts, cf_config.get(), &mismatch));
1820 ASSERT_FALSE(cf_config->AreEquivalent(cfg_opts, mcf_config.get(), &mismatch));
1821
1822 delete cf_opts.compaction_filter;
1823 }
1824 #endif // !ROCKSDB_LITE
1825
TEST_F(OptionsTest,ConvertOptionsTest)1826 TEST_F(OptionsTest, ConvertOptionsTest) {
1827 LevelDBOptions leveldb_opt;
1828 Options converted_opt = ConvertOptions(leveldb_opt);
1829
1830 ASSERT_EQ(converted_opt.create_if_missing, leveldb_opt.create_if_missing);
1831 ASSERT_EQ(converted_opt.error_if_exists, leveldb_opt.error_if_exists);
1832 ASSERT_EQ(converted_opt.paranoid_checks, leveldb_opt.paranoid_checks);
1833 ASSERT_EQ(converted_opt.env, leveldb_opt.env);
1834 ASSERT_EQ(converted_opt.info_log.get(), leveldb_opt.info_log);
1835 ASSERT_EQ(converted_opt.write_buffer_size, leveldb_opt.write_buffer_size);
1836 ASSERT_EQ(converted_opt.max_open_files, leveldb_opt.max_open_files);
1837 ASSERT_EQ(converted_opt.compression, leveldb_opt.compression);
1838
1839 std::shared_ptr<TableFactory> table_factory = converted_opt.table_factory;
1840 const auto table_opt = table_factory->GetOptions<BlockBasedTableOptions>();
1841 ASSERT_NE(table_opt, nullptr);
1842
1843 ASSERT_EQ(table_opt->block_cache->GetCapacity(), 8UL << 20);
1844 ASSERT_EQ(table_opt->block_size, leveldb_opt.block_size);
1845 ASSERT_EQ(table_opt->block_restart_interval,
1846 leveldb_opt.block_restart_interval);
1847 ASSERT_EQ(table_opt->filter_policy.get(), leveldb_opt.filter_policy);
1848 }
1849
1850 #ifndef ROCKSDB_LITE
1851 const static std::string kCustomEnvName = "Custom";
1852 const static std::string kCustomEnvProp = "env=" + kCustomEnvName;
1853 class CustomEnv : public EnvWrapper {
1854 public:
CustomEnv(Env * _target)1855 explicit CustomEnv(Env* _target) : EnvWrapper(_target) {}
1856 };
1857
RegisterCustomEnv(ObjectLibrary & library,const std::string & arg)1858 static int RegisterCustomEnv(ObjectLibrary& library, const std::string& arg) {
1859 library.Register<Env>(
1860 arg, [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
1861 std::string* /* errmsg */) {
1862 static CustomEnv env(Env::Default());
1863 return &env;
1864 });
1865 return 1;
1866 }
1867
1868 // This test suite tests the old APIs into the Configure options methods.
1869 // Once those APIs are officially deprecated, this test suite can be deleted.
1870 class OptionsOldApiTest : public testing::Test {};
1871
TEST_F(OptionsOldApiTest,GetOptionsFromMapTest)1872 TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
1873 std::unordered_map<std::string, std::string> cf_options_map = {
1874 {"write_buffer_size", "1"},
1875 {"max_write_buffer_number", "2"},
1876 {"min_write_buffer_number_to_merge", "3"},
1877 {"max_write_buffer_number_to_maintain", "99"},
1878 {"max_write_buffer_size_to_maintain", "-99999"},
1879 {"compression", "kSnappyCompression"},
1880 {"compression_per_level",
1881 "kNoCompression:"
1882 "kSnappyCompression:"
1883 "kZlibCompression:"
1884 "kBZip2Compression:"
1885 "kLZ4Compression:"
1886 "kLZ4HCCompression:"
1887 "kXpressCompression:"
1888 "kZSTD:"
1889 "kZSTDNotFinalCompression"},
1890 {"bottommost_compression", "kLZ4Compression"},
1891 {"bottommost_compression_opts", "5:6:7:8:9:true"},
1892 {"compression_opts", "4:5:6:7:8:true"},
1893 {"num_levels", "8"},
1894 {"level0_file_num_compaction_trigger", "8"},
1895 {"level0_slowdown_writes_trigger", "9"},
1896 {"level0_stop_writes_trigger", "10"},
1897 {"target_file_size_base", "12"},
1898 {"target_file_size_multiplier", "13"},
1899 {"max_bytes_for_level_base", "14"},
1900 {"level_compaction_dynamic_level_bytes", "true"},
1901 {"max_bytes_for_level_multiplier", "15.0"},
1902 {"max_bytes_for_level_multiplier_additional", "16:17:18"},
1903 {"max_compaction_bytes", "21"},
1904 {"soft_rate_limit", "1.1"},
1905 {"hard_rate_limit", "2.1"},
1906 {"hard_pending_compaction_bytes_limit", "211"},
1907 {"arena_block_size", "22"},
1908 {"disable_auto_compactions", "true"},
1909 {"compaction_style", "kCompactionStyleLevel"},
1910 {"compaction_pri", "kOldestSmallestSeqFirst"},
1911 {"verify_checksums_in_compaction", "false"},
1912 {"compaction_options_fifo", "23"},
1913 {"max_sequential_skip_in_iterations", "24"},
1914 {"inplace_update_support", "true"},
1915 {"report_bg_io_stats", "true"},
1916 {"compaction_measure_io_stats", "false"},
1917 {"inplace_update_num_locks", "25"},
1918 {"memtable_prefix_bloom_size_ratio", "0.26"},
1919 {"memtable_whole_key_filtering", "true"},
1920 {"memtable_huge_page_size", "28"},
1921 {"bloom_locality", "29"},
1922 {"max_successive_merges", "30"},
1923 {"min_partial_merge_operands", "31"},
1924 {"prefix_extractor", "fixed:31"},
1925 {"optimize_filters_for_hits", "true"},
1926 {"enable_blob_files", "true"},
1927 {"min_blob_size", "1K"},
1928 {"blob_file_size", "1G"},
1929 {"blob_compression_type", "kZSTD"},
1930 {"enable_blob_garbage_collection", "true"},
1931 {"blob_garbage_collection_age_cutoff", "0.5"},
1932 };
1933
1934 std::unordered_map<std::string, std::string> db_options_map = {
1935 {"create_if_missing", "false"},
1936 {"create_missing_column_families", "true"},
1937 {"error_if_exists", "false"},
1938 {"paranoid_checks", "true"},
1939 {"track_and_verify_wals_in_manifest", "true"},
1940 {"max_open_files", "32"},
1941 {"max_total_wal_size", "33"},
1942 {"use_fsync", "true"},
1943 {"db_log_dir", "/db_log_dir"},
1944 {"wal_dir", "/wal_dir"},
1945 {"delete_obsolete_files_period_micros", "34"},
1946 {"max_background_compactions", "35"},
1947 {"max_background_flushes", "36"},
1948 {"max_log_file_size", "37"},
1949 {"log_file_time_to_roll", "38"},
1950 {"keep_log_file_num", "39"},
1951 {"recycle_log_file_num", "5"},
1952 {"max_manifest_file_size", "40"},
1953 {"table_cache_numshardbits", "41"},
1954 {"WAL_ttl_seconds", "43"},
1955 {"WAL_size_limit_MB", "44"},
1956 {"manifest_preallocation_size", "45"},
1957 {"allow_mmap_reads", "true"},
1958 {"allow_mmap_writes", "false"},
1959 {"use_direct_reads", "false"},
1960 {"use_direct_io_for_flush_and_compaction", "false"},
1961 {"is_fd_close_on_exec", "true"},
1962 {"skip_log_error_on_recovery", "false"},
1963 {"stats_dump_period_sec", "46"},
1964 {"stats_persist_period_sec", "57"},
1965 {"persist_stats_to_disk", "false"},
1966 {"stats_history_buffer_size", "69"},
1967 {"advise_random_on_open", "true"},
1968 {"use_adaptive_mutex", "false"},
1969 {"new_table_reader_for_compaction_inputs", "true"},
1970 {"compaction_readahead_size", "100"},
1971 {"random_access_max_buffer_size", "3145728"},
1972 {"writable_file_max_buffer_size", "314159"},
1973 {"bytes_per_sync", "47"},
1974 {"wal_bytes_per_sync", "48"},
1975 {"strict_bytes_per_sync", "true"},
1976 };
1977
1978 ColumnFamilyOptions base_cf_opt;
1979 ColumnFamilyOptions new_cf_opt;
1980 ASSERT_OK(GetColumnFamilyOptionsFromMap(
1981 base_cf_opt, cf_options_map, &new_cf_opt));
1982 ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
1983 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
1984 ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
1985 ASSERT_EQ(new_cf_opt.max_write_buffer_number_to_maintain, 99);
1986 ASSERT_EQ(new_cf_opt.max_write_buffer_size_to_maintain, -99999);
1987 ASSERT_EQ(new_cf_opt.compression, kSnappyCompression);
1988 ASSERT_EQ(new_cf_opt.compression_per_level.size(), 9U);
1989 ASSERT_EQ(new_cf_opt.compression_per_level[0], kNoCompression);
1990 ASSERT_EQ(new_cf_opt.compression_per_level[1], kSnappyCompression);
1991 ASSERT_EQ(new_cf_opt.compression_per_level[2], kZlibCompression);
1992 ASSERT_EQ(new_cf_opt.compression_per_level[3], kBZip2Compression);
1993 ASSERT_EQ(new_cf_opt.compression_per_level[4], kLZ4Compression);
1994 ASSERT_EQ(new_cf_opt.compression_per_level[5], kLZ4HCCompression);
1995 ASSERT_EQ(new_cf_opt.compression_per_level[6], kXpressCompression);
1996 ASSERT_EQ(new_cf_opt.compression_per_level[7], kZSTD);
1997 ASSERT_EQ(new_cf_opt.compression_per_level[8], kZSTDNotFinalCompression);
1998 ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
1999 ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
2000 ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
2001 ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7u);
2002 ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
2003 ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads,
2004 CompressionOptions().parallel_threads);
2005 ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
2006 ASSERT_EQ(new_cf_opt.bottommost_compression, kLZ4Compression);
2007 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 5);
2008 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
2009 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
2010 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
2011 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 9u);
2012 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
2013 CompressionOptions().parallel_threads);
2014 ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, true);
2015 ASSERT_EQ(new_cf_opt.num_levels, 8);
2016 ASSERT_EQ(new_cf_opt.level0_file_num_compaction_trigger, 8);
2017 ASSERT_EQ(new_cf_opt.level0_slowdown_writes_trigger, 9);
2018 ASSERT_EQ(new_cf_opt.level0_stop_writes_trigger, 10);
2019 ASSERT_EQ(new_cf_opt.target_file_size_base, static_cast<uint64_t>(12));
2020 ASSERT_EQ(new_cf_opt.target_file_size_multiplier, 13);
2021 ASSERT_EQ(new_cf_opt.max_bytes_for_level_base, 14U);
2022 ASSERT_EQ(new_cf_opt.level_compaction_dynamic_level_bytes, true);
2023 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier, 15.0);
2024 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional.size(), 3U);
2025 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[0], 16);
2026 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[1], 17);
2027 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[2], 18);
2028 ASSERT_EQ(new_cf_opt.max_compaction_bytes, 21);
2029 ASSERT_EQ(new_cf_opt.hard_pending_compaction_bytes_limit, 211);
2030 ASSERT_EQ(new_cf_opt.arena_block_size, 22U);
2031 ASSERT_EQ(new_cf_opt.disable_auto_compactions, true);
2032 ASSERT_EQ(new_cf_opt.compaction_style, kCompactionStyleLevel);
2033 ASSERT_EQ(new_cf_opt.compaction_pri, kOldestSmallestSeqFirst);
2034 ASSERT_EQ(new_cf_opt.compaction_options_fifo.max_table_files_size,
2035 static_cast<uint64_t>(23));
2036 ASSERT_EQ(new_cf_opt.max_sequential_skip_in_iterations,
2037 static_cast<uint64_t>(24));
2038 ASSERT_EQ(new_cf_opt.inplace_update_support, true);
2039 ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 25U);
2040 ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_size_ratio, 0.26);
2041 ASSERT_EQ(new_cf_opt.memtable_whole_key_filtering, true);
2042 ASSERT_EQ(new_cf_opt.memtable_huge_page_size, 28U);
2043 ASSERT_EQ(new_cf_opt.bloom_locality, 29U);
2044 ASSERT_EQ(new_cf_opt.max_successive_merges, 30U);
2045 ASSERT_TRUE(new_cf_opt.prefix_extractor != nullptr);
2046 ASSERT_EQ(new_cf_opt.optimize_filters_for_hits, true);
2047 ASSERT_EQ(std::string(new_cf_opt.prefix_extractor->Name()),
2048 "rocksdb.FixedPrefix.31");
2049 ASSERT_EQ(new_cf_opt.enable_blob_files, true);
2050 ASSERT_EQ(new_cf_opt.min_blob_size, 1ULL << 10);
2051 ASSERT_EQ(new_cf_opt.blob_file_size, 1ULL << 30);
2052 ASSERT_EQ(new_cf_opt.blob_compression_type, kZSTD);
2053 ASSERT_EQ(new_cf_opt.enable_blob_garbage_collection, true);
2054 ASSERT_EQ(new_cf_opt.blob_garbage_collection_age_cutoff, 0.5);
2055
2056 cf_options_map["write_buffer_size"] = "hello";
2057 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
2058 base_cf_opt, cf_options_map, &new_cf_opt));
2059 ConfigOptions exact, loose;
2060 exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
2061 loose.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
2062
2063 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2064
2065 cf_options_map["write_buffer_size"] = "1";
2066 ASSERT_OK(GetColumnFamilyOptionsFromMap(
2067 base_cf_opt, cf_options_map, &new_cf_opt));
2068
2069 cf_options_map["unknown_option"] = "1";
2070 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
2071 base_cf_opt, cf_options_map, &new_cf_opt));
2072 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2073
2074 ASSERT_OK(GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map,
2075 &new_cf_opt,
2076 false, /* input_strings_escaped */
2077 true /* ignore_unknown_options */));
2078 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
2079 loose, base_cf_opt, new_cf_opt, nullptr /* new_opt_map */));
2080 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
2081 exact /* default for VerifyCFOptions */, base_cf_opt, new_cf_opt, nullptr));
2082
2083 DBOptions base_db_opt;
2084 DBOptions new_db_opt;
2085 ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
2086 ASSERT_EQ(new_db_opt.create_if_missing, false);
2087 ASSERT_EQ(new_db_opt.create_missing_column_families, true);
2088 ASSERT_EQ(new_db_opt.error_if_exists, false);
2089 ASSERT_EQ(new_db_opt.paranoid_checks, true);
2090 ASSERT_EQ(new_db_opt.track_and_verify_wals_in_manifest, true);
2091 ASSERT_EQ(new_db_opt.max_open_files, 32);
2092 ASSERT_EQ(new_db_opt.max_total_wal_size, static_cast<uint64_t>(33));
2093 ASSERT_EQ(new_db_opt.use_fsync, true);
2094 ASSERT_EQ(new_db_opt.db_log_dir, "/db_log_dir");
2095 ASSERT_EQ(new_db_opt.wal_dir, "/wal_dir");
2096 ASSERT_EQ(new_db_opt.delete_obsolete_files_period_micros,
2097 static_cast<uint64_t>(34));
2098 ASSERT_EQ(new_db_opt.max_background_compactions, 35);
2099 ASSERT_EQ(new_db_opt.max_background_flushes, 36);
2100 ASSERT_EQ(new_db_opt.max_log_file_size, 37U);
2101 ASSERT_EQ(new_db_opt.log_file_time_to_roll, 38U);
2102 ASSERT_EQ(new_db_opt.keep_log_file_num, 39U);
2103 ASSERT_EQ(new_db_opt.recycle_log_file_num, 5U);
2104 ASSERT_EQ(new_db_opt.max_manifest_file_size, static_cast<uint64_t>(40));
2105 ASSERT_EQ(new_db_opt.table_cache_numshardbits, 41);
2106 ASSERT_EQ(new_db_opt.WAL_ttl_seconds, static_cast<uint64_t>(43));
2107 ASSERT_EQ(new_db_opt.WAL_size_limit_MB, static_cast<uint64_t>(44));
2108 ASSERT_EQ(new_db_opt.manifest_preallocation_size, 45U);
2109 ASSERT_EQ(new_db_opt.allow_mmap_reads, true);
2110 ASSERT_EQ(new_db_opt.allow_mmap_writes, false);
2111 ASSERT_EQ(new_db_opt.use_direct_reads, false);
2112 ASSERT_EQ(new_db_opt.use_direct_io_for_flush_and_compaction, false);
2113 ASSERT_EQ(new_db_opt.is_fd_close_on_exec, true);
2114 ASSERT_EQ(new_db_opt.skip_log_error_on_recovery, false);
2115 ASSERT_EQ(new_db_opt.stats_dump_period_sec, 46U);
2116 ASSERT_EQ(new_db_opt.stats_persist_period_sec, 57U);
2117 ASSERT_EQ(new_db_opt.persist_stats_to_disk, false);
2118 ASSERT_EQ(new_db_opt.stats_history_buffer_size, 69U);
2119 ASSERT_EQ(new_db_opt.advise_random_on_open, true);
2120 ASSERT_EQ(new_db_opt.use_adaptive_mutex, false);
2121 ASSERT_EQ(new_db_opt.new_table_reader_for_compaction_inputs, true);
2122 ASSERT_EQ(new_db_opt.compaction_readahead_size, 100);
2123 ASSERT_EQ(new_db_opt.random_access_max_buffer_size, 3145728);
2124 ASSERT_EQ(new_db_opt.writable_file_max_buffer_size, 314159);
2125 ASSERT_EQ(new_db_opt.bytes_per_sync, static_cast<uint64_t>(47));
2126 ASSERT_EQ(new_db_opt.wal_bytes_per_sync, static_cast<uint64_t>(48));
2127 ASSERT_EQ(new_db_opt.strict_bytes_per_sync, true);
2128
2129 db_options_map["max_open_files"] = "hello";
2130 ASSERT_NOK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
2131 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
2132 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
2133
2134 // unknow options should fail parsing without ignore_unknown_options = true
2135 db_options_map["unknown_db_option"] = "1";
2136 ASSERT_NOK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
2137 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
2138
2139 ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt,
2140 false, /* input_strings_escaped */
2141 true /* ignore_unknown_options */));
2142 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
2143 ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
2144 }
2145
TEST_F(OptionsOldApiTest,GetColumnFamilyOptionsFromStringTest)2146 TEST_F(OptionsOldApiTest, GetColumnFamilyOptionsFromStringTest) {
2147 ColumnFamilyOptions base_cf_opt;
2148 ColumnFamilyOptions new_cf_opt;
2149 base_cf_opt.table_factory.reset();
2150 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt, "", &new_cf_opt));
2151 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2152 "write_buffer_size=5", &new_cf_opt));
2153 ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
2154 ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
2155 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2156 "write_buffer_size=6;", &new_cf_opt));
2157 ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
2158 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2159 " write_buffer_size = 7 ", &new_cf_opt));
2160 ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
2161 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2162 " write_buffer_size = 8 ; ", &new_cf_opt));
2163 ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
2164 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2165 "write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
2166 ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
2167 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
2168 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2169 "write_buffer_size=11; max_write_buffer_number = 12 ;",
2170 &new_cf_opt));
2171 ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
2172 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
2173 // Wrong name "max_write_buffer_number_"
2174 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2175 "write_buffer_size=13;max_write_buffer_number_=14;",
2176 &new_cf_opt));
2177 ConfigOptions exact;
2178 exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
2179 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2180
2181 // Comparator from object registry
2182 std::string kCompName = "reverse_comp";
2183 ObjectLibrary::Default()->Register<const Comparator>(
2184 kCompName,
2185 [](const std::string& /*name*/,
2186 std::unique_ptr<const Comparator>* /*guard*/,
2187 std::string* /* errmsg */) { return ReverseBytewiseComparator(); });
2188
2189 ASSERT_OK(GetColumnFamilyOptionsFromString(
2190 base_cf_opt, "comparator=" + kCompName + ";", &new_cf_opt));
2191 ASSERT_EQ(new_cf_opt.comparator, ReverseBytewiseComparator());
2192
2193 // MergeOperator from object registry
2194 std::unique_ptr<BytesXOROperator> bxo(new BytesXOROperator());
2195 std::string kMoName = bxo->Name();
2196 ObjectLibrary::Default()->Register<MergeOperator>(
2197 kMoName,
2198 [](const std::string& /*name*/, std::unique_ptr<MergeOperator>* guard,
2199 std::string* /* errmsg */) {
2200 guard->reset(new BytesXOROperator());
2201 return guard->get();
2202 });
2203
2204 ASSERT_OK(GetColumnFamilyOptionsFromString(
2205 base_cf_opt, "merge_operator=" + kMoName + ";", &new_cf_opt));
2206 ASSERT_EQ(kMoName, std::string(new_cf_opt.merge_operator->Name()));
2207
2208 // Wrong key/value pair
2209 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2210 "write_buffer_size=13;max_write_buffer_number;", &new_cf_opt));
2211 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2212
2213 // Error Paring value
2214 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2215 "write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt));
2216 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2217
2218 // Missing option name
2219 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2220 "write_buffer_size=13; =100;", &new_cf_opt));
2221 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2222
2223 const uint64_t kilo = 1024UL;
2224 const uint64_t mega = 1024 * kilo;
2225 const uint64_t giga = 1024 * mega;
2226 const uint64_t tera = 1024 * giga;
2227
2228 // Units (k)
2229 ASSERT_OK(GetColumnFamilyOptionsFromString(
2230 base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
2231 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 15 * kilo);
2232 // Units (m)
2233 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2234 "max_write_buffer_number=16m;inplace_update_num_locks=17M",
2235 &new_cf_opt));
2236 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
2237 ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17u * mega);
2238 // Units (g)
2239 ASSERT_OK(GetColumnFamilyOptionsFromString(
2240 base_cf_opt,
2241 "write_buffer_size=18g;prefix_extractor=capped:8;"
2242 "arena_block_size=19G",
2243 &new_cf_opt));
2244
2245 ASSERT_EQ(new_cf_opt.write_buffer_size, 18 * giga);
2246 ASSERT_EQ(new_cf_opt.arena_block_size, 19 * giga);
2247 ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
2248 std::string prefix_name(new_cf_opt.prefix_extractor->Name());
2249 ASSERT_EQ(prefix_name, "rocksdb.CappedPrefix.8");
2250
2251 // Units (t)
2252 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2253 "write_buffer_size=20t;arena_block_size=21T", &new_cf_opt));
2254 ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
2255 ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
2256
2257 // Nested block based table options
2258 // Empty
2259 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2260 "write_buffer_size=10;max_write_buffer_number=16;"
2261 "block_based_table_factory={};arena_block_size=1024",
2262 &new_cf_opt));
2263 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2264 // Non-empty
2265 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2266 "write_buffer_size=10;max_write_buffer_number=16;"
2267 "block_based_table_factory={block_cache=1M;block_size=4;};"
2268 "arena_block_size=1024",
2269 &new_cf_opt));
2270 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2271 // Last one
2272 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2273 "write_buffer_size=10;max_write_buffer_number=16;"
2274 "block_based_table_factory={block_cache=1M;block_size=4;}",
2275 &new_cf_opt));
2276 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2277 // Mismatch curly braces
2278 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2279 "write_buffer_size=10;max_write_buffer_number=16;"
2280 "block_based_table_factory={{{block_size=4;};"
2281 "arena_block_size=1024",
2282 &new_cf_opt));
2283 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2284
2285 // Unexpected chars after closing curly brace
2286 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2287 "write_buffer_size=10;max_write_buffer_number=16;"
2288 "block_based_table_factory={block_size=4;}};"
2289 "arena_block_size=1024",
2290 &new_cf_opt));
2291 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2292
2293 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2294 "write_buffer_size=10;max_write_buffer_number=16;"
2295 "block_based_table_factory={block_size=4;}xdfa;"
2296 "arena_block_size=1024",
2297 &new_cf_opt));
2298 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2299
2300 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2301 "write_buffer_size=10;max_write_buffer_number=16;"
2302 "block_based_table_factory={block_size=4;}xdfa",
2303 &new_cf_opt));
2304 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2305
2306 // Invalid block based table option
2307 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2308 "write_buffer_size=10;max_write_buffer_number=16;"
2309 "block_based_table_factory={xx_block_size=4;}",
2310 &new_cf_opt));
2311 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2312
2313 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2314 "optimize_filters_for_hits=true",
2315 &new_cf_opt));
2316 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2317 "optimize_filters_for_hits=false",
2318 &new_cf_opt));
2319
2320 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2321 "optimize_filters_for_hits=junk",
2322 &new_cf_opt));
2323 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2324
2325 // Nested plain table options
2326 // Empty
2327 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2328 "write_buffer_size=10;max_write_buffer_number=16;"
2329 "plain_table_factory={};arena_block_size=1024",
2330 &new_cf_opt));
2331 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2332 ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
2333 // Non-empty
2334 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2335 "write_buffer_size=10;max_write_buffer_number=16;"
2336 "plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
2337 "arena_block_size=1024",
2338 &new_cf_opt));
2339 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2340 ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
2341
2342 // memtable factory
2343 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2344 "write_buffer_size=10;max_write_buffer_number=16;"
2345 "memtable=skip_list:10;arena_block_size=1024",
2346 &new_cf_opt));
2347 ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
2348 ASSERT_EQ(std::string(new_cf_opt.memtable_factory->Name()), "SkipListFactory");
2349 }
2350
TEST_F(OptionsOldApiTest,GetBlockBasedTableOptionsFromString)2351 TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
2352 BlockBasedTableOptions table_opt;
2353 BlockBasedTableOptions new_opt;
2354 // make sure default values are overwritten by something else
2355 ASSERT_OK(GetBlockBasedTableOptionsFromString(
2356 table_opt,
2357 "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
2358 "checksum=kxxHash;hash_index_allow_collision=1;no_block_cache=1;"
2359 "block_cache=1M;block_cache_compressed=1k;block_size=1024;"
2360 "block_size_deviation=8;block_restart_interval=4;"
2361 "format_version=5;whole_key_filtering=1;"
2362 "filter_policy=bloomfilter:4.567:false;",
2363 &new_opt));
2364 ASSERT_TRUE(new_opt.cache_index_and_filter_blocks);
2365 ASSERT_EQ(new_opt.index_type, BlockBasedTableOptions::kHashSearch);
2366 ASSERT_EQ(new_opt.checksum, ChecksumType::kxxHash);
2367 ASSERT_TRUE(new_opt.hash_index_allow_collision);
2368 ASSERT_TRUE(new_opt.no_block_cache);
2369 ASSERT_TRUE(new_opt.block_cache != nullptr);
2370 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
2371 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2372 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL);
2373 ASSERT_EQ(new_opt.block_size, 1024UL);
2374 ASSERT_EQ(new_opt.block_size_deviation, 8);
2375 ASSERT_EQ(new_opt.block_restart_interval, 4);
2376 ASSERT_EQ(new_opt.format_version, 5U);
2377 ASSERT_EQ(new_opt.whole_key_filtering, true);
2378 ASSERT_TRUE(new_opt.filter_policy != nullptr);
2379 const BloomFilterPolicy& bfp =
2380 dynamic_cast<const BloomFilterPolicy&>(*new_opt.filter_policy);
2381 EXPECT_EQ(bfp.GetMillibitsPerKey(), 4567);
2382 EXPECT_EQ(bfp.GetWholeBitsPerKey(), 5);
2383
2384 // unknown option
2385 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2386 "cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
2387 "bad_option=1",
2388 &new_opt));
2389 ASSERT_EQ(static_cast<bool>(table_opt.cache_index_and_filter_blocks),
2390 new_opt.cache_index_and_filter_blocks);
2391 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
2392
2393 // unrecognized index type
2394 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2395 "cache_index_and_filter_blocks=1;index_type=kBinarySearchXX",
2396 &new_opt));
2397 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2398 new_opt.cache_index_and_filter_blocks);
2399 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
2400
2401 // unrecognized checksum type
2402 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2403 "cache_index_and_filter_blocks=1;checksum=kxxHashXX",
2404 &new_opt));
2405 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2406 new_opt.cache_index_and_filter_blocks);
2407 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
2408
2409 // unrecognized filter policy name
2410 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2411 "cache_index_and_filter_blocks=1;"
2412 "filter_policy=bloomfilterxx:4:true",
2413 &new_opt));
2414 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2415 new_opt.cache_index_and_filter_blocks);
2416 ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
2417
2418 // unrecognized filter policy config
2419 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2420 "cache_index_and_filter_blocks=1;"
2421 "filter_policy=bloomfilter:4",
2422 &new_opt));
2423 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2424 new_opt.cache_index_and_filter_blocks);
2425 ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
2426
2427 // Check block cache options are overwritten when specified
2428 // in new format as a struct.
2429 ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
2430 "block_cache={capacity=1M;num_shard_bits=4;"
2431 "strict_capacity_limit=true;high_pri_pool_ratio=0.5;};"
2432 "block_cache_compressed={capacity=1M;num_shard_bits=4;"
2433 "strict_capacity_limit=true;high_pri_pool_ratio=0.5;}",
2434 &new_opt));
2435 ASSERT_TRUE(new_opt.block_cache != nullptr);
2436 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
2437 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2438 new_opt.block_cache)->GetNumShardBits(), 4);
2439 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
2440 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
2441 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
2442 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2443 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
2444 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2445 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
2446 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
2447 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
2448 new_opt.block_cache_compressed)->GetHighPriPoolRatio(),
2449 0.5);
2450
2451 // Set only block cache capacity. Check other values are
2452 // reset to default values.
2453 ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
2454 "block_cache={capacity=2M};"
2455 "block_cache_compressed={capacity=2M}",
2456 &new_opt));
2457 ASSERT_TRUE(new_opt.block_cache != nullptr);
2458 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 2*1024UL*1024UL);
2459 // Default values
2460 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2461 new_opt.block_cache)->GetNumShardBits(),
2462 GetDefaultCacheShardBits(new_opt.block_cache->GetCapacity()));
2463 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
2464 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
2465 ->GetHighPriPoolRatio(),
2466 0.5);
2467 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2468 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 2*1024UL*1024UL);
2469 // Default values
2470 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2471 new_opt.block_cache_compressed)->GetNumShardBits(),
2472 GetDefaultCacheShardBits(
2473 new_opt.block_cache_compressed->GetCapacity()));
2474 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
2475 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
2476 ->GetHighPriPoolRatio(),
2477 0.5);
2478
2479 // Set couple of block cache options.
2480 ASSERT_OK(GetBlockBasedTableOptionsFromString(
2481 table_opt,
2482 "block_cache={num_shard_bits=5;high_pri_pool_ratio=0.5;};"
2483 "block_cache_compressed={num_shard_bits=5;"
2484 "high_pri_pool_ratio=0.0;}",
2485 &new_opt));
2486 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 0);
2487 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2488 new_opt.block_cache)->GetNumShardBits(), 5);
2489 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
2490 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
2491 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
2492 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2493 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 0);
2494 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2495 new_opt.block_cache_compressed)->GetNumShardBits(), 5);
2496 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
2497 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
2498 ->GetHighPriPoolRatio(),
2499 0.0);
2500
2501 // Set couple of block cache options.
2502 ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
2503 "block_cache={capacity=1M;num_shard_bits=4;"
2504 "strict_capacity_limit=true;};"
2505 "block_cache_compressed={capacity=1M;num_shard_bits=4;"
2506 "strict_capacity_limit=true;}",
2507 &new_opt));
2508 ASSERT_TRUE(new_opt.block_cache != nullptr);
2509 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
2510 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2511 new_opt.block_cache)->GetNumShardBits(), 4);
2512 ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
2513 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
2514 ->GetHighPriPoolRatio(),
2515 0.5);
2516 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2517 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
2518 ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2519 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
2520 ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
2521 ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
2522 ->GetHighPriPoolRatio(),
2523 0.5);
2524 }
2525
TEST_F(OptionsOldApiTest,GetPlainTableOptionsFromString)2526 TEST_F(OptionsOldApiTest, GetPlainTableOptionsFromString) {
2527 PlainTableOptions table_opt;
2528 PlainTableOptions new_opt;
2529 // make sure default values are overwritten by something else
2530 ASSERT_OK(GetPlainTableOptionsFromString(table_opt,
2531 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
2532 "index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
2533 "full_scan_mode=true;store_index_in_file=true",
2534 &new_opt));
2535 ASSERT_EQ(new_opt.user_key_len, 66u);
2536 ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
2537 ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
2538 ASSERT_EQ(new_opt.index_sparseness, 8);
2539 ASSERT_EQ(new_opt.huge_page_tlb_size, 4);
2540 ASSERT_EQ(new_opt.encoding_type, EncodingType::kPrefix);
2541 ASSERT_TRUE(new_opt.full_scan_mode);
2542 ASSERT_TRUE(new_opt.store_index_in_file);
2543
2544 // unknown option
2545 ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
2546 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
2547 "bad_option=1",
2548 &new_opt));
2549
2550 // unrecognized EncodingType
2551 ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
2552 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
2553 "encoding_type=kPrefixXX",
2554 &new_opt));
2555 }
2556
TEST_F(OptionsOldApiTest,GetOptionsFromStringTest)2557 TEST_F(OptionsOldApiTest, GetOptionsFromStringTest) {
2558 Options base_options, new_options;
2559 base_options.write_buffer_size = 20;
2560 base_options.min_write_buffer_number_to_merge = 15;
2561 BlockBasedTableOptions block_based_table_options;
2562 block_based_table_options.cache_index_and_filter_blocks = true;
2563 base_options.table_factory.reset(
2564 NewBlockBasedTableFactory(block_based_table_options));
2565
2566 // Register an Env with object registry.
2567 ObjectLibrary::Default()->Register<Env>(
2568 "CustomEnvDefault",
2569 [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
2570 std::string* /* errmsg */) {
2571 static CustomEnv env(Env::Default());
2572 return &env;
2573 });
2574
2575 ASSERT_OK(GetOptionsFromString(
2576 base_options,
2577 "write_buffer_size=10;max_write_buffer_number=16;"
2578 "block_based_table_factory={block_cache=1M;block_size=4;};"
2579 "compression_opts=4:5:6;create_if_missing=true;max_open_files=1;"
2580 "bottommost_compression_opts=5:6:7;create_if_missing=true;max_open_files="
2581 "1;"
2582 "rate_limiter_bytes_per_sec=1024;env=CustomEnvDefault",
2583 &new_options));
2584
2585 ASSERT_EQ(new_options.compression_opts.window_bits, 4);
2586 ASSERT_EQ(new_options.compression_opts.level, 5);
2587 ASSERT_EQ(new_options.compression_opts.strategy, 6);
2588 ASSERT_EQ(new_options.compression_opts.max_dict_bytes, 0u);
2589 ASSERT_EQ(new_options.compression_opts.zstd_max_train_bytes, 0u);
2590 ASSERT_EQ(new_options.compression_opts.parallel_threads, 1u);
2591 ASSERT_EQ(new_options.compression_opts.enabled, false);
2592 ASSERT_EQ(new_options.bottommost_compression, kDisableCompressionOption);
2593 ASSERT_EQ(new_options.bottommost_compression_opts.window_bits, 5);
2594 ASSERT_EQ(new_options.bottommost_compression_opts.level, 6);
2595 ASSERT_EQ(new_options.bottommost_compression_opts.strategy, 7);
2596 ASSERT_EQ(new_options.bottommost_compression_opts.max_dict_bytes, 0u);
2597 ASSERT_EQ(new_options.bottommost_compression_opts.zstd_max_train_bytes, 0u);
2598 ASSERT_EQ(new_options.bottommost_compression_opts.parallel_threads, 1u);
2599 ASSERT_EQ(new_options.bottommost_compression_opts.enabled, false);
2600 ASSERT_EQ(new_options.write_buffer_size, 10U);
2601 ASSERT_EQ(new_options.max_write_buffer_number, 16);
2602
2603 auto new_block_based_table_options =
2604 new_options.table_factory->GetOptions<BlockBasedTableOptions>();
2605 ASSERT_NE(new_block_based_table_options, nullptr);
2606 ASSERT_EQ(new_block_based_table_options->block_cache->GetCapacity(),
2607 1U << 20);
2608 ASSERT_EQ(new_block_based_table_options->block_size, 4U);
2609 // don't overwrite block based table options
2610 ASSERT_TRUE(new_block_based_table_options->cache_index_and_filter_blocks);
2611
2612 ASSERT_EQ(new_options.create_if_missing, true);
2613 ASSERT_EQ(new_options.max_open_files, 1);
2614 ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
2615 Env* newEnv = new_options.env;
2616 ASSERT_OK(Env::LoadEnv("CustomEnvDefault", &newEnv));
2617 ASSERT_EQ(newEnv, new_options.env);
2618 }
2619
TEST_F(OptionsOldApiTest,DBOptionsSerialization)2620 TEST_F(OptionsOldApiTest, DBOptionsSerialization) {
2621 Options base_options, new_options;
2622 Random rnd(301);
2623
2624 // Phase 1: Make big change in base_options
2625 test::RandomInitDBOptions(&base_options, &rnd);
2626
2627 // Phase 2: obtain a string from base_option
2628 std::string base_options_file_content;
2629 ASSERT_OK(GetStringFromDBOptions(&base_options_file_content, base_options));
2630
2631 // Phase 3: Set new_options from the derived string and expect
2632 // new_options == base_options
2633 ASSERT_OK(GetDBOptionsFromString(DBOptions(), base_options_file_content,
2634 &new_options));
2635 ConfigOptions config_options;
2636 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_options, new_options));
2637 }
2638
TEST_F(OptionsOldApiTest,ColumnFamilyOptionsSerialization)2639 TEST_F(OptionsOldApiTest, ColumnFamilyOptionsSerialization) {
2640 Options options;
2641 ColumnFamilyOptions base_opt, new_opt;
2642 Random rnd(302);
2643 // Phase 1: randomly assign base_opt
2644 // custom type options
2645 test::RandomInitCFOptions(&base_opt, options, &rnd);
2646
2647 // Phase 2: obtain a string from base_opt
2648 std::string base_options_file_content;
2649 ASSERT_OK(
2650 GetStringFromColumnFamilyOptions(&base_options_file_content, base_opt));
2651
2652 // Phase 3: Set new_opt from the derived string and expect
2653 // new_opt == base_opt
2654 ASSERT_OK(GetColumnFamilyOptionsFromString(
2655 ColumnFamilyOptions(), base_options_file_content, &new_opt));
2656 ConfigOptions config_options;
2657 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_opt, new_opt));
2658 if (base_opt.compaction_filter) {
2659 delete base_opt.compaction_filter;
2660 }
2661 }
2662 #endif // !ROCKSDB_LITE
2663
2664 #ifndef ROCKSDB_LITE
2665 class OptionsParserTest : public testing::Test {
2666 public:
OptionsParserTest()2667 OptionsParserTest() { fs_.reset(new test::StringFS(FileSystem::Default())); }
2668
2669 protected:
2670 std::shared_ptr<test::StringFS> fs_;
2671 };
2672
TEST_F(OptionsParserTest,Comment)2673 TEST_F(OptionsParserTest, Comment) {
2674 DBOptions db_opt;
2675 db_opt.max_open_files = 12345;
2676 db_opt.max_background_flushes = 301;
2677 db_opt.max_total_wal_size = 1024;
2678 ColumnFamilyOptions cf_opt;
2679
2680 std::string options_file_content =
2681 "# This is a testing option string.\n"
2682 "# Currently we only support \"#\" styled comment.\n"
2683 "\n"
2684 "[Version]\n"
2685 " rocksdb_version=3.14.0\n"
2686 " options_file_version=1\n"
2687 "[ DBOptions ]\n"
2688 " # note that we don't support space around \"=\"\n"
2689 " max_open_files=12345;\n"
2690 " max_background_flushes=301 # comment after a statement is fine\n"
2691 " # max_background_flushes=1000 # this line would be ignored\n"
2692 " # max_background_compactions=2000 # so does this one\n"
2693 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
2694 "[CFOptions \"default\"] # column family must be specified\n"
2695 " # in the correct order\n"
2696 " # if a section is blank, we will use the default\n";
2697
2698 const std::string kTestFileName = "test-rocksdb-options.ini";
2699 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2700 RocksDBOptionsParser parser;
2701 ASSERT_OK(
2702 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2703
2704 ConfigOptions exact;
2705 exact.input_strings_escaped = false;
2706 exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
2707 ASSERT_OK(
2708 RocksDBOptionsParser::VerifyDBOptions(exact, *parser.db_opt(), db_opt));
2709 ASSERT_EQ(parser.NumColumnFamilies(), 1U);
2710 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
2711 exact, *parser.GetCFOptions("default"), cf_opt));
2712 }
2713
TEST_F(OptionsParserTest,ExtraSpace)2714 TEST_F(OptionsParserTest, ExtraSpace) {
2715 std::string options_file_content =
2716 "# This is a testing option string.\n"
2717 "# Currently we only support \"#\" styled comment.\n"
2718 "\n"
2719 "[ Version ]\n"
2720 " rocksdb_version = 3.14.0 \n"
2721 " options_file_version=1 # some comment\n"
2722 "[DBOptions ] # some comment\n"
2723 "max_open_files=12345 \n"
2724 " max_background_flushes = 301 \n"
2725 " max_total_wal_size = 1024 # keep_log_file_num=1000\n"
2726 " [CFOptions \"default\" ]\n"
2727 " # if a section is blank, we will use the default\n";
2728
2729 const std::string kTestFileName = "test-rocksdb-options.ini";
2730 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2731 RocksDBOptionsParser parser;
2732 ASSERT_OK(
2733 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2734 }
2735
TEST_F(OptionsParserTest,MissingDBOptions)2736 TEST_F(OptionsParserTest, MissingDBOptions) {
2737 std::string options_file_content =
2738 "# This is a testing option string.\n"
2739 "# Currently we only support \"#\" styled comment.\n"
2740 "\n"
2741 "[Version]\n"
2742 " rocksdb_version=3.14.0\n"
2743 " options_file_version=1\n"
2744 "[CFOptions \"default\"]\n"
2745 " # if a section is blank, we will use the default\n";
2746
2747 const std::string kTestFileName = "test-rocksdb-options.ini";
2748 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2749 RocksDBOptionsParser parser;
2750 ASSERT_NOK(
2751 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2752 ;
2753 }
2754
TEST_F(OptionsParserTest,DoubleDBOptions)2755 TEST_F(OptionsParserTest, DoubleDBOptions) {
2756 DBOptions db_opt;
2757 db_opt.max_open_files = 12345;
2758 db_opt.max_background_flushes = 301;
2759 db_opt.max_total_wal_size = 1024;
2760 ColumnFamilyOptions cf_opt;
2761
2762 std::string options_file_content =
2763 "# This is a testing option string.\n"
2764 "# Currently we only support \"#\" styled comment.\n"
2765 "\n"
2766 "[Version]\n"
2767 " rocksdb_version=3.14.0\n"
2768 " options_file_version=1\n"
2769 "[DBOptions]\n"
2770 " max_open_files=12345\n"
2771 " max_background_flushes=301\n"
2772 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
2773 "[DBOptions]\n"
2774 "[CFOptions \"default\"]\n"
2775 " # if a section is blank, we will use the default\n";
2776
2777 const std::string kTestFileName = "test-rocksdb-options.ini";
2778 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2779 RocksDBOptionsParser parser;
2780 ASSERT_NOK(
2781 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2782 }
2783
TEST_F(OptionsParserTest,NoDefaultCFOptions)2784 TEST_F(OptionsParserTest, NoDefaultCFOptions) {
2785 DBOptions db_opt;
2786 db_opt.max_open_files = 12345;
2787 db_opt.max_background_flushes = 301;
2788 db_opt.max_total_wal_size = 1024;
2789 ColumnFamilyOptions cf_opt;
2790
2791 std::string options_file_content =
2792 "# This is a testing option string.\n"
2793 "# Currently we only support \"#\" styled comment.\n"
2794 "\n"
2795 "[Version]\n"
2796 " rocksdb_version=3.14.0\n"
2797 " options_file_version=1\n"
2798 "[DBOptions]\n"
2799 " max_open_files=12345\n"
2800 " max_background_flushes=301\n"
2801 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
2802 "[CFOptions \"something_else\"]\n"
2803 " # if a section is blank, we will use the default\n";
2804
2805 const std::string kTestFileName = "test-rocksdb-options.ini";
2806 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2807 RocksDBOptionsParser parser;
2808 ASSERT_NOK(
2809 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2810 }
2811
TEST_F(OptionsParserTest,DefaultCFOptionsMustBeTheFirst)2812 TEST_F(OptionsParserTest, DefaultCFOptionsMustBeTheFirst) {
2813 DBOptions db_opt;
2814 db_opt.max_open_files = 12345;
2815 db_opt.max_background_flushes = 301;
2816 db_opt.max_total_wal_size = 1024;
2817 ColumnFamilyOptions cf_opt;
2818
2819 std::string options_file_content =
2820 "# This is a testing option string.\n"
2821 "# Currently we only support \"#\" styled comment.\n"
2822 "\n"
2823 "[Version]\n"
2824 " rocksdb_version=3.14.0\n"
2825 " options_file_version=1\n"
2826 "[DBOptions]\n"
2827 " max_open_files=12345\n"
2828 " max_background_flushes=301\n"
2829 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
2830 "[CFOptions \"something_else\"]\n"
2831 " # if a section is blank, we will use the default\n"
2832 "[CFOptions \"default\"]\n"
2833 " # if a section is blank, we will use the default\n";
2834
2835 const std::string kTestFileName = "test-rocksdb-options.ini";
2836 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2837 RocksDBOptionsParser parser;
2838 ASSERT_NOK(
2839 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2840 }
2841
TEST_F(OptionsParserTest,DuplicateCFOptions)2842 TEST_F(OptionsParserTest, DuplicateCFOptions) {
2843 DBOptions db_opt;
2844 db_opt.max_open_files = 12345;
2845 db_opt.max_background_flushes = 301;
2846 db_opt.max_total_wal_size = 1024;
2847 ColumnFamilyOptions cf_opt;
2848
2849 std::string options_file_content =
2850 "# This is a testing option string.\n"
2851 "# Currently we only support \"#\" styled comment.\n"
2852 "\n"
2853 "[Version]\n"
2854 " rocksdb_version=3.14.0\n"
2855 " options_file_version=1\n"
2856 "[DBOptions]\n"
2857 " max_open_files=12345\n"
2858 " max_background_flushes=301\n"
2859 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
2860 "[CFOptions \"default\"]\n"
2861 "[CFOptions \"something_else\"]\n"
2862 "[CFOptions \"something_else\"]\n";
2863
2864 const std::string kTestFileName = "test-rocksdb-options.ini";
2865 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2866 RocksDBOptionsParser parser;
2867 ASSERT_NOK(
2868 parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2869 }
2870
TEST_F(OptionsParserTest,IgnoreUnknownOptions)2871 TEST_F(OptionsParserTest, IgnoreUnknownOptions) {
2872 for (int case_id = 0; case_id < 5; case_id++) {
2873 DBOptions db_opt;
2874 db_opt.max_open_files = 12345;
2875 db_opt.max_background_flushes = 301;
2876 db_opt.max_total_wal_size = 1024;
2877 ColumnFamilyOptions cf_opt;
2878
2879 std::string version_string;
2880 bool should_ignore = true;
2881 if (case_id == 0) {
2882 // same version
2883 should_ignore = false;
2884 version_string =
2885 ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR) + ".0";
2886 } else if (case_id == 1) {
2887 // higher minor version
2888 should_ignore = true;
2889 version_string =
2890 ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR + 1) + ".0";
2891 } else if (case_id == 2) {
2892 // higher major version.
2893 should_ignore = true;
2894 version_string = ToString(ROCKSDB_MAJOR + 1) + ".0.0";
2895 } else if (case_id == 3) {
2896 // lower minor version
2897 #if ROCKSDB_MINOR == 0
2898 continue;
2899 #else
2900 version_string =
2901 ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR - 1) + ".0";
2902 should_ignore = false;
2903 #endif
2904 } else {
2905 // lower major version
2906 should_ignore = false;
2907 version_string =
2908 ToString(ROCKSDB_MAJOR - 1) + "." + ToString(ROCKSDB_MINOR) + ".0";
2909 }
2910
2911 std::string options_file_content =
2912 "# This is a testing option string.\n"
2913 "# Currently we only support \"#\" styled comment.\n"
2914 "\n"
2915 "[Version]\n"
2916 " rocksdb_version=" +
2917 version_string +
2918 "\n"
2919 " options_file_version=1\n"
2920 "[DBOptions]\n"
2921 " max_open_files=12345\n"
2922 " max_background_flushes=301\n"
2923 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
2924 " unknown_db_option1=321\n"
2925 " unknown_db_option2=false\n"
2926 "[CFOptions \"default\"]\n"
2927 " unknown_cf_option1=hello\n"
2928 "[CFOptions \"something_else\"]\n"
2929 " unknown_cf_option2=world\n"
2930 " # if a section is blank, we will use the default\n";
2931
2932 const std::string kTestFileName = "test-rocksdb-options.ini";
2933 auto s = fs_->FileExists(kTestFileName, IOOptions(), nullptr);
2934 ASSERT_TRUE(s.ok() || s.IsNotFound());
2935 if (s.ok()) {
2936 ASSERT_OK(fs_->DeleteFile(kTestFileName, IOOptions(), nullptr));
2937 }
2938 ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2939 RocksDBOptionsParser parser;
2940 ASSERT_NOK(parser.Parse(kTestFileName, fs_.get(), false,
2941 4096 /* readahead_size */));
2942 if (should_ignore) {
2943 ASSERT_OK(parser.Parse(kTestFileName, fs_.get(),
2944 true /* ignore_unknown_options */,
2945 4096 /* readahead_size */));
2946 } else {
2947 ASSERT_NOK(parser.Parse(kTestFileName, fs_.get(),
2948 true /* ignore_unknown_options */,
2949 4096 /* readahead_size */));
2950 }
2951 }
2952 }
2953
TEST_F(OptionsParserTest,ParseVersion)2954 TEST_F(OptionsParserTest, ParseVersion) {
2955 DBOptions db_opt;
2956 db_opt.max_open_files = 12345;
2957 db_opt.max_background_flushes = 301;
2958 db_opt.max_total_wal_size = 1024;
2959 ColumnFamilyOptions cf_opt;
2960
2961 std::string file_template =
2962 "# This is a testing option string.\n"
2963 "# Currently we only support \"#\" styled comment.\n"
2964 "\n"
2965 "[Version]\n"
2966 " rocksdb_version=3.13.1\n"
2967 " options_file_version=%s\n"
2968 "[DBOptions]\n"
2969 "[CFOptions \"default\"]\n";
2970 const int kLength = 1000;
2971 char buffer[kLength];
2972 RocksDBOptionsParser parser;
2973
2974 const std::vector<std::string> invalid_versions = {
2975 "a.b.c", "3.2.2b", "3.-12", "3. 1", // only digits and dots are allowed
2976 "1.2.3.4",
2977 "1.2.3" // can only contains at most one dot.
2978 "0", // options_file_version must be at least one
2979 "3..2",
2980 ".", ".1.2", // must have at least one digit before each dot
2981 "1.2.", "1.", "2.34."}; // must have at least one digit after each dot
2982 for (auto iv : invalid_versions) {
2983 snprintf(buffer, kLength - 1, file_template.c_str(), iv.c_str());
2984
2985 parser.Reset();
2986 ASSERT_OK(fs_->WriteToNewFile(iv, buffer));
2987 ASSERT_NOK(parser.Parse(iv, fs_.get(), false, 0 /* readahead_size */));
2988 }
2989
2990 const std::vector<std::string> valid_versions = {
2991 "1.232", "100", "3.12", "1", "12.3 ", " 1.25 "};
2992 for (auto vv : valid_versions) {
2993 snprintf(buffer, kLength - 1, file_template.c_str(), vv.c_str());
2994 parser.Reset();
2995 ASSERT_OK(fs_->WriteToNewFile(vv, buffer));
2996 ASSERT_OK(parser.Parse(vv, fs_.get(), false, 0 /* readahead_size */));
2997 }
2998 }
2999
VerifyCFPointerTypedOptions(ColumnFamilyOptions * base_cf_opt,const ColumnFamilyOptions * new_cf_opt,const std::unordered_map<std::string,std::string> * new_cf_opt_map)3000 void VerifyCFPointerTypedOptions(
3001 ColumnFamilyOptions* base_cf_opt, const ColumnFamilyOptions* new_cf_opt,
3002 const std::unordered_map<std::string, std::string>* new_cf_opt_map) {
3003 std::string name_buffer;
3004 ConfigOptions config_options;
3005 config_options.input_strings_escaped = false;
3006 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, *base_cf_opt,
3007 *new_cf_opt, new_cf_opt_map));
3008
3009 // change the name of merge operator back-and-forth
3010 {
3011 auto* merge_operator = dynamic_cast<test::ChanglingMergeOperator*>(
3012 base_cf_opt->merge_operator.get());
3013 if (merge_operator != nullptr) {
3014 name_buffer = merge_operator->Name();
3015 // change the name and expect non-ok status
3016 merge_operator->SetName("some-other-name");
3017 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3018 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3019 // change the name back and expect ok status
3020 merge_operator->SetName(name_buffer);
3021 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3022 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3023 }
3024 }
3025
3026 // change the name of the compaction filter factory back-and-forth
3027 {
3028 auto* compaction_filter_factory =
3029 dynamic_cast<test::ChanglingCompactionFilterFactory*>(
3030 base_cf_opt->compaction_filter_factory.get());
3031 if (compaction_filter_factory != nullptr) {
3032 name_buffer = compaction_filter_factory->Name();
3033 // change the name and expect non-ok status
3034 compaction_filter_factory->SetName("some-other-name");
3035 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3036 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3037 // change the name back and expect ok status
3038 compaction_filter_factory->SetName(name_buffer);
3039 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3040 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3041 }
3042 }
3043
3044 // test by setting compaction_filter to nullptr
3045 {
3046 auto* tmp_compaction_filter = base_cf_opt->compaction_filter;
3047 if (tmp_compaction_filter != nullptr) {
3048 base_cf_opt->compaction_filter = nullptr;
3049 // set compaction_filter to nullptr and expect non-ok status
3050 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3051 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3052 // set the value back and expect ok status
3053 base_cf_opt->compaction_filter = tmp_compaction_filter;
3054 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3055 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3056 }
3057 }
3058
3059 // test by setting table_factory to nullptr
3060 {
3061 auto tmp_table_factory = base_cf_opt->table_factory;
3062 if (tmp_table_factory != nullptr) {
3063 base_cf_opt->table_factory.reset();
3064 // set table_factory to nullptr and expect non-ok status
3065 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3066 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3067 // set the value back and expect ok status
3068 base_cf_opt->table_factory = tmp_table_factory;
3069 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3070 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3071 }
3072 }
3073
3074 // test by setting memtable_factory to nullptr
3075 {
3076 auto tmp_memtable_factory = base_cf_opt->memtable_factory;
3077 if (tmp_memtable_factory != nullptr) {
3078 base_cf_opt->memtable_factory.reset();
3079 // set memtable_factory to nullptr and expect non-ok status
3080 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3081 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3082 // set the value back and expect ok status
3083 base_cf_opt->memtable_factory = tmp_memtable_factory;
3084 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3085 config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3086 }
3087 }
3088 }
3089
TEST_F(OptionsParserTest,Readahead)3090 TEST_F(OptionsParserTest, Readahead) {
3091 DBOptions base_db_opt;
3092 std::vector<ColumnFamilyOptions> base_cf_opts;
3093 base_cf_opts.emplace_back();
3094 base_cf_opts.emplace_back();
3095
3096 std::string one_mb_string = std::string(1024 * 1024, 'x');
3097 std::vector<std::string> cf_names = {"default", one_mb_string};
3098 const std::string kOptionsFileName = "test-persisted-options.ini";
3099
3100 ASSERT_OK(PersistRocksDBOptions(base_db_opt, cf_names, base_cf_opts,
3101 kOptionsFileName, fs_.get()));
3102
3103 uint64_t file_size = 0;
3104 ASSERT_OK(
3105 fs_->GetFileSize(kOptionsFileName, IOOptions(), &file_size, nullptr));
3106 assert(file_size > 0);
3107
3108 RocksDBOptionsParser parser;
3109
3110 fs_->num_seq_file_read_ = 0;
3111 size_t readahead_size = 128 * 1024;
3112
3113 ASSERT_OK(parser.Parse(kOptionsFileName, fs_.get(), false, readahead_size));
3114 ASSERT_EQ(fs_->num_seq_file_read_.load(),
3115 (file_size - 1) / readahead_size + 1);
3116
3117 fs_->num_seq_file_read_.store(0);
3118 readahead_size = 1024 * 1024;
3119 ASSERT_OK(parser.Parse(kOptionsFileName, fs_.get(), false, readahead_size));
3120 ASSERT_EQ(fs_->num_seq_file_read_.load(),
3121 (file_size - 1) / readahead_size + 1);
3122
3123 // Tiny readahead. 8 KB is read each time.
3124 fs_->num_seq_file_read_.store(0);
3125 ASSERT_OK(
3126 parser.Parse(kOptionsFileName, fs_.get(), false, 1 /* readahead_size */));
3127 ASSERT_GE(fs_->num_seq_file_read_.load(), file_size / (8 * 1024));
3128 ASSERT_LT(fs_->num_seq_file_read_.load(), file_size / (8 * 1024) * 2);
3129
3130 // Disable readahead means 512KB readahead.
3131 fs_->num_seq_file_read_.store(0);
3132 ASSERT_OK(
3133 parser.Parse(kOptionsFileName, fs_.get(), false, 0 /* readahead_size */));
3134 ASSERT_GE(fs_->num_seq_file_read_.load(), (file_size - 1) / (512 * 1024) + 1);
3135 }
3136
TEST_F(OptionsParserTest,DumpAndParse)3137 TEST_F(OptionsParserTest, DumpAndParse) {
3138 DBOptions base_db_opt;
3139 std::vector<ColumnFamilyOptions> base_cf_opts;
3140 std::vector<std::string> cf_names = {"default", "cf1", "cf2", "cf3",
3141 "c:f:4:4:4"
3142 "p\\i\\k\\a\\chu\\\\\\",
3143 "###rocksdb#1-testcf#2###"};
3144 const int num_cf = static_cast<int>(cf_names.size());
3145 Random rnd(302);
3146 test::RandomInitDBOptions(&base_db_opt, &rnd);
3147 base_db_opt.db_log_dir += "/#odd #but #could #happen #path #/\\\\#OMG";
3148
3149 BlockBasedTableOptions special_bbto;
3150 special_bbto.cache_index_and_filter_blocks = true;
3151 special_bbto.block_size = 999999;
3152
3153 for (int c = 0; c < num_cf; ++c) {
3154 ColumnFamilyOptions cf_opt;
3155 Random cf_rnd(0xFB + c);
3156 test::RandomInitCFOptions(&cf_opt, base_db_opt, &cf_rnd);
3157 if (c < 4) {
3158 cf_opt.prefix_extractor.reset(test::RandomSliceTransform(&rnd, c));
3159 }
3160 if (c < 3) {
3161 cf_opt.table_factory.reset(test::RandomTableFactory(&rnd, c));
3162 } else if (c == 4) {
3163 cf_opt.table_factory.reset(NewBlockBasedTableFactory(special_bbto));
3164 }
3165 base_cf_opts.emplace_back(cf_opt);
3166 }
3167
3168 const std::string kOptionsFileName = "test-persisted-options.ini";
3169 // Use default for escaped(true), unknown(false) and check (exact)
3170 ConfigOptions config_options;
3171 ASSERT_OK(PersistRocksDBOptions(base_db_opt, cf_names, base_cf_opts,
3172 kOptionsFileName, fs_.get()));
3173
3174 RocksDBOptionsParser parser;
3175 ASSERT_OK(parser.Parse(config_options, kOptionsFileName, fs_.get()));
3176
3177 // Make sure block-based table factory options was deserialized correctly
3178 std::shared_ptr<TableFactory> ttf = (*parser.cf_opts())[4].table_factory;
3179 ASSERT_EQ(TableFactory::kBlockBasedTableName(), std::string(ttf->Name()));
3180 const auto parsed_bbto = ttf->GetOptions<BlockBasedTableOptions>();
3181 ASSERT_NE(parsed_bbto, nullptr);
3182 ASSERT_EQ(special_bbto.block_size, parsed_bbto->block_size);
3183 ASSERT_EQ(special_bbto.cache_index_and_filter_blocks,
3184 parsed_bbto->cache_index_and_filter_blocks);
3185
3186 ASSERT_OK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
3187 config_options, base_db_opt, cf_names, base_cf_opts, kOptionsFileName,
3188 fs_.get()));
3189
3190 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(
3191 config_options, *parser.db_opt(), base_db_opt));
3192 for (int c = 0; c < num_cf; ++c) {
3193 const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
3194 ASSERT_NE(cf_opt, nullptr);
3195 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3196 config_options, base_cf_opts[c], *cf_opt,
3197 &(parser.cf_opt_maps()->at(c))));
3198 }
3199
3200 // Further verify pointer-typed options
3201 for (int c = 0; c < num_cf; ++c) {
3202 const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
3203 ASSERT_NE(cf_opt, nullptr);
3204 VerifyCFPointerTypedOptions(&base_cf_opts[c], cf_opt,
3205 &(parser.cf_opt_maps()->at(c)));
3206 }
3207
3208 ASSERT_EQ(parser.GetCFOptions("does not exist"), nullptr);
3209
3210 base_db_opt.max_open_files++;
3211 ASSERT_NOK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
3212 config_options, base_db_opt, cf_names, base_cf_opts, kOptionsFileName,
3213 fs_.get()));
3214
3215 for (int c = 0; c < num_cf; ++c) {
3216 if (base_cf_opts[c].compaction_filter) {
3217 delete base_cf_opts[c].compaction_filter;
3218 }
3219 }
3220 }
3221
TEST_F(OptionsParserTest,DifferentDefault)3222 TEST_F(OptionsParserTest, DifferentDefault) {
3223 const std::string kOptionsFileName = "test-persisted-options.ini";
3224
3225 ColumnFamilyOptions cf_level_opts;
3226 ASSERT_EQ(CompactionPri::kMinOverlappingRatio, cf_level_opts.compaction_pri);
3227 cf_level_opts.OptimizeLevelStyleCompaction();
3228
3229 ColumnFamilyOptions cf_univ_opts;
3230 cf_univ_opts.OptimizeUniversalStyleCompaction();
3231
3232 ASSERT_OK(PersistRocksDBOptions(DBOptions(), {"default", "universal"},
3233 {cf_level_opts, cf_univ_opts},
3234 kOptionsFileName, fs_.get()));
3235
3236 RocksDBOptionsParser parser;
3237 ASSERT_OK(parser.Parse(kOptionsFileName, fs_.get(), false,
3238 4096 /* readahead_size */));
3239
3240 {
3241 Options old_default_opts;
3242 old_default_opts.OldDefaults();
3243 ASSERT_EQ(10 * 1048576, old_default_opts.max_bytes_for_level_base);
3244 ASSERT_EQ(5000, old_default_opts.max_open_files);
3245 ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3246 ASSERT_EQ(WALRecoveryMode::kTolerateCorruptedTailRecords,
3247 old_default_opts.wal_recovery_mode);
3248 }
3249 {
3250 Options old_default_opts;
3251 old_default_opts.OldDefaults(4, 6);
3252 ASSERT_EQ(10 * 1048576, old_default_opts.max_bytes_for_level_base);
3253 ASSERT_EQ(5000, old_default_opts.max_open_files);
3254 }
3255 {
3256 Options old_default_opts;
3257 old_default_opts.OldDefaults(4, 7);
3258 ASSERT_NE(10 * 1048576, old_default_opts.max_bytes_for_level_base);
3259 ASSERT_NE(4, old_default_opts.table_cache_numshardbits);
3260 ASSERT_EQ(5000, old_default_opts.max_open_files);
3261 ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3262 }
3263 {
3264 ColumnFamilyOptions old_default_cf_opts;
3265 old_default_cf_opts.OldDefaults();
3266 ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
3267 ASSERT_EQ(4 << 20, old_default_cf_opts.write_buffer_size);
3268 ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
3269 ASSERT_EQ(0, old_default_cf_opts.soft_pending_compaction_bytes_limit);
3270 ASSERT_EQ(0, old_default_cf_opts.hard_pending_compaction_bytes_limit);
3271 ASSERT_EQ(CompactionPri::kByCompensatedSize,
3272 old_default_cf_opts.compaction_pri);
3273 }
3274 {
3275 ColumnFamilyOptions old_default_cf_opts;
3276 old_default_cf_opts.OldDefaults(4, 6);
3277 ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
3278 ASSERT_EQ(CompactionPri::kByCompensatedSize,
3279 old_default_cf_opts.compaction_pri);
3280 }
3281 {
3282 ColumnFamilyOptions old_default_cf_opts;
3283 old_default_cf_opts.OldDefaults(4, 7);
3284 ASSERT_NE(2 * 1048576, old_default_cf_opts.target_file_size_base);
3285 ASSERT_EQ(CompactionPri::kByCompensatedSize,
3286 old_default_cf_opts.compaction_pri);
3287 }
3288 {
3289 Options old_default_opts;
3290 old_default_opts.OldDefaults(5, 1);
3291 ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3292 }
3293 {
3294 Options old_default_opts;
3295 old_default_opts.OldDefaults(5, 2);
3296 ASSERT_EQ(16 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3297 ASSERT_TRUE(old_default_opts.compaction_pri ==
3298 CompactionPri::kByCompensatedSize);
3299 }
3300 {
3301 Options old_default_opts;
3302 old_default_opts.OldDefaults(5, 18);
3303 ASSERT_TRUE(old_default_opts.compaction_pri ==
3304 CompactionPri::kByCompensatedSize);
3305 }
3306
3307 Options small_opts;
3308 small_opts.OptimizeForSmallDb();
3309 ASSERT_EQ(2 << 20, small_opts.write_buffer_size);
3310 ASSERT_EQ(5000, small_opts.max_open_files);
3311 }
3312
3313 class OptionsSanityCheckTest : public OptionsParserTest {
3314 public:
OptionsSanityCheckTest()3315 OptionsSanityCheckTest() {}
3316
3317 protected:
SanityCheckCFOptions(const ColumnFamilyOptions & cf_opts,ConfigOptions::SanityLevel level,bool input_strings_escaped=true)3318 Status SanityCheckCFOptions(const ColumnFamilyOptions& cf_opts,
3319 ConfigOptions::SanityLevel level,
3320 bool input_strings_escaped = true) {
3321 ConfigOptions config_options;
3322 config_options.sanity_level = level;
3323 config_options.ignore_unknown_options = false;
3324 config_options.input_strings_escaped = input_strings_escaped;
3325
3326 return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
3327 config_options, DBOptions(), {"default"}, {cf_opts}, kOptionsFileName,
3328 fs_.get());
3329 }
3330
PersistCFOptions(const ColumnFamilyOptions & cf_opts)3331 Status PersistCFOptions(const ColumnFamilyOptions& cf_opts) {
3332 Status s = fs_->DeleteFile(kOptionsFileName, IOOptions(), nullptr);
3333 if (!s.ok()) {
3334 return s;
3335 }
3336 return PersistRocksDBOptions(DBOptions(), {"default"}, {cf_opts},
3337 kOptionsFileName, fs_.get());
3338 }
3339
3340 const std::string kOptionsFileName = "OPTIONS";
3341 };
3342
TEST_F(OptionsSanityCheckTest,SanityCheck)3343 TEST_F(OptionsSanityCheckTest, SanityCheck) {
3344 ColumnFamilyOptions opts;
3345 Random rnd(301);
3346
3347 // default ColumnFamilyOptions
3348 {
3349 ASSERT_OK(PersistCFOptions(opts));
3350 ASSERT_OK(
3351 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3352 }
3353
3354 // prefix_extractor
3355 {
3356 // Okay to change prefix_extractor form nullptr to non-nullptr
3357 ASSERT_EQ(opts.prefix_extractor.get(), nullptr);
3358 opts.prefix_extractor.reset(NewCappedPrefixTransform(10));
3359 ASSERT_OK(SanityCheckCFOptions(
3360 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3361 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3362
3363 // persist the change
3364 ASSERT_OK(PersistCFOptions(opts));
3365 ASSERT_OK(
3366 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3367
3368 // use same prefix extractor but with different parameter
3369 opts.prefix_extractor.reset(NewCappedPrefixTransform(15));
3370 // expect pass only in
3371 // ConfigOptions::kSanityLevelLooselyCompatible
3372 ASSERT_NOK(
3373 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3374 ASSERT_OK(SanityCheckCFOptions(
3375 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3376 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3377
3378 // repeat the test with FixedPrefixTransform
3379 opts.prefix_extractor.reset(NewFixedPrefixTransform(10));
3380 ASSERT_NOK(
3381 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3382 ASSERT_OK(SanityCheckCFOptions(
3383 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3384 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3385
3386 // persist the change of prefix_extractor
3387 ASSERT_OK(PersistCFOptions(opts));
3388 ASSERT_OK(
3389 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3390
3391 // use same prefix extractor but with different parameter
3392 opts.prefix_extractor.reset(NewFixedPrefixTransform(15));
3393 // expect pass only in
3394 // ConfigOptions::kSanityLevelLooselyCompatible
3395 ASSERT_NOK(
3396 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3397 ASSERT_OK(SanityCheckCFOptions(
3398 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3399 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3400
3401 // Change prefix extractor from non-nullptr to nullptr
3402 opts.prefix_extractor.reset();
3403 // expect pass as it's safe to change prefix_extractor
3404 // from non-null to null
3405 ASSERT_OK(SanityCheckCFOptions(
3406 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3407 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3408 }
3409 // persist the change
3410 ASSERT_OK(PersistCFOptions(opts));
3411 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3412
3413 // table_factory
3414 {
3415 for (int tb = 0; tb <= 2; ++tb) {
3416 // change the table factory
3417 opts.table_factory.reset(test::RandomTableFactory(&rnd, tb));
3418 ASSERT_NOK(SanityCheckCFOptions(
3419 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3420 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3421
3422 // persist the change
3423 ASSERT_OK(PersistCFOptions(opts));
3424 ASSERT_OK(
3425 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3426 }
3427 }
3428
3429 // merge_operator
3430 {
3431 // Test when going from nullptr -> merge operator
3432 opts.merge_operator.reset(test::RandomMergeOperator(&rnd));
3433 ASSERT_OK(SanityCheckCFOptions(
3434 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3435 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3436
3437 // persist the change
3438 ASSERT_OK(PersistCFOptions(opts));
3439 ASSERT_OK(
3440 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3441
3442 for (int test = 0; test < 5; ++test) {
3443 // change the merge operator
3444 opts.merge_operator.reset(test::RandomMergeOperator(&rnd));
3445 ASSERT_NOK(SanityCheckCFOptions(
3446 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3447 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3448
3449 // persist the change
3450 ASSERT_OK(PersistCFOptions(opts));
3451 ASSERT_OK(
3452 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3453 }
3454
3455 // Test when going from merge operator -> nullptr
3456 opts.merge_operator = nullptr;
3457 ASSERT_NOK(SanityCheckCFOptions(
3458 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3459 ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3460
3461 // persist the change
3462 ASSERT_OK(PersistCFOptions(opts));
3463 ASSERT_OK(
3464 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3465 }
3466
3467 // compaction_filter
3468 {
3469 for (int test = 0; test < 5; ++test) {
3470 // change the compaction filter
3471 opts.compaction_filter = test::RandomCompactionFilter(&rnd);
3472 ASSERT_NOK(
3473 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3474 ASSERT_OK(SanityCheckCFOptions(
3475 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3476
3477 // persist the change
3478 ASSERT_OK(PersistCFOptions(opts));
3479 ASSERT_OK(
3480 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3481 delete opts.compaction_filter;
3482 opts.compaction_filter = nullptr;
3483 }
3484 }
3485
3486 // compaction_filter_factory
3487 {
3488 for (int test = 0; test < 5; ++test) {
3489 // change the compaction filter factory
3490 opts.compaction_filter_factory.reset(
3491 test::RandomCompactionFilterFactory(&rnd));
3492 ASSERT_NOK(
3493 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3494 ASSERT_OK(SanityCheckCFOptions(
3495 opts, ConfigOptions::kSanityLevelLooselyCompatible));
3496
3497 // persist the change
3498 ASSERT_OK(PersistCFOptions(opts));
3499 ASSERT_OK(
3500 SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3501 }
3502 }
3503 }
3504
3505 namespace {
IsEscapedString(const std::string & str)3506 bool IsEscapedString(const std::string& str) {
3507 for (size_t i = 0; i < str.size(); ++i) {
3508 if (str[i] == '\\') {
3509 // since we already handle those two consecutive '\'s in
3510 // the next if-then branch, any '\' appear at the end
3511 // of an escaped string in such case is not valid.
3512 if (i == str.size() - 1) {
3513 return false;
3514 }
3515 if (str[i + 1] == '\\') {
3516 // if there're two consecutive '\'s, skip the second one.
3517 i++;
3518 continue;
3519 }
3520 switch (str[i + 1]) {
3521 case ':':
3522 case '\\':
3523 case '#':
3524 continue;
3525 default:
3526 // if true, '\' together with str[i + 1] is not a valid escape.
3527 if (UnescapeChar(str[i + 1]) == str[i + 1]) {
3528 return false;
3529 }
3530 }
3531 } else if (isSpecialChar(str[i]) && (i == 0 || str[i - 1] != '\\')) {
3532 return false;
3533 }
3534 }
3535 return true;
3536 }
3537 } // namespace
3538
TEST_F(OptionsParserTest,IntegerParsing)3539 TEST_F(OptionsParserTest, IntegerParsing) {
3540 ASSERT_EQ(ParseUint64("18446744073709551615"), 18446744073709551615U);
3541 ASSERT_EQ(ParseUint32("4294967295"), 4294967295U);
3542 ASSERT_EQ(ParseSizeT("18446744073709551615"), 18446744073709551615U);
3543 ASSERT_EQ(ParseInt64("9223372036854775807"), 9223372036854775807);
3544 ASSERT_EQ(ParseInt64("-9223372036854775808"), port::kMinInt64);
3545 ASSERT_EQ(ParseInt32("2147483647"), 2147483647);
3546 ASSERT_EQ(ParseInt32("-2147483648"), port::kMinInt32);
3547 ASSERT_EQ(ParseInt("-32767"), -32767);
3548 ASSERT_EQ(ParseDouble("-1.234567"), -1.234567);
3549 }
3550
TEST_F(OptionsParserTest,EscapeOptionString)3551 TEST_F(OptionsParserTest, EscapeOptionString) {
3552 ASSERT_EQ(UnescapeOptionString(
3553 "This is a test string with \\# \\: and \\\\ escape chars."),
3554 "This is a test string with # : and \\ escape chars.");
3555
3556 ASSERT_EQ(
3557 EscapeOptionString("This is a test string with # : and \\ escape chars."),
3558 "This is a test string with \\# \\: and \\\\ escape chars.");
3559
3560 std::string readible_chars =
3561 "A String like this \"1234567890-=_)(*&^%$#@!ertyuiop[]{POIU"
3562 "YTREWQasdfghjkl;':LKJHGFDSAzxcvbnm,.?>"
3563 "<MNBVCXZ\\\" should be okay to \\#\\\\\\:\\#\\#\\#\\ "
3564 "be serialized and deserialized";
3565
3566 std::string escaped_string = EscapeOptionString(readible_chars);
3567 ASSERT_TRUE(IsEscapedString(escaped_string));
3568 // This two transformations should be canceled and should output
3569 // the original input.
3570 ASSERT_EQ(UnescapeOptionString(escaped_string), readible_chars);
3571
3572 std::string all_chars;
3573 for (unsigned char c = 0;; ++c) {
3574 all_chars += c;
3575 if (c == 255) {
3576 break;
3577 }
3578 }
3579 escaped_string = EscapeOptionString(all_chars);
3580 ASSERT_TRUE(IsEscapedString(escaped_string));
3581 ASSERT_EQ(UnescapeOptionString(escaped_string), all_chars);
3582
3583 ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
3584 " A simple statement with a comment. # like this :)"),
3585 "A simple statement with a comment.");
3586
3587 ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
3588 "Escape \\# and # comment together ."),
3589 "Escape \\# and");
3590 }
3591
TestAndCompareOption(const ConfigOptions & config_options,const OptionTypeInfo & opt_info,const std::string & opt_name,void * base_ptr,void * comp_ptr)3592 static void TestAndCompareOption(const ConfigOptions& config_options,
3593 const OptionTypeInfo& opt_info,
3594 const std::string& opt_name, void* base_ptr,
3595 void* comp_ptr) {
3596 std::string result, mismatch;
3597 ASSERT_OK(opt_info.Serialize(config_options, opt_name, base_ptr, &result));
3598 ASSERT_OK(opt_info.Parse(config_options, opt_name, result, comp_ptr));
3599 ASSERT_TRUE(opt_info.AreEqual(config_options, opt_name, base_ptr, comp_ptr,
3600 &mismatch));
3601 }
3602
TestAndCompareOption(const ConfigOptions & config_options,const OptionTypeInfo & opt_info,const std::string & opt_name,const std::string & opt_value,void * base_ptr,void * comp_ptr)3603 static void TestAndCompareOption(const ConfigOptions& config_options,
3604 const OptionTypeInfo& opt_info,
3605 const std::string& opt_name,
3606 const std::string& opt_value, void* base_ptr,
3607 void* comp_ptr) {
3608 ASSERT_OK(opt_info.Parse(config_options, opt_name, opt_value, base_ptr));
3609 TestAndCompareOption(config_options, opt_info, opt_name, base_ptr, comp_ptr);
3610 }
3611
3612 template <typename T>
TestOptInfo(const ConfigOptions & config_options,OptionType opt_type,T * base,T * comp)3613 void TestOptInfo(const ConfigOptions& config_options, OptionType opt_type,
3614 T* base, T* comp) {
3615 std::string result;
3616 OptionTypeInfo opt_info(0, opt_type);
3617 ASSERT_FALSE(opt_info.AreEqual(config_options, "base", base, comp, &result));
3618 ASSERT_EQ(result, "base");
3619 ASSERT_NE(*base, *comp);
3620 TestAndCompareOption(config_options, opt_info, "base", base, comp);
3621 ASSERT_EQ(*base, *comp);
3622 }
3623
3624 class OptionTypeInfoTest : public testing::Test {};
3625
TEST_F(OptionTypeInfoTest,BasicTypes)3626 TEST_F(OptionTypeInfoTest, BasicTypes) {
3627 ConfigOptions config_options;
3628 {
3629 bool a = true, b = false;
3630 TestOptInfo(config_options, OptionType::kBoolean, &a, &b);
3631 }
3632 {
3633 int a = 100, b = 200;
3634 TestOptInfo(config_options, OptionType::kInt, &a, &b);
3635 }
3636 {
3637 int32_t a = 100, b = 200;
3638 TestOptInfo(config_options, OptionType::kInt32T, &a, &b);
3639 }
3640 {
3641 int64_t a = 100, b = 200;
3642 TestOptInfo(config_options, OptionType::kInt64T, &a, &b);
3643 }
3644 {
3645 unsigned int a = 100, b = 200;
3646 TestOptInfo(config_options, OptionType::kUInt, &a, &b);
3647 }
3648 {
3649 uint32_t a = 100, b = 200;
3650 TestOptInfo(config_options, OptionType::kUInt32T, &a, &b);
3651 }
3652 {
3653 uint64_t a = 100, b = 200;
3654 TestOptInfo(config_options, OptionType::kUInt64T, &a, &b);
3655 }
3656 {
3657 size_t a = 100, b = 200;
3658 TestOptInfo(config_options, OptionType::kSizeT, &a, &b);
3659 }
3660 {
3661 std::string a = "100", b = "200";
3662 TestOptInfo(config_options, OptionType::kString, &a, &b);
3663 }
3664 {
3665 double a = 1.0, b = 2.0;
3666 TestOptInfo(config_options, OptionType::kDouble, &a, &b);
3667 }
3668 }
3669
TEST_F(OptionTypeInfoTest,TestInvalidArgs)3670 TEST_F(OptionTypeInfoTest, TestInvalidArgs) {
3671 ConfigOptions config_options;
3672 bool b;
3673 int i;
3674 int32_t i32;
3675 int64_t i64;
3676 unsigned int u;
3677 int32_t u32;
3678 int64_t u64;
3679 size_t sz;
3680 double d;
3681
3682 ASSERT_NOK(OptionTypeInfo(0, OptionType::kBoolean)
3683 .Parse(config_options, "b", "x", &b));
3684 ASSERT_NOK(
3685 OptionTypeInfo(0, OptionType::kInt).Parse(config_options, "b", "x", &i));
3686 ASSERT_NOK(OptionTypeInfo(0, OptionType::kInt32T)
3687 .Parse(config_options, "b", "x", &i32));
3688 ASSERT_NOK(OptionTypeInfo(0, OptionType::kInt64T)
3689 .Parse(config_options, "b", "x", &i64));
3690 ASSERT_NOK(
3691 OptionTypeInfo(0, OptionType::kUInt).Parse(config_options, "b", "x", &u));
3692 ASSERT_NOK(OptionTypeInfo(0, OptionType::kUInt32T)
3693 .Parse(config_options, "b", "x", &u32));
3694 ASSERT_NOK(OptionTypeInfo(0, OptionType::kUInt64T)
3695 .Parse(config_options, "b", "x", &u64));
3696 ASSERT_NOK(OptionTypeInfo(0, OptionType::kSizeT)
3697 .Parse(config_options, "b", "x", &sz));
3698 ASSERT_NOK(OptionTypeInfo(0, OptionType::kDouble)
3699 .Parse(config_options, "b", "x", &d));
3700
3701 // Don't know how to convert Unknowns to anything else
3702 ASSERT_NOK(OptionTypeInfo(0, OptionType::kUnknown)
3703 .Parse(config_options, "b", "x", &d));
3704
3705 // Verify that if the parse function throws an exception, it is also trapped
3706 OptionTypeInfo func_info(0, OptionType::kUnknown,
3707 OptionVerificationType::kNormal,
3708 OptionTypeFlags::kNone,
3709 [](const ConfigOptions&, const std::string&,
3710 const std::string& value, void* addr) {
3711 auto ptr = static_cast<int*>(addr);
3712 *ptr = ParseInt(value);
3713 return Status::OK();
3714 });
3715 ASSERT_OK(func_info.Parse(config_options, "b", "1", &i));
3716 ASSERT_NOK(func_info.Parse(config_options, "b", "x", &i));
3717 }
3718
TEST_F(OptionTypeInfoTest,TestParseFunc)3719 TEST_F(OptionTypeInfoTest, TestParseFunc) {
3720 OptionTypeInfo opt_info(
3721 0, OptionType::kUnknown, OptionVerificationType::kNormal,
3722 OptionTypeFlags::kNone,
3723 [](const ConfigOptions& /*opts*/, const std::string& name,
3724 const std::string& value, void* addr) {
3725 auto ptr = static_cast<std::string*>(addr);
3726 if (name == "Oops") {
3727 return Status::InvalidArgument(value);
3728 } else {
3729 *ptr = value + " " + name;
3730 return Status::OK();
3731 }
3732 });
3733 ConfigOptions config_options;
3734 std::string base;
3735 ASSERT_OK(opt_info.Parse(config_options, "World", "Hello", &base));
3736 ASSERT_EQ(base, "Hello World");
3737 ASSERT_NOK(opt_info.Parse(config_options, "Oops", "Hello", &base));
3738 }
3739
TEST_F(OptionTypeInfoTest,TestSerializeFunc)3740 TEST_F(OptionTypeInfoTest, TestSerializeFunc) {
3741 OptionTypeInfo opt_info(
3742 0, OptionType::kString, OptionVerificationType::kNormal,
3743 OptionTypeFlags::kNone, nullptr,
3744 [](const ConfigOptions& /*opts*/, const std::string& name,
3745 const void* /*addr*/, std::string* value) {
3746 if (name == "Oops") {
3747 return Status::InvalidArgument(name);
3748 } else {
3749 *value = name;
3750 return Status::OK();
3751 }
3752 },
3753 nullptr);
3754 ConfigOptions config_options;
3755 std::string base;
3756 std::string value;
3757 ASSERT_OK(opt_info.Serialize(config_options, "Hello", &base, &value));
3758 ASSERT_EQ(value, "Hello");
3759 ASSERT_NOK(opt_info.Serialize(config_options, "Oops", &base, &value));
3760 }
3761
TEST_F(OptionTypeInfoTest,TestEqualsFunc)3762 TEST_F(OptionTypeInfoTest, TestEqualsFunc) {
3763 OptionTypeInfo opt_info(
3764 0, OptionType::kInt, OptionVerificationType::kNormal,
3765 OptionTypeFlags::kNone, nullptr, nullptr,
3766 [](const ConfigOptions& /*opts*/, const std::string& name,
3767 const void* addr1, const void* addr2, std::string* mismatch) {
3768 auto i1 = *(static_cast<const int*>(addr1));
3769 auto i2 = *(static_cast<const int*>(addr2));
3770 if (name == "LT") {
3771 return i1 < i2;
3772 } else if (name == "GT") {
3773 return i1 > i2;
3774 } else if (name == "EQ") {
3775 return i1 == i2;
3776 } else {
3777 *mismatch = name + "???";
3778 return false;
3779 }
3780 });
3781
3782 ConfigOptions config_options;
3783 int int1 = 100;
3784 int int2 = 200;
3785 std::string mismatch;
3786 ASSERT_TRUE(opt_info.AreEqual(config_options, "LT", &int1, &int2, &mismatch));
3787 ASSERT_EQ(mismatch, "");
3788 ASSERT_FALSE(
3789 opt_info.AreEqual(config_options, "GT", &int1, &int2, &mismatch));
3790 ASSERT_EQ(mismatch, "GT");
3791 ASSERT_FALSE(
3792 opt_info.AreEqual(config_options, "NO", &int1, &int2, &mismatch));
3793 ASSERT_EQ(mismatch, "NO???");
3794 }
3795
TEST_F(OptionTypeInfoTest,TestOptionFlags)3796 TEST_F(OptionTypeInfoTest, TestOptionFlags) {
3797 OptionTypeInfo opt_none(0, OptionType::kString,
3798 OptionVerificationType::kNormal,
3799 OptionTypeFlags::kDontSerialize);
3800 OptionTypeInfo opt_never(0, OptionType::kString,
3801 OptionVerificationType::kNormal,
3802 OptionTypeFlags::kCompareNever);
3803 OptionTypeInfo opt_alias(0, OptionType::kString,
3804 OptionVerificationType::kAlias,
3805 OptionTypeFlags::kNone);
3806 OptionTypeInfo opt_deprecated(0, OptionType::kString,
3807 OptionVerificationType::kDeprecated,
3808 OptionTypeFlags::kNone);
3809 ConfigOptions config_options;
3810 std::string opts_str;
3811 std::string base = "base";
3812 std::string comp = "comp";
3813
3814 // If marked string none, the serialization returns not supported
3815 ASSERT_NOK(opt_none.Serialize(config_options, "None", &base, &opts_str));
3816 // If marked never compare, they match even when they do not
3817 ASSERT_TRUE(opt_never.AreEqual(config_options, "Never", &base, &comp, &base));
3818 ASSERT_FALSE(opt_none.AreEqual(config_options, "Never", &base, &comp, &base));
3819
3820 // An alias can change the value via parse, but does nothing on serialize on
3821 // match
3822 std::string result;
3823 ASSERT_OK(opt_alias.Parse(config_options, "Alias", "Alias", &base));
3824 ASSERT_OK(opt_alias.Serialize(config_options, "Alias", &base, &result));
3825 ASSERT_TRUE(
3826 opt_alias.AreEqual(config_options, "Alias", &base, &comp, &result));
3827 ASSERT_EQ(base, "Alias");
3828 ASSERT_NE(base, comp);
3829
3830 // Deprecated options do nothing on any of the commands
3831 ASSERT_OK(opt_deprecated.Parse(config_options, "Alias", "Deprecated", &base));
3832 ASSERT_OK(opt_deprecated.Serialize(config_options, "Alias", &base, &result));
3833 ASSERT_TRUE(
3834 opt_deprecated.AreEqual(config_options, "Alias", &base, &comp, &result));
3835 ASSERT_EQ(base, "Alias");
3836 ASSERT_NE(base, comp);
3837 }
3838
TEST_F(OptionTypeInfoTest,TestCustomEnum)3839 TEST_F(OptionTypeInfoTest, TestCustomEnum) {
3840 enum TestEnum { kA, kB, kC };
3841 std::unordered_map<std::string, TestEnum> enum_map = {
3842 {"A", TestEnum::kA},
3843 {"B", TestEnum::kB},
3844 {"C", TestEnum::kC},
3845 };
3846 OptionTypeInfo opt_info = OptionTypeInfo::Enum<TestEnum>(0, &enum_map);
3847 TestEnum e1, e2;
3848 ConfigOptions config_options;
3849 std::string result, mismatch;
3850
3851 e2 = TestEnum::kA;
3852
3853 ASSERT_OK(opt_info.Parse(config_options, "", "B", &e1));
3854 ASSERT_OK(opt_info.Serialize(config_options, "", &e1, &result));
3855 ASSERT_EQ(e1, TestEnum::kB);
3856 ASSERT_EQ(result, "B");
3857
3858 ASSERT_FALSE(opt_info.AreEqual(config_options, "Enum", &e1, &e2, &mismatch));
3859 ASSERT_EQ(mismatch, "Enum");
3860
3861 TestAndCompareOption(config_options, opt_info, "", "C", &e1, &e2);
3862 ASSERT_EQ(e2, TestEnum::kC);
3863
3864 ASSERT_NOK(opt_info.Parse(config_options, "", "D", &e1));
3865 ASSERT_EQ(e1, TestEnum::kC);
3866 }
3867
TEST_F(OptionTypeInfoTest,TestBuiltinEnum)3868 TEST_F(OptionTypeInfoTest, TestBuiltinEnum) {
3869 ConfigOptions config_options;
3870 for (auto iter : OptionsHelper::compaction_style_string_map) {
3871 CompactionStyle e1, e2;
3872 TestAndCompareOption(config_options,
3873 OptionTypeInfo(0, OptionType::kCompactionStyle),
3874 "CompactionStyle", iter.first, &e1, &e2);
3875 ASSERT_EQ(e1, iter.second);
3876 }
3877 for (auto iter : OptionsHelper::compaction_pri_string_map) {
3878 CompactionPri e1, e2;
3879 TestAndCompareOption(config_options,
3880 OptionTypeInfo(0, OptionType::kCompactionPri),
3881 "CompactionPri", iter.first, &e1, &e2);
3882 ASSERT_EQ(e1, iter.second);
3883 }
3884 for (auto iter : OptionsHelper::compression_type_string_map) {
3885 CompressionType e1, e2;
3886 TestAndCompareOption(config_options,
3887 OptionTypeInfo(0, OptionType::kCompressionType),
3888 "CompressionType", iter.first, &e1, &e2);
3889 ASSERT_EQ(e1, iter.second);
3890 }
3891 for (auto iter : OptionsHelper::compaction_stop_style_string_map) {
3892 CompactionStopStyle e1, e2;
3893 TestAndCompareOption(config_options,
3894 OptionTypeInfo(0, OptionType::kCompactionStopStyle),
3895 "CompactionStopStyle", iter.first, &e1, &e2);
3896 ASSERT_EQ(e1, iter.second);
3897 }
3898 for (auto iter : OptionsHelper::checksum_type_string_map) {
3899 ChecksumType e1, e2;
3900 TestAndCompareOption(config_options,
3901 OptionTypeInfo(0, OptionType::kChecksumType),
3902 "CheckSumType", iter.first, &e1, &e2);
3903 ASSERT_EQ(e1, iter.second);
3904 }
3905 for (auto iter : OptionsHelper::encoding_type_string_map) {
3906 EncodingType e1, e2;
3907 TestAndCompareOption(config_options,
3908 OptionTypeInfo(0, OptionType::kEncodingType),
3909 "EncodingType", iter.first, &e1, &e2);
3910 ASSERT_EQ(e1, iter.second);
3911 }
3912 }
3913
TEST_F(OptionTypeInfoTest,TestStruct)3914 TEST_F(OptionTypeInfoTest, TestStruct) {
3915 struct Basic {
3916 int i = 42;
3917 std::string s = "Hello";
3918 };
3919
3920 struct Extended {
3921 int j = 11;
3922 Basic b;
3923 };
3924
3925 std::unordered_map<std::string, OptionTypeInfo> basic_type_map = {
3926 {"i", {offsetof(struct Basic, i), OptionType::kInt}},
3927 {"s", {offsetof(struct Basic, s), OptionType::kString}},
3928 };
3929 OptionTypeInfo basic_info = OptionTypeInfo::Struct(
3930 "b", &basic_type_map, 0, OptionVerificationType::kNormal,
3931 OptionTypeFlags::kMutable);
3932
3933 std::unordered_map<std::string, OptionTypeInfo> extended_type_map = {
3934 {"j", {offsetof(struct Extended, j), OptionType::kInt}},
3935 {"b", OptionTypeInfo::Struct(
3936 "b", &basic_type_map, offsetof(struct Extended, b),
3937 OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
3938 {"m", OptionTypeInfo::Struct(
3939 "m", &basic_type_map, offsetof(struct Extended, b),
3940 OptionVerificationType::kNormal, OptionTypeFlags::kMutable)},
3941 };
3942 OptionTypeInfo extended_info = OptionTypeInfo::Struct(
3943 "e", &extended_type_map, 0, OptionVerificationType::kNormal,
3944 OptionTypeFlags::kMutable);
3945 Extended e1, e2;
3946 ConfigOptions config_options;
3947 std::string mismatch;
3948 TestAndCompareOption(config_options, basic_info, "b", "{i=33;s=33}", &e1.b,
3949 &e2.b);
3950 ASSERT_EQ(e1.b.i, 33);
3951 ASSERT_EQ(e1.b.s, "33");
3952
3953 TestAndCompareOption(config_options, basic_info, "b.i", "44", &e1.b, &e2.b);
3954 ASSERT_EQ(e1.b.i, 44);
3955
3956 TestAndCompareOption(config_options, basic_info, "i", "55", &e1.b, &e2.b);
3957 ASSERT_EQ(e1.b.i, 55);
3958
3959 e1.b.i = 0;
3960
3961 ASSERT_FALSE(
3962 basic_info.AreEqual(config_options, "b", &e1.b, &e2.b, &mismatch));
3963 ASSERT_EQ(mismatch, "b.i");
3964 mismatch.clear();
3965 ASSERT_FALSE(
3966 basic_info.AreEqual(config_options, "b.i", &e1.b, &e2.b, &mismatch));
3967 ASSERT_EQ(mismatch, "b.i");
3968 mismatch.clear();
3969 ASSERT_FALSE(
3970 basic_info.AreEqual(config_options, "i", &e1.b, &e2.b, &mismatch));
3971 ASSERT_EQ(mismatch, "b.i");
3972 mismatch.clear();
3973
3974 e1 = e2;
3975 ASSERT_NOK(basic_info.Parse(config_options, "b", "{i=33;s=33;j=44}", &e1.b));
3976 ASSERT_NOK(basic_info.Parse(config_options, "b.j", "44", &e1.b));
3977 ASSERT_NOK(basic_info.Parse(config_options, "j", "44", &e1.b));
3978
3979 TestAndCompareOption(config_options, extended_info, "e",
3980 "b={i=55;s=55}; j=22;", &e1, &e2);
3981 ASSERT_EQ(e1.b.i, 55);
3982 ASSERT_EQ(e1.j, 22);
3983 ASSERT_EQ(e1.b.s, "55");
3984 TestAndCompareOption(config_options, extended_info, "e.b", "{i=66;s=66;}",
3985 &e1, &e2);
3986 ASSERT_EQ(e1.b.i, 66);
3987 ASSERT_EQ(e1.j, 22);
3988 ASSERT_EQ(e1.b.s, "66");
3989 TestAndCompareOption(config_options, extended_info, "e.b.i", "77", &e1, &e2);
3990 ASSERT_EQ(e1.b.i, 77);
3991 ASSERT_EQ(e1.j, 22);
3992 ASSERT_EQ(e1.b.s, "66");
3993 }
3994
TEST_F(OptionTypeInfoTest,TestVectorType)3995 TEST_F(OptionTypeInfoTest, TestVectorType) {
3996 OptionTypeInfo vec_info = OptionTypeInfo::Vector<std::string>(
3997 0, OptionVerificationType::kNormal, OptionTypeFlags::kNone,
3998 {0, OptionType::kString});
3999 std::vector<std::string> vec1, vec2;
4000 std::string mismatch;
4001
4002 ConfigOptions config_options;
4003 TestAndCompareOption(config_options, vec_info, "v", "a:b:c:d", &vec1, &vec2);
4004 ASSERT_EQ(vec1.size(), 4);
4005 ASSERT_EQ(vec1[0], "a");
4006 ASSERT_EQ(vec1[1], "b");
4007 ASSERT_EQ(vec1[2], "c");
4008 ASSERT_EQ(vec1[3], "d");
4009 vec1[3] = "e";
4010 ASSERT_FALSE(vec_info.AreEqual(config_options, "v", &vec1, &vec2, &mismatch));
4011 ASSERT_EQ(mismatch, "v");
4012
4013 // Test vectors with inner brackets
4014 TestAndCompareOption(config_options, vec_info, "v", "a:{b}:c:d", &vec1,
4015 &vec2);
4016 ASSERT_EQ(vec1.size(), 4);
4017 ASSERT_EQ(vec1[0], "a");
4018 ASSERT_EQ(vec1[1], "b");
4019 ASSERT_EQ(vec1[2], "c");
4020 ASSERT_EQ(vec1[3], "d");
4021
4022 OptionTypeInfo bar_info = OptionTypeInfo::Vector<std::string>(
4023 0, OptionVerificationType::kNormal, OptionTypeFlags::kNone,
4024 {0, OptionType::kString}, '|');
4025 TestAndCompareOption(config_options, vec_info, "v", "x|y|z", &vec1, &vec2);
4026 // Test vectors with inner vector
4027 TestAndCompareOption(config_options, bar_info, "v",
4028 "a|{b1|b2}|{c1|c2|{d1|d2}}", &vec1, &vec2);
4029 ASSERT_EQ(vec1.size(), 3);
4030 ASSERT_EQ(vec1[0], "a");
4031 ASSERT_EQ(vec1[1], "b1|b2");
4032 ASSERT_EQ(vec1[2], "c1|c2|{d1|d2}");
4033 }
4034
TEST_F(OptionTypeInfoTest,TestStaticType)4035 TEST_F(OptionTypeInfoTest, TestStaticType) {
4036 struct SimpleOptions {
4037 size_t size = 0;
4038 bool verify = true;
4039 };
4040
4041 static std::unordered_map<std::string, OptionTypeInfo> type_map = {
4042 {"size", {offsetof(struct SimpleOptions, size), OptionType::kSizeT}},
4043 {"verify",
4044 {offsetof(struct SimpleOptions, verify), OptionType::kBoolean}},
4045 };
4046
4047 ConfigOptions config_options;
4048 SimpleOptions opts, copy;
4049 opts.size = 12345;
4050 opts.verify = false;
4051 std::string str, mismatch;
4052
4053 ASSERT_OK(
4054 OptionTypeInfo::SerializeType(config_options, type_map, &opts, &str));
4055 ASSERT_FALSE(OptionTypeInfo::TypesAreEqual(config_options, type_map, &opts,
4056 ©, &mismatch));
4057 ASSERT_OK(OptionTypeInfo::ParseType(config_options, str, type_map, ©));
4058 ASSERT_TRUE(OptionTypeInfo::TypesAreEqual(config_options, type_map, &opts,
4059 ©, &mismatch));
4060 }
4061
4062 class ConfigOptionsTest : public testing::Test {};
4063
TEST_F(ConfigOptionsTest,EnvFromConfigOptions)4064 TEST_F(ConfigOptionsTest, EnvFromConfigOptions) {
4065 ConfigOptions config_options;
4066 DBOptions db_opts;
4067 Options opts;
4068 Env* mem_env = NewMemEnv(Env::Default());
4069 config_options.registry->AddLibrary("custom-env", RegisterCustomEnv,
4070 kCustomEnvName);
4071
4072 config_options.env = mem_env;
4073 // First test that we can get the env as expected
4074 ASSERT_OK(GetDBOptionsFromString(config_options, DBOptions(), kCustomEnvProp,
4075 &db_opts));
4076 ASSERT_OK(
4077 GetOptionsFromString(config_options, Options(), kCustomEnvProp, &opts));
4078 ASSERT_NE(config_options.env, db_opts.env);
4079 ASSERT_EQ(opts.env, db_opts.env);
4080 Env* custom_env = db_opts.env;
4081
4082 // Now try a "bad" env" and check that nothing changed
4083 config_options.ignore_unsupported_options = true;
4084 ASSERT_OK(
4085 GetDBOptionsFromString(config_options, db_opts, "env=unknown", &db_opts));
4086 ASSERT_OK(GetOptionsFromString(config_options, opts, "env=unknown", &opts));
4087 ASSERT_EQ(config_options.env, mem_env);
4088 ASSERT_EQ(db_opts.env, custom_env);
4089 ASSERT_EQ(opts.env, db_opts.env);
4090
4091 // Now try a "bad" env" ignoring unknown objects
4092 config_options.ignore_unsupported_options = false;
4093 ASSERT_NOK(
4094 GetDBOptionsFromString(config_options, db_opts, "env=unknown", &db_opts));
4095 ASSERT_EQ(config_options.env, mem_env);
4096 ASSERT_EQ(db_opts.env, custom_env);
4097 ASSERT_EQ(opts.env, db_opts.env);
4098
4099 delete mem_env;
4100 }
4101 #endif // !ROCKSDB_LITE
4102 } // namespace ROCKSDB_NAMESPACE
4103
main(int argc,char ** argv)4104 int main(int argc, char** argv) {
4105 ::testing::InitGoogleTest(&argc, argv);
4106 #ifdef GFLAGS
4107 ParseCommandLineFlags(&argc, &argv, true);
4108 #endif // GFLAGS
4109 return RUN_ALL_TESTS();
4110 }
4111