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