YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/util/options_test.cc
Line
Count
Source (jump to first uncovered line)
1
2
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
3
//  This source code is licensed under the BSD-style license found in the
4
//  LICENSE file in the root directory of this source tree. An additional grant
5
//  of patent rights can be found in the PATENTS file in the same directory.
6
//
7
// The following only applies to changes made to this file as part of YugaByte development.
8
//
9
// Portions Copyright (c) YugaByte, Inc.
10
//
11
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
12
// in compliance with the License.  You may obtain a copy of the License at
13
//
14
// http://www.apache.org/licenses/LICENSE-2.0
15
//
16
// Unless required by applicable law or agreed to in writing, software distributed under the License
17
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
18
// or implied.  See the License for the specific language governing permissions and limitations
19
// under the License.
20
//
21
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
23
// found in the LICENSE file. See the AUTHORS file for names of contributors.
24
25
#ifndef __STDC_FORMAT_MACROS
26
#define __STDC_FORMAT_MACROS
27
#endif
28
29
#include <string>
30
#include <unordered_map>
31
32
#include <boost/preprocessor/stringize.hpp>
33
34
#include <gtest/gtest.h>
35
36
#include "yb/rocksdb/cache.h"
37
#include "yb/rocksdb/convenience.h"
38
#include "yb/rocksdb/memtablerep.h"
39
#include "yb/rocksdb/utilities/leveldb_options.h"
40
#include "yb/rocksdb/util/options_helper.h"
41
#include "yb/rocksdb/util/options_parser.h"
42
#include "yb/rocksdb/util/options_sanity_check.h"
43
#include "yb/rocksdb/util/random.h"
44
#include "yb/rocksdb/env.h"
45
#include "yb/util/test_macros.h"
46
#include "yb/rocksdb/util/testutil.h"
47
#include "yb/util/format.h"
48
49
#ifndef GFLAGS
50
bool FLAGS_enable_print = false;
51
#else
52
#include <gflags/gflags.h>
53
using GFLAGS::ParseCommandLineFlags;
54
DEFINE_bool(enable_print, false, "Print options generated to console.");
55
#endif  // GFLAGS
56
57
namespace rocksdb {
58
59
class StderrLogger : public Logger {
60
 public:
61
  using Logger::Logv;
62
0
  void Logv(const char* format, va_list ap) override {
63
0
    vprintf(format, ap);
64
0
    printf("\n");
65
0
  }
66
};
67
68
Options PrintAndGetOptions(size_t total_write_buffer_limit,
69
                           int read_amplification_threshold,
70
                           int write_amplification_threshold,
71
5
                           uint64_t target_db_size = 68719476736) {
72
5
  StderrLogger logger;
73
74
5
  if (FLAGS_enable_print) {
75
0
    printf("---- total_write_buffer_limit: %" ROCKSDB_PRIszt
76
0
           " "
77
0
           "read_amplification_threshold: %d write_amplification_threshold: %d "
78
0
           "target_db_size %" PRIu64 " ----\n",
79
0
           total_write_buffer_limit, read_amplification_threshold,
80
0
           write_amplification_threshold, target_db_size);
81
0
  }
82
83
5
  Options options =
84
5
      GetOptions(total_write_buffer_limit, read_amplification_threshold,
85
5
                 write_amplification_threshold, target_db_size);
86
5
  if (FLAGS_enable_print) {
87
0
    options.Dump(&logger);
88
0
    printf("-------------------------------------\n\n\n");
89
0
  }
90
5
  return options;
91
5
}
92
93
class OptionsTest : public RocksDBTest {};
94
95
1
TEST_F(OptionsTest, LooseCondition) {
96
1
  Options options;
97
1
  PrintAndGetOptions(static_cast<size_t>(10) * 1024 * 1024 * 1024, 100, 100);
98
99
  // Less mem table memory budget
100
1
  PrintAndGetOptions(32 * 1024 * 1024, 100, 100);
101
102
  // Tight read amplification
103
1
  options = PrintAndGetOptions(128 * 1024 * 1024, 8, 100);
104
1
  ASSERT_EQ(options.compaction_style, kCompactionStyleLevel);
105
106
1
#ifndef ROCKSDB_LITE  // Universal compaction is not supported in ROCKSDB_LITE
107
  // Tight write amplification
108
1
  options = PrintAndGetOptions(128 * 1024 * 1024, 64, 10);
109
1
  ASSERT_EQ(options.compaction_style, kCompactionStyleUniversal);
110
1
#endif  // !ROCKSDB_LITE
111
112
  // Both tight amplifications
113
1
  PrintAndGetOptions(128 * 1024 * 1024, 4, 8);
114
1
}
115
116
#ifndef ROCKSDB_LITE  // GetOptionsFromMap is not supported in ROCKSDB_LITE
117
1
TEST_F(OptionsTest, GetOptionsFromMapTest) {
118
1
  std::unordered_map<std::string, std::string> cf_options_map = {
119
1
      {"write_buffer_size", "1"},
120
1
      {"max_write_buffer_number", "2"},
121
1
      {"min_write_buffer_number_to_merge", "3"},
122
1
      {"max_write_buffer_number_to_maintain", "99"},
123
1
      {"compression", "kSnappyCompression"},
124
1
      {"compression_per_level",
125
1
       "kNoCompression:"
126
1
       "kSnappyCompression:"
127
1
       "kZlibCompression:"
128
1
       "kBZip2Compression:"
129
1
       "kLZ4Compression:"
130
1
       "kLZ4HCCompression:"
131
1
       "kZSTDNotFinalCompression"},
132
1
      {"compression_opts", "4:5:6"},
133
1
      {"num_levels", "7"},
134
1
      {"level0_file_num_compaction_trigger", "8"},
135
1
      {"level0_slowdown_writes_trigger", "9"},
136
1
      {"level0_stop_writes_trigger", "10"},
137
1
      {"target_file_size_base", "12"},
138
1
      {"target_file_size_multiplier", "13"},
139
1
      {"max_bytes_for_level_base", "14"},
140
1
      {"level_compaction_dynamic_level_bytes", "true"},
141
1
      {"max_bytes_for_level_multiplier", "15"},
142
1
      {"max_bytes_for_level_multiplier_additional", "16:17:18"},
143
1
      {"expanded_compaction_factor", "19"},
144
1
      {"source_compaction_factor", "20"},
145
1
      {"max_grandparent_overlap_factor", "21"},
146
1
      {"soft_rate_limit", "1.1"},
147
1
      {"hard_rate_limit", "2.1"},
148
1
      {"hard_pending_compaction_bytes_limit", "211"},
149
1
      {"arena_block_size", "22"},
150
1
      {"disable_auto_compactions", "true"},
151
1
      {"compaction_style", "kCompactionStyleLevel"},
152
1
      {"verify_checksums_in_compaction", "false"},
153
1
      {"compaction_options_fifo", "23"},
154
1
      {"filter_deletes", "0"},
155
1
      {"max_sequential_skip_in_iterations", "24"},
156
1
      {"inplace_update_support", "true"},
157
1
      {"compaction_measure_io_stats", "true"},
158
1
      {"inplace_update_num_locks", "25"},
159
1
      {"memtable_prefix_bloom_bits", "26"},
160
1
      {"memtable_prefix_bloom_probes", "27"},
161
1
      {"memtable_prefix_bloom_huge_page_tlb_size", "28"},
162
1
      {"bloom_locality", "29"},
163
1
      {"max_successive_merges", "30"},
164
1
      {"min_partial_merge_operands", "31"},
165
1
      {"prefix_extractor", "fixed:31"},
166
1
      {"optimize_filters_for_hits", "true"},
167
1
  };
168
169
1
  std::unordered_map<std::string, std::string> db_options_map = {
170
1
      {"create_if_missing", "false"},
171
1
      {"create_missing_column_families", "true"},
172
1
      {"error_if_exists", "false"},
173
1
      {"paranoid_checks", "true"},
174
1
      {"max_open_files", "32"},
175
1
      {"max_total_wal_size", "33"},
176
1
      {"disable_data_sync", "false"},
177
1
      {"use_fsync", "true"},
178
1
      {"db_log_dir", "/db_log_dir"},
179
1
      {"wal_dir", "/wal_dir"},
180
1
      {"delete_obsolete_files_period_micros", "34"},
181
1
      {"max_background_compactions", "35"},
182
1
      {"max_background_flushes", "36"},
183
1
      {"max_log_file_size", "37"},
184
1
      {"log_file_time_to_roll", "38"},
185
1
      {"keep_log_file_num", "39"},
186
1
      {"recycle_log_file_num", "5"},
187
1
      {"max_manifest_file_size", "40"},
188
1
      {"table_cache_numshardbits", "41"},
189
1
      {"WAL_ttl_seconds", "43"},
190
1
      {"WAL_size_limit_MB", "44"},
191
1
      {"manifest_preallocation_size", "45"},
192
1
      {"allow_os_buffer", "false"},
193
1
      {"allow_mmap_reads", "true"},
194
1
      {"allow_mmap_writes", "false"},
195
1
      {"is_fd_close_on_exec", "true"},
196
1
      {"skip_log_error_on_recovery", "false"},
197
1
      {"stats_dump_period_sec", "46"},
198
1
      {"advise_random_on_open", "true"},
199
1
      {"use_adaptive_mutex", "false"},
200
1
      {"new_table_reader_for_compaction_inputs", "true"},
201
1
      {"compaction_readahead_size", "100"},
202
1
      {"random_access_max_buffer_size", "3145728"},
203
1
      {"writable_file_max_buffer_size", "314159"},
204
1
      {"bytes_per_sync", "47"},
205
1
      {"wal_bytes_per_sync", "48"},
206
1
  };
207
208
1
  ColumnFamilyOptions base_cf_opt;
209
1
  ColumnFamilyOptions new_cf_opt;
210
1
  ASSERT_OK(GetColumnFamilyOptionsFromMap(
211
1
            base_cf_opt, cf_options_map, &new_cf_opt));
212
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
213
1
  ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
214
1
  ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
215
1
  ASSERT_EQ(new_cf_opt.max_write_buffer_number_to_maintain, 99);
216
1
  ASSERT_EQ(new_cf_opt.compression, kSnappyCompression);
217
1
  ASSERT_EQ(new_cf_opt.compression_per_level.size(), 7U);
218
1
  ASSERT_EQ(new_cf_opt.compression_per_level[0], kNoCompression);
219
1
  ASSERT_EQ(new_cf_opt.compression_per_level[1], kSnappyCompression);
220
1
  ASSERT_EQ(new_cf_opt.compression_per_level[2], kZlibCompression);
221
1
  ASSERT_EQ(new_cf_opt.compression_per_level[3], kBZip2Compression);
222
1
  ASSERT_EQ(new_cf_opt.compression_per_level[4], kLZ4Compression);
223
1
  ASSERT_EQ(new_cf_opt.compression_per_level[5], kLZ4HCCompression);
224
1
  ASSERT_EQ(new_cf_opt.compression_per_level[6], kZSTDNotFinalCompression);
225
1
  ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
226
1
  ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
227
1
  ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
228
1
  ASSERT_EQ(new_cf_opt.num_levels, 7);
229
1
  ASSERT_EQ(new_cf_opt.level0_file_num_compaction_trigger, 8);
230
1
  ASSERT_EQ(new_cf_opt.level0_slowdown_writes_trigger, 9);
231
1
  ASSERT_EQ(new_cf_opt.level0_stop_writes_trigger, 10);
232
1
  ASSERT_EQ(new_cf_opt.target_file_size_base, static_cast<uint64_t>(12));
233
1
  ASSERT_EQ(new_cf_opt.target_file_size_multiplier, 13);
234
1
  ASSERT_EQ(new_cf_opt.max_bytes_for_level_base, 14U);
235
1
  ASSERT_EQ(new_cf_opt.level_compaction_dynamic_level_bytes, true);
236
1
  ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier, 15);
237
1
  ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional.size(), 3U);
238
1
  ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[0], 16);
239
1
  ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[1], 17);
240
1
  ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[2], 18);
241
1
  ASSERT_EQ(new_cf_opt.expanded_compaction_factor, 19);
242
1
  ASSERT_EQ(new_cf_opt.source_compaction_factor, 20);
243
1
  ASSERT_EQ(new_cf_opt.max_grandparent_overlap_factor, 21);
244
1
  ASSERT_EQ(new_cf_opt.soft_rate_limit, 1.1);
245
1
  ASSERT_EQ(new_cf_opt.hard_pending_compaction_bytes_limit, 211);
246
1
  ASSERT_EQ(new_cf_opt.arena_block_size, 22U);
247
1
  ASSERT_EQ(new_cf_opt.disable_auto_compactions, true);
248
1
  ASSERT_EQ(new_cf_opt.compaction_style, kCompactionStyleLevel);
249
1
  ASSERT_EQ(new_cf_opt.verify_checksums_in_compaction, false);
250
1
  ASSERT_EQ(new_cf_opt.compaction_options_fifo.max_table_files_size,
251
1
            static_cast<uint64_t>(23));
252
1
  ASSERT_EQ(new_cf_opt.filter_deletes, false);
253
1
  ASSERT_EQ(new_cf_opt.max_sequential_skip_in_iterations,
254
1
            static_cast<uint64_t>(24));
255
1
  ASSERT_EQ(new_cf_opt.inplace_update_support, true);
256
1
  ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 25U);
257
1
  ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_bits, 26U);
258
1
  ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_probes, 27U);
259
1
  ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_huge_page_tlb_size, 28U);
260
1
  ASSERT_EQ(new_cf_opt.bloom_locality, 29U);
261
1
  ASSERT_EQ(new_cf_opt.max_successive_merges, 30U);
262
1
  ASSERT_EQ(new_cf_opt.min_partial_merge_operands, 31U);
263
1
  ASSERT_TRUE(new_cf_opt.prefix_extractor != nullptr);
264
1
  ASSERT_EQ(new_cf_opt.optimize_filters_for_hits, true);
265
1
  ASSERT_EQ(std::string(new_cf_opt.prefix_extractor->Name()),
266
1
            "rocksdb.FixedPrefix.31");
267
268
1
  cf_options_map["write_buffer_size"] = "hello";
269
1
  ASSERT_NOK(GetColumnFamilyOptionsFromMap(
270
1
             base_cf_opt, cf_options_map, &new_cf_opt));
271
1
  cf_options_map["write_buffer_size"] = "1";
272
1
  ASSERT_OK(GetColumnFamilyOptionsFromMap(
273
1
            base_cf_opt, cf_options_map, &new_cf_opt));
274
1
  cf_options_map["unknown_option"] = "1";
275
1
  ASSERT_NOK(GetColumnFamilyOptionsFromMap(
276
1
             base_cf_opt, cf_options_map, &new_cf_opt));
277
278
1
  DBOptions base_db_opt;
279
1
  DBOptions new_db_opt;
280
1
  ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
281
1
  ASSERT_EQ(new_db_opt.create_if_missing, false);
282
1
  ASSERT_EQ(new_db_opt.create_missing_column_families, true);
283
1
  ASSERT_EQ(new_db_opt.error_if_exists, false);
284
1
  ASSERT_EQ(new_db_opt.paranoid_checks, true);
285
1
  ASSERT_EQ(new_db_opt.max_open_files, 32);
286
1
  ASSERT_EQ(new_db_opt.max_total_wal_size, static_cast<uint64_t>(33));
287
1
  ASSERT_EQ(new_db_opt.disableDataSync, false);
288
1
  ASSERT_EQ(new_db_opt.use_fsync, true);
289
1
  ASSERT_EQ(new_db_opt.db_log_dir, "/db_log_dir");
290
1
  ASSERT_EQ(new_db_opt.wal_dir, "/wal_dir");
291
1
  ASSERT_EQ(new_db_opt.delete_obsolete_files_period_micros,
292
1
            static_cast<uint64_t>(34));
293
1
  ASSERT_EQ(new_db_opt.max_background_compactions, 35);
294
1
  ASSERT_EQ(new_db_opt.max_background_flushes, 36);
295
1
  ASSERT_EQ(new_db_opt.max_log_file_size, 37U);
296
1
  ASSERT_EQ(new_db_opt.log_file_time_to_roll, 38U);
297
1
  ASSERT_EQ(new_db_opt.keep_log_file_num, 39U);
298
1
  ASSERT_EQ(new_db_opt.recycle_log_file_num, 5U);
299
1
  ASSERT_EQ(new_db_opt.max_manifest_file_size, static_cast<uint64_t>(40));
300
1
  ASSERT_EQ(new_db_opt.table_cache_numshardbits, 41);
301
1
  ASSERT_EQ(new_db_opt.WAL_ttl_seconds, static_cast<uint64_t>(43));
302
1
  ASSERT_EQ(new_db_opt.WAL_size_limit_MB, static_cast<uint64_t>(44));
303
1
  ASSERT_EQ(new_db_opt.manifest_preallocation_size, 45U);
304
1
  ASSERT_EQ(new_db_opt.allow_os_buffer, false);
305
1
  ASSERT_EQ(new_db_opt.allow_mmap_reads, true);
306
1
  ASSERT_EQ(new_db_opt.allow_mmap_writes, false);
307
1
  ASSERT_EQ(new_db_opt.is_fd_close_on_exec, true);
308
1
  ASSERT_EQ(new_db_opt.skip_log_error_on_recovery, false);
309
1
  ASSERT_EQ(new_db_opt.stats_dump_period_sec, 46U);
310
1
  ASSERT_EQ(new_db_opt.advise_random_on_open, true);
311
1
  ASSERT_EQ(new_db_opt.use_adaptive_mutex, false);
312
1
  ASSERT_EQ(new_db_opt.new_table_reader_for_compaction_inputs, true);
313
1
  ASSERT_EQ(new_db_opt.compaction_readahead_size, 100);
314
1
  ASSERT_EQ(new_db_opt.random_access_max_buffer_size, 3145728);
315
1
  ASSERT_EQ(new_db_opt.writable_file_max_buffer_size, 314159);
316
1
  ASSERT_EQ(new_db_opt.bytes_per_sync, static_cast<uint64_t>(47));
317
1
  ASSERT_EQ(new_db_opt.wal_bytes_per_sync, static_cast<uint64_t>(48));
318
1
}
319
#endif  // !ROCKSDB_LITE
320
321
#ifndef ROCKSDB_LITE  // GetColumnFamilyOptionsFromString is not supported in
322
                      // ROCKSDB_LITE
323
1
TEST_F(OptionsTest, GetColumnFamilyOptionsFromStringTest) {
324
1
  ColumnFamilyOptions base_cf_opt;
325
1
  ColumnFamilyOptions new_cf_opt;
326
1
  base_cf_opt.table_factory.reset();
327
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt, "", &new_cf_opt));
328
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
329
1
            "write_buffer_size=5", &new_cf_opt));
330
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
331
1
  ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
332
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
333
1
            "write_buffer_size=6;", &new_cf_opt));
334
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
335
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
336
1
            "  write_buffer_size =  7  ", &new_cf_opt));
337
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
338
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
339
1
            "  write_buffer_size =  8 ; ", &new_cf_opt));
340
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
341
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
342
1
            "write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
343
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
344
1
  ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
345
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
346
1
            "write_buffer_size=11; max_write_buffer_number  =  12 ;",
347
1
            &new_cf_opt));
348
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
349
1
  ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
350
  // Wrong name "max_write_buffer_number_"
351
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
352
1
             "write_buffer_size=13;max_write_buffer_number_=14;",
353
1
              &new_cf_opt));
354
  // Wrong key/value pair
355
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
356
1
             "write_buffer_size=13;max_write_buffer_number;", &new_cf_opt));
357
  // Error Paring value
358
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
359
1
             "write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt));
360
  // Missing option name
361
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
362
1
             "write_buffer_size=13; =100;", &new_cf_opt));
363
364
1
  const int64_t kilo = 1024UL;
365
1
  const int64_t mega = 1024 * kilo;
366
1
  const int64_t giga = 1024 * mega;
367
1
  const int64_t tera = 1024 * giga;
368
369
  // Units (k)
370
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
371
1
            "memtable_prefix_bloom_bits=14k;max_write_buffer_number=-15K",
372
1
            &new_cf_opt));
373
1
  ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_bits, 14UL * kilo);
374
1
  ASSERT_EQ(new_cf_opt.max_write_buffer_number, -15 * kilo);
375
  // Units (m)
376
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
377
1
            "max_write_buffer_number=16m;inplace_update_num_locks=17M",
378
1
            &new_cf_opt));
379
1
  ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
380
1
  ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17 * mega);
381
  // Units (g)
382
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(
383
1
      base_cf_opt,
384
1
      "write_buffer_size=18g;prefix_extractor=capped:8;"
385
1
      "arena_block_size=19G",
386
1
      &new_cf_opt));
387
388
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 18 * giga);
389
1
  ASSERT_EQ(new_cf_opt.arena_block_size, 19 * giga);
390
1
  ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
391
1
  std::string prefix_name(new_cf_opt.prefix_extractor->Name());
392
1
  ASSERT_EQ(prefix_name, "rocksdb.CappedPrefix.8");
393
394
  // Units (t)
395
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
396
1
            "write_buffer_size=20t;arena_block_size=21T", &new_cf_opt));
397
1
  ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
398
1
  ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
399
400
  // Nested block based table options
401
  // Emtpy
402
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
403
1
            "write_buffer_size=10;max_write_buffer_number=16;"
404
1
            "block_based_table_factory={};arena_block_size=1024",
405
1
            &new_cf_opt));
406
1
  ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
407
  // Non-empty
408
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
409
1
            "write_buffer_size=10;max_write_buffer_number=16;"
410
1
            "block_based_table_factory={block_cache=1M;block_size=4;};"
411
1
            "arena_block_size=1024",
412
1
            &new_cf_opt));
413
1
  ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
414
  // Last one
415
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
416
1
            "write_buffer_size=10;max_write_buffer_number=16;"
417
1
            "block_based_table_factory={block_cache=1M;block_size=4;}",
418
1
            &new_cf_opt));
419
1
  ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
420
  // Mismatch curly braces
421
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
422
1
             "write_buffer_size=10;max_write_buffer_number=16;"
423
1
             "block_based_table_factory={{{block_size=4;};"
424
1
             "arena_block_size=1024",
425
1
             &new_cf_opt));
426
  // Unexpected chars after closing curly brace
427
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
428
1
             "write_buffer_size=10;max_write_buffer_number=16;"
429
1
             "block_based_table_factory={block_size=4;}};"
430
1
             "arena_block_size=1024",
431
1
             &new_cf_opt));
432
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
433
1
             "write_buffer_size=10;max_write_buffer_number=16;"
434
1
             "block_based_table_factory={block_size=4;}xdfa;"
435
1
             "arena_block_size=1024",
436
1
             &new_cf_opt));
437
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
438
1
             "write_buffer_size=10;max_write_buffer_number=16;"
439
1
             "block_based_table_factory={block_size=4;}xdfa",
440
1
             &new_cf_opt));
441
  // Invalid block based table option
442
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
443
1
             "write_buffer_size=10;max_write_buffer_number=16;"
444
1
             "block_based_table_factory={xx_block_size=4;}",
445
1
             &new_cf_opt));
446
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
447
1
           "optimize_filters_for_hits=true",
448
1
           &new_cf_opt));
449
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
450
1
            "optimize_filters_for_hits=false",
451
1
            &new_cf_opt));
452
1
  ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
453
1
              "optimize_filters_for_hits=junk",
454
1
              &new_cf_opt));
455
456
  // Nested plain table options
457
  // Emtpy
458
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
459
1
            "write_buffer_size=10;max_write_buffer_number=16;"
460
1
            "plain_table_factory={};arena_block_size=1024",
461
1
            &new_cf_opt));
462
1
  ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
463
1
  ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
464
  // Non-empty
465
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
466
1
            "write_buffer_size=10;max_write_buffer_number=16;"
467
1
            "plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
468
1
            "arena_block_size=1024",
469
1
            &new_cf_opt));
470
1
  ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
471
1
  ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
472
473
  // memtable factory
474
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
475
1
            "write_buffer_size=10;max_write_buffer_number=16;"
476
1
            "memtable=skip_list:10;arena_block_size=1024",
477
1
            &new_cf_opt));
478
1
  ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
479
1
  ASSERT_EQ(std::string(new_cf_opt.memtable_factory->Name()), "SkipListFactory");
480
1
}
481
#endif  // !ROCKSDB_LITE
482
483
#ifndef ROCKSDB_LITE  // GetBlockBasedTableOptionsFromString is not supported
484
1
TEST_F(OptionsTest, GetBlockBasedTableOptionsFromString) {
485
1
  BlockBasedTableOptions table_opt;
486
1
  BlockBasedTableOptions new_opt;
487
  // make sure default values are overwritten by something else
488
1
  ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
489
1
            "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
490
1
            "checksum=kxxHash;hash_index_allow_collision=1;no_block_cache=1;"
491
1
            "block_cache=1M;block_cache_compressed=1k;block_size=1024;filter_block_size=4096;"
492
1
            "block_size_deviation=8;block_restart_interval=4;index_block_size=16384;"
493
1
            "min_keys_per_index_block=16;filter_policy=bloomfilter:4:true;whole_key_filtering=1;"
494
1
            "skip_table_builder_flush=1",
495
1
            &new_opt));
496
1
  ASSERT_TRUE(new_opt.cache_index_and_filter_blocks);
497
1
  ASSERT_EQ(new_opt.index_type, IndexType::kHashSearch);
498
1
  ASSERT_EQ(new_opt.checksum, ChecksumType::kxxHash);
499
1
  ASSERT_TRUE(new_opt.hash_index_allow_collision);
500
1
  ASSERT_TRUE(new_opt.no_block_cache);
501
1
  ASSERT_TRUE(new_opt.block_cache != nullptr);
502
1
  ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
503
1
  ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
504
1
  ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL);
505
1
  ASSERT_EQ(new_opt.block_size, 1024UL);
506
1
  ASSERT_EQ(new_opt.filter_block_size, 4096UL);
507
1
  ASSERT_EQ(new_opt.block_size_deviation, 8);
508
1
  ASSERT_EQ(new_opt.block_restart_interval, 4);
509
1
  ASSERT_EQ(new_opt.index_block_size, 16384UL);
510
1
  ASSERT_EQ(new_opt.min_keys_per_index_block, 16);
511
1
  ASSERT_TRUE(new_opt.filter_policy != nullptr);
512
1
  ASSERT_TRUE(new_opt.skip_table_builder_flush);
513
514
  // unknown option
515
1
  ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
516
1
             "cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
517
1
             "bad_option=1",
518
1
             &new_opt));
519
520
  // unrecognized index type
521
1
  ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
522
1
             "cache_index_and_filter_blocks=1;index_type=kBinarySearchXX",
523
1
             &new_opt));
524
525
  // unrecognized checksum type
526
1
  ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
527
1
             "cache_index_and_filter_blocks=1;checksum=kxxHashXX",
528
1
             &new_opt));
529
530
  // unrecognized filter policy name
531
1
  ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
532
1
             "cache_index_and_filter_blocks=1;"
533
1
             "filter_policy=bloomfilterxx:4:true",
534
1
             &new_opt));
535
  // unrecognized filter policy config
536
1
  ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
537
1
             "cache_index_and_filter_blocks=1;"
538
1
             "filter_policy=bloomfilter:4",
539
1
             &new_opt));
540
1
}
541
#endif  // !ROCKSDB_LITE
542
543
544
#ifndef ROCKSDB_LITE  // GetPlainTableOptionsFromString is not supported
545
1
TEST_F(OptionsTest, GetPlainTableOptionsFromString) {
546
1
  PlainTableOptions table_opt;
547
1
  PlainTableOptions new_opt;
548
  // make sure default values are overwritten by something else
549
1
  ASSERT_OK(GetPlainTableOptionsFromString(table_opt,
550
1
            "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
551
1
            "index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
552
1
            "full_scan_mode=true;store_index_in_file=true",
553
1
            &new_opt));
554
1
  ASSERT_EQ(new_opt.user_key_len, 66);
555
1
  ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
556
1
  ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
557
1
  ASSERT_EQ(new_opt.index_sparseness, 8);
558
1
  ASSERT_EQ(new_opt.huge_page_tlb_size, 4);
559
1
  ASSERT_EQ(new_opt.encoding_type, EncodingType::kPrefix);
560
1
  ASSERT_TRUE(new_opt.full_scan_mode);
561
1
  ASSERT_TRUE(new_opt.store_index_in_file);
562
563
  // unknown option
564
1
  ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
565
1
             "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
566
1
             "bad_option=1",
567
1
             &new_opt));
568
569
  // unrecognized EncodingType
570
1
  ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
571
1
             "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
572
1
             "encoding_type=kPrefixXX",
573
1
             &new_opt));
574
1
}
575
#endif  // !ROCKSDB_LITE
576
577
#ifndef ROCKSDB_LITE  // GetMemTableRepFactoryFromString is not supported
578
1
TEST_F(OptionsTest, GetMemTableRepFactoryFromString) {
579
1
  std::unique_ptr<MemTableRepFactory> new_mem_factory = nullptr;
580
581
1
  ASSERT_OK(GetMemTableRepFactoryFromString("skip_list", &new_mem_factory));
582
1
  ASSERT_OK(GetMemTableRepFactoryFromString("skip_list:16", &new_mem_factory));
583
1
  ASSERT_EQ(std::string(new_mem_factory->Name()), "SkipListFactory");
584
1
  ASSERT_NOK(GetMemTableRepFactoryFromString("skip_list:16:invalid_opt",
585
1
                                             &new_mem_factory));
586
587
1
  ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash", &new_mem_factory));
588
1
  ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash:1000",
589
1
                                            &new_mem_factory));
590
1
  ASSERT_EQ(std::string(new_mem_factory->Name()), "HashSkipListRepFactory");
591
1
  ASSERT_NOK(GetMemTableRepFactoryFromString("prefix_hash:1000:invalid_opt",
592
1
                                             &new_mem_factory));
593
594
1
  ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist",
595
1
                                            &new_mem_factory));
596
1
  ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist:1000",
597
1
                                            &new_mem_factory));
598
1
  ASSERT_EQ(std::string(new_mem_factory->Name()), "HashLinkListRepFactory");
599
1
  ASSERT_NOK(GetMemTableRepFactoryFromString("hash_linkedlist:1000:invalid_opt",
600
1
                                             &new_mem_factory));
601
602
1
  ASSERT_OK(GetMemTableRepFactoryFromString("vector", &new_mem_factory));
603
1
  ASSERT_OK(GetMemTableRepFactoryFromString("vector:1024", &new_mem_factory));
604
1
  ASSERT_EQ(std::string(new_mem_factory->Name()), "VectorRepFactory");
605
1
  ASSERT_NOK(GetMemTableRepFactoryFromString("vector:1024:invalid_opt",
606
1
                                             &new_mem_factory));
607
608
1
  ASSERT_NOK(GetMemTableRepFactoryFromString("bad_factory", &new_mem_factory));
609
1
}
610
#endif  // !ROCKSDB_LITE
611
612
#ifndef ROCKSDB_LITE  // GetOptionsFromString is not supported in RocksDB Lite
613
1
TEST_F(OptionsTest, GetOptionsFromStringTest) {
614
1
  Options base_options, new_options;
615
1
  base_options.write_buffer_size = 20;
616
1
  base_options.min_write_buffer_number_to_merge = 15;
617
1
  BlockBasedTableOptions block_based_table_options;
618
1
  block_based_table_options.cache_index_and_filter_blocks = true;
619
1
  base_options.table_factory.reset(
620
1
      NewBlockBasedTableFactory(block_based_table_options));
621
1
  ASSERT_OK(GetOptionsFromString(
622
1
      base_options,
623
1
      "write_buffer_size=10;max_write_buffer_number=16;"
624
1
      "block_based_table_factory={block_cache=1M;block_size=4;};"
625
1
      "create_if_missing=true;max_open_files=1;rate_limiter_bytes_per_sec=1024",
626
1
      &new_options));
627
628
1
  ASSERT_EQ(new_options.write_buffer_size, 10U);
629
1
  ASSERT_EQ(new_options.max_write_buffer_number, 16);
630
1
  BlockBasedTableOptions new_block_based_table_options =
631
1
      dynamic_cast<BlockBasedTableFactory*>(new_options.table_factory.get())
632
1
          ->table_options();
633
1
  ASSERT_EQ(new_block_based_table_options.block_cache->GetCapacity(), 1U << 20);
634
1
  ASSERT_EQ(new_block_based_table_options.block_size, 4U);
635
  // don't overwrite block based table options
636
1
  ASSERT_TRUE(new_block_based_table_options.cache_index_and_filter_blocks);
637
638
1
  ASSERT_EQ(new_options.create_if_missing, true);
639
1
  ASSERT_EQ(new_options.max_open_files, 1);
640
1
  ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
641
1
}
642
643
1
TEST_F(OptionsTest, DBOptionsSerialization) {
644
1
  Options base_options, new_options;
645
1
  Random rnd(301);
646
647
  // Phase 1: Make big change in base_options
648
1
  test::RandomInitDBOptions(&base_options, &rnd);
649
650
  // Phase 2: obtain a string from base_option
651
1
  std::string base_options_file_content;
652
1
  ASSERT_OK(GetStringFromDBOptions(&base_options_file_content, base_options));
653
654
  // Phase 3: Set new_options from the derived string and expect
655
  //          new_options == base_options
656
1
  ASSERT_OK(GetDBOptionsFromString(DBOptions(), base_options_file_content,
657
1
                                   &new_options));
658
1
  ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(base_options, new_options));
659
1
}
660
661
1
TEST_F(OptionsTest, ColumnFamilyOptionsSerialization) {
662
1
  ColumnFamilyOptions base_opt, new_opt;
663
1
  Random rnd(302);
664
  // Phase 1: randomly assign base_opt
665
  // custom type options
666
1
  test::RandomInitCFOptions(&base_opt, &rnd);
667
668
  // Phase 2: obtain a string from base_opt
669
1
  std::string base_options_file_content;
670
1
  ASSERT_OK(
671
1
      GetStringFromColumnFamilyOptions(&base_options_file_content, base_opt));
672
673
  // Phase 3: Set new_opt from the derived string and expect
674
  //          new_opt == base_opt
675
1
  ASSERT_OK(GetColumnFamilyOptionsFromString(
676
1
      ColumnFamilyOptions(), base_options_file_content, &new_opt));
677
1
  ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_opt, new_opt));
678
1
  if (base_opt.compaction_filter) {
679
1
    delete base_opt.compaction_filter;
680
1
  }
681
1
}
682
683
#endif  // !ROCKSDB_LITE
684
685
Status StringToMap(
686
    const std::string& opts_str,
687
    std::unordered_map<std::string, std::string>* opts_map);
688
689
#ifndef ROCKSDB_LITE  // StringToMap is not supported in ROCKSDB_LITE
690
1
TEST_F(OptionsTest, StringToMapTest) {
691
1
  std::unordered_map<std::string, std::string> opts_map;
692
  // Regular options
693
1
  ASSERT_OK(StringToMap("k1=v1;k2=v2;k3=v3", &opts_map));
694
1
  ASSERT_EQ(opts_map["k1"], "v1");
695
1
  ASSERT_EQ(opts_map["k2"], "v2");
696
1
  ASSERT_EQ(opts_map["k3"], "v3");
697
  // Value with '='
698
1
  opts_map.clear();
699
1
  ASSERT_OK(StringToMap("k1==v1;k2=v2=;", &opts_map));
700
1
  ASSERT_EQ(opts_map["k1"], "=v1");
701
1
  ASSERT_EQ(opts_map["k2"], "v2=");
702
  // Overwrriten option
703
1
  opts_map.clear();
704
1
  ASSERT_OK(StringToMap("k1=v1;k1=v2;k3=v3", &opts_map));
705
1
  ASSERT_EQ(opts_map["k1"], "v2");
706
1
  ASSERT_EQ(opts_map["k3"], "v3");
707
  // Empty value
708
1
  opts_map.clear();
709
1
  ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4=", &opts_map));
710
1
  ASSERT_EQ(opts_map["k1"], "v1");
711
1
  ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
712
1
  ASSERT_EQ(opts_map["k2"], "");
713
1
  ASSERT_EQ(opts_map["k3"], "v3");
714
1
  ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
715
1
  ASSERT_EQ(opts_map["k4"], "");
716
1
  opts_map.clear();
717
1
  ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4=   ", &opts_map));
718
1
  ASSERT_EQ(opts_map["k1"], "v1");
719
1
  ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
720
1
  ASSERT_EQ(opts_map["k2"], "");
721
1
  ASSERT_EQ(opts_map["k3"], "v3");
722
1
  ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
723
1
  ASSERT_EQ(opts_map["k4"], "");
724
1
  opts_map.clear();
725
1
  ASSERT_OK(StringToMap("k1=v1;k2=;k3=", &opts_map));
726
1
  ASSERT_EQ(opts_map["k1"], "v1");
727
1
  ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
728
1
  ASSERT_EQ(opts_map["k2"], "");
729
1
  ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
730
1
  ASSERT_EQ(opts_map["k3"], "");
731
1
  opts_map.clear();
732
1
  ASSERT_OK(StringToMap("k1=v1;k2=;k3=;", &opts_map));
733
1
  ASSERT_EQ(opts_map["k1"], "v1");
734
1
  ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
735
1
  ASSERT_EQ(opts_map["k2"], "");
736
1
  ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
737
1
  ASSERT_EQ(opts_map["k3"], "");
738
  // Regular nested options
739
1
  opts_map.clear();
740
1
  ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2=nv2};k3=v3", &opts_map));
741
1
  ASSERT_EQ(opts_map["k1"], "v1");
742
1
  ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2=nv2");
743
1
  ASSERT_EQ(opts_map["k3"], "v3");
744
  // Multi-level nested options
745
1
  opts_map.clear();
746
1
  ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2={nnk1=nnk2}};"
747
1
                        "k3={nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}};k4=v4",
748
1
                        &opts_map));
749
1
  ASSERT_EQ(opts_map["k1"], "v1");
750
1
  ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2={nnk1=nnk2}");
751
1
  ASSERT_EQ(opts_map["k3"], "nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}");
752
1
  ASSERT_EQ(opts_map["k4"], "v4");
753
  // Garbage inside curly braces
754
1
  opts_map.clear();
755
1
  ASSERT_OK(StringToMap("k1=v1;k2={dfad=};k3={=};k4=v4",
756
1
                        &opts_map));
757
1
  ASSERT_EQ(opts_map["k1"], "v1");
758
1
  ASSERT_EQ(opts_map["k2"], "dfad=");
759
1
  ASSERT_EQ(opts_map["k3"], "=");
760
1
  ASSERT_EQ(opts_map["k4"], "v4");
761
  // Empty nested options
762
1
  opts_map.clear();
763
1
  ASSERT_OK(StringToMap("k1=v1;k2={};", &opts_map));
764
1
  ASSERT_EQ(opts_map["k1"], "v1");
765
1
  ASSERT_EQ(opts_map["k2"], "");
766
1
  opts_map.clear();
767
1
  ASSERT_OK(StringToMap("k1=v1;k2={{{{}}}{}{}};", &opts_map));
768
1
  ASSERT_EQ(opts_map["k1"], "v1");
769
1
  ASSERT_EQ(opts_map["k2"], "{{{}}}{}{}");
770
  // With random spaces
771
1
  opts_map.clear();
772
1
  ASSERT_OK(StringToMap("  k1 =  v1 ; k2= {nk1=nv1; nk2={nnk1=nnk2}}  ; "
773
1
                        "k3={  {   } }; k4= v4  ",
774
1
                        &opts_map));
775
1
  ASSERT_EQ(opts_map["k1"], "v1");
776
1
  ASSERT_EQ(opts_map["k2"], "nk1=nv1; nk2={nnk1=nnk2}");
777
1
  ASSERT_EQ(opts_map["k3"], "{   }");
778
1
  ASSERT_EQ(opts_map["k4"], "v4");
779
780
  // Empty key
781
1
  ASSERT_NOK(StringToMap("k1=v1;k2=v2;=", &opts_map));
782
1
  ASSERT_NOK(StringToMap("=v1;k2=v2", &opts_map));
783
1
  ASSERT_NOK(StringToMap("k1=v1;k2v2;", &opts_map));
784
1
  ASSERT_NOK(StringToMap("k1=v1;k2=v2;fadfa", &opts_map));
785
1
  ASSERT_NOK(StringToMap("k1=v1;k2=v2;;", &opts_map));
786
  // Mismatch curly braces
787
1
  ASSERT_NOK(StringToMap("k1=v1;k2={;k3=v3", &opts_map));
788
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{};k3=v3", &opts_map));
789
1
  ASSERT_NOK(StringToMap("k1=v1;k2={}};k3=v3", &opts_map));
790
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{}{}}};k3=v3", &opts_map));
791
  // However this is valid!
792
1
  opts_map.clear();
793
1
  ASSERT_OK(StringToMap("k1=v1;k2=};k3=v3", &opts_map));
794
1
  ASSERT_EQ(opts_map["k1"], "v1");
795
1
  ASSERT_EQ(opts_map["k2"], "}");
796
1
  ASSERT_EQ(opts_map["k3"], "v3");
797
798
  // Invalid chars after closing curly brace
799
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{}}{};k3=v3", &opts_map));
800
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{}}cfda;k3=v3", &opts_map));
801
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{}}  cfda;k3=v3", &opts_map));
802
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{}}  cfda", &opts_map));
803
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{}}{}", &opts_map));
804
1
  ASSERT_NOK(StringToMap("k1=v1;k2={{dfdl}adfa}{}", &opts_map));
805
1
}
806
#endif  // ROCKSDB_LITE
807
808
#ifndef ROCKSDB_LITE  // StringToMap is not supported in ROCKSDB_LITE
809
1
TEST_F(OptionsTest, StringToMapRandomTest) {
810
1
  std::unordered_map<std::string, std::string> opts_map;
811
  // Make sure segfault is not hit by semi-random strings
812
813
1
  std::vector<std::string> bases = {
814
1
      "a={aa={};tt={xxx={}}};c=defff",
815
1
      "a={aa={};tt={xxx={}}};c=defff;d={{}yxx{}3{xx}}",
816
1
      "abc={{}{}{}{{{}}}{{}{}{}{}{}{}{}"};
817
818
3
  for (std::string base : bases) {
819
303
    for (int rand_seed = 301; rand_seed < 401; rand_seed++) {
820
300
      Random rnd(rand_seed);
821
3.30k
      for (int attempt = 0; attempt < 10; attempt++) {
822
3.00k
        std::string str = base;
823
        // Replace random position to space
824
3.00k
        size_t pos = static_cast<size_t>(
825
3.00k
            rnd.Uniform(static_cast<int>(base.size())));
826
3.00k
        str[pos] = ' ';
827
3.00k
        Status s = StringToMap(str, &opts_map);
828
3.00k
        ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
829
3.00k
        opts_map.clear();
830
3.00k
      }
831
300
    }
832
3
  }
833
834
  // Random Construct a string
835
1
  std::vector<char> chars = {'{', '}', ' ', '=', ';', 'c'};
836
1.00k
  for (int rand_seed = 301; rand_seed < 1301; rand_seed++) {
837
1.00k
    Random rnd(rand_seed);
838
1.00k
    int len = rnd.Uniform(30);
839
1.00k
    std::string str = "";
840
15.5k
    for (int attempt = 0; attempt < len; attempt++) {
841
      // Add a random character
842
14.5k
      size_t pos = static_cast<size_t>(
843
14.5k
          rnd.Uniform(static_cast<int>(chars.size())));
844
14.5k
      str.append(1, chars[pos]);
845
14.5k
    }
846
1.00k
    Status s = StringToMap(str, &opts_map);
847
1.00k
    ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
848
1.00k
    s = StringToMap("name=" + str, &opts_map);
849
1.00k
    ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
850
1.00k
    opts_map.clear();
851
1.00k
  }
852
1
}
853
#endif  // !ROCKSDB_LITE
854
855
1
TEST_F(OptionsTest, ConvertOptionsTest) {
856
1
  LevelDBOptions leveldb_opt;
857
1
  Options converted_opt = ConvertOptions(leveldb_opt);
858
859
1
  ASSERT_EQ(converted_opt.create_if_missing, leveldb_opt.create_if_missing);
860
1
  ASSERT_EQ(converted_opt.error_if_exists, leveldb_opt.error_if_exists);
861
1
  ASSERT_EQ(converted_opt.paranoid_checks, leveldb_opt.paranoid_checks);
862
1
  ASSERT_EQ(converted_opt.env, leveldb_opt.env);
863
1
  ASSERT_EQ(converted_opt.info_log.get(), leveldb_opt.info_log);
864
1
  ASSERT_EQ(converted_opt.write_buffer_size, leveldb_opt.write_buffer_size);
865
1
  ASSERT_EQ(converted_opt.max_open_files, leveldb_opt.max_open_files);
866
1
  ASSERT_EQ(converted_opt.compression, leveldb_opt.compression);
867
868
1
  std::shared_ptr<BlockBasedTableFactory> table_factory =
869
1
      std::dynamic_pointer_cast<BlockBasedTableFactory>(
870
1
          converted_opt.table_factory);
871
872
1
  ASSERT_TRUE(table_factory.get() != nullptr);
873
874
1
  const BlockBasedTableOptions table_opt = table_factory->table_options();
875
876
1
  ASSERT_EQ(table_opt.block_cache->GetCapacity(), 8UL << 20);
877
1
  ASSERT_EQ(table_opt.block_size, leveldb_opt.block_size);
878
1
  ASSERT_EQ(table_opt.block_restart_interval,
879
1
            leveldb_opt.block_restart_interval);
880
1
  ASSERT_EQ(table_opt.filter_policy.get(), leveldb_opt.filter_policy);
881
1
}
882
883
#ifndef ROCKSDB_LITE
884
class OptionsParserTest : public RocksDBTest {
885
 public:
886
12
  OptionsParserTest() { env_.reset(new test::StringEnv(Env::Default())); }
887
888
 protected:
889
  std::unique_ptr<test::StringEnv> env_;
890
};
891
892
1
TEST_F(OptionsParserTest, Comment) {
893
1
  DBOptions db_opt;
894
1
  db_opt.max_open_files = 12345;
895
1
  db_opt.max_background_flushes = 301;
896
1
  db_opt.max_total_wal_size = 1024;
897
1
  ColumnFamilyOptions cf_opt;
898
899
1
  std::string options_file_content =
900
1
      "# This is a testing option string.\n"
901
1
      "# Currently we only support \"#\" styled comment.\n"
902
1
      "\n"
903
1
      "[Version]\n"
904
1
      "  rocksdb_version=3.14.0\n"
905
1
      "  options_file_version=1\n"
906
1
      "[ DBOptions ]\n"
907
1
      "  # note that we don't support space around \"=\"\n"
908
1
      "  max_open_files=12345;\n"
909
1
      "  max_background_flushes=301  # comment after a statement is fine\n"
910
1
      "  # max_background_flushes=1000  # this line would be ignored\n"
911
1
      "  # max_background_compactions=2000 # so does this one\n"
912
1
      "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
913
1
      "[CFOptions   \"default\"]  # column family must be specified\n"
914
1
      "                     # in the correct order\n"
915
1
      "  # if a section is blank, we will use the default\n";
916
917
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
918
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
919
1
  RocksDBOptionsParser parser;
920
1
  ASSERT_OK(parser.Parse(kTestFileName, env_.get()));
921
922
1
  ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(*parser.db_opt(), db_opt));
923
1
  ASSERT_EQ(parser.NumColumnFamilies(), 1U);
924
1
  ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
925
1
      *parser.GetCFOptions("default"), cf_opt));
926
1
}
927
928
1
TEST_F(OptionsParserTest, ExtraSpace) {
929
1
  std::string options_file_content =
930
1
      "# This is a testing option string.\n"
931
1
      "# Currently we only support \"#\" styled comment.\n"
932
1
      "\n"
933
1
      "[      Version   ]\n"
934
1
      "  rocksdb_version     = 3.14.0      \n"
935
1
      "  options_file_version=1   # some comment\n"
936
1
      "[DBOptions  ]  # some comment\n"
937
1
      "max_open_files=12345   \n"
938
1
      "    max_background_flushes   =    301   \n"
939
1
      " max_total_wal_size     =   1024  # keep_log_file_num=1000\n"
940
1
      "        [CFOptions      \"default\"     ]\n"
941
1
      "  # if a section is blank, we will use the default\n";
942
943
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
944
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
945
1
  RocksDBOptionsParser parser;
946
1
  ASSERT_OK(parser.Parse(kTestFileName, env_.get()));
947
1
}
948
949
1
TEST_F(OptionsParserTest, MissingDBOptions) {
950
1
  std::string options_file_content =
951
1
      "# This is a testing option string.\n"
952
1
      "# Currently we only support \"#\" styled comment.\n"
953
1
      "\n"
954
1
      "[Version]\n"
955
1
      "  rocksdb_version=3.14.0\n"
956
1
      "  options_file_version=1\n"
957
1
      "[CFOptions \"default\"]\n"
958
1
      "  # if a section is blank, we will use the default\n";
959
960
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
961
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
962
1
  RocksDBOptionsParser parser;
963
1
  ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
964
1
}
965
966
1
TEST_F(OptionsParserTest, DoubleDBOptions) {
967
1
  DBOptions db_opt;
968
1
  db_opt.max_open_files = 12345;
969
1
  db_opt.max_background_flushes = 301;
970
1
  db_opt.max_total_wal_size = 1024;
971
1
  ColumnFamilyOptions cf_opt;
972
973
1
  std::string options_file_content =
974
1
      "# This is a testing option string.\n"
975
1
      "# Currently we only support \"#\" styled comment.\n"
976
1
      "\n"
977
1
      "[Version]\n"
978
1
      "  rocksdb_version=3.14.0\n"
979
1
      "  options_file_version=1\n"
980
1
      "[DBOptions]\n"
981
1
      "  max_open_files=12345\n"
982
1
      "  max_background_flushes=301\n"
983
1
      "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
984
1
      "[DBOptions]\n"
985
1
      "[CFOptions \"default\"]\n"
986
1
      "  # if a section is blank, we will use the default\n";
987
988
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
989
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
990
1
  RocksDBOptionsParser parser;
991
1
  ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
992
1
}
993
994
1
TEST_F(OptionsParserTest, NoDefaultCFOptions) {
995
1
  DBOptions db_opt;
996
1
  db_opt.max_open_files = 12345;
997
1
  db_opt.max_background_flushes = 301;
998
1
  db_opt.max_total_wal_size = 1024;
999
1
  ColumnFamilyOptions cf_opt;
1000
1001
1
  std::string options_file_content =
1002
1
      "# This is a testing option string.\n"
1003
1
      "# Currently we only support \"#\" styled comment.\n"
1004
1
      "\n"
1005
1
      "[Version]\n"
1006
1
      "  rocksdb_version=3.14.0\n"
1007
1
      "  options_file_version=1\n"
1008
1
      "[DBOptions]\n"
1009
1
      "  max_open_files=12345\n"
1010
1
      "  max_background_flushes=301\n"
1011
1
      "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
1012
1
      "[CFOptions \"something_else\"]\n"
1013
1
      "  # if a section is blank, we will use the default\n";
1014
1015
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
1016
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
1017
1
  RocksDBOptionsParser parser;
1018
1
  ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
1019
1
}
1020
1021
1
TEST_F(OptionsParserTest, DefaultCFOptionsMustBeTheFirst) {
1022
1
  DBOptions db_opt;
1023
1
  db_opt.max_open_files = 12345;
1024
1
  db_opt.max_background_flushes = 301;
1025
1
  db_opt.max_total_wal_size = 1024;
1026
1
  ColumnFamilyOptions cf_opt;
1027
1028
1
  std::string options_file_content =
1029
1
      "# This is a testing option string.\n"
1030
1
      "# Currently we only support \"#\" styled comment.\n"
1031
1
      "\n"
1032
1
      "[Version]\n"
1033
1
      "  rocksdb_version=3.14.0\n"
1034
1
      "  options_file_version=1\n"
1035
1
      "[DBOptions]\n"
1036
1
      "  max_open_files=12345\n"
1037
1
      "  max_background_flushes=301\n"
1038
1
      "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
1039
1
      "[CFOptions \"something_else\"]\n"
1040
1
      "  # if a section is blank, we will use the default\n"
1041
1
      "[CFOptions \"default\"]\n"
1042
1
      "  # if a section is blank, we will use the default\n";
1043
1044
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
1045
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
1046
1
  RocksDBOptionsParser parser;
1047
1
  ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
1048
1
}
1049
1050
1
TEST_F(OptionsParserTest, DuplicateCFOptions) {
1051
1
  DBOptions db_opt;
1052
1
  db_opt.max_open_files = 12345;
1053
1
  db_opt.max_background_flushes = 301;
1054
1
  db_opt.max_total_wal_size = 1024;
1055
1
  ColumnFamilyOptions cf_opt;
1056
1057
1
  std::string options_file_content =
1058
1
      "# This is a testing option string.\n"
1059
1
      "# Currently we only support \"#\" styled comment.\n"
1060
1
      "\n"
1061
1
      "[Version]\n"
1062
1
      "  rocksdb_version=3.14.0\n"
1063
1
      "  options_file_version=1\n"
1064
1
      "[DBOptions]\n"
1065
1
      "  max_open_files=12345\n"
1066
1
      "  max_background_flushes=301\n"
1067
1
      "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
1068
1
      "[CFOptions \"default\"]\n"
1069
1
      "[CFOptions \"something_else\"]\n"
1070
1
      "[CFOptions \"something_else\"]\n";
1071
1072
1
  const std::string kTestFileName = "test-rocksdb-options.ini";
1073
1
  ASSERT_OK(env_->WriteToNewFile(kTestFileName, options_file_content));
1074
1
  RocksDBOptionsParser parser;
1075
1
  ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
1076
1
}
1077
1078
1
TEST_F(OptionsParserTest, ParseVersion) {
1079
1
  DBOptions db_opt;
1080
1
  db_opt.max_open_files = 12345;
1081
1
  db_opt.max_background_flushes = 301;
1082
1
  db_opt.max_total_wal_size = 1024;
1083
1
  ColumnFamilyOptions cf_opt;
1084
1085
1
  std::string file_template =
1086
1
      "# This is a testing option string.\n"
1087
1
      "# Currently we only support \"#\" styled comment.\n"
1088
1
      "\n"
1089
1
      "[Version]\n"
1090
1
      "  rocksdb_version=3.13.1\n"
1091
1
      "  options_file_version=%s\n"
1092
1
      "[DBOptions]\n"
1093
1
      "[CFOptions \"default\"]\n";
1094
1
  const int kLength = 1000;
1095
1
  char buffer[kLength];
1096
1
  RocksDBOptionsParser parser;
1097
1098
1
  const std::vector<std::string> invalid_versions = {
1099
1
      "a.b.c", "3.2.2b", "3.-12", "3. 1",  // only digits and dots are allowed
1100
1
      "1.2.3.4",
1101
1
      "1.2.3"  // can only contains at most one dot.
1102
1
      "0",     // options_file_version must be at least one
1103
1
      "3..2",
1104
1
      ".", ".1.2",             // must have at least one digit before each dot
1105
1
      "1.2.", "1.", "2.34."};  // must have at least one digit after each dot
1106
12
  for (auto iv : invalid_versions) {
1107
12
    snprintf(buffer, kLength - 1, file_template.c_str(), iv.c_str());
1108
1109
12
    parser.Reset();
1110
12
    ASSERT_OK(env_->WriteToNewFile(iv, buffer));
1111
12
    ASSERT_NOK(parser.Parse(iv, env_.get()));
1112
12
  }
1113
1114
1
  const std::vector<std::string> valid_versions = {
1115
1
      "1.232", "100", "3.12", "1", "12.3  ", "  1.25  "};
1116
6
  for (auto vv : valid_versions) {
1117
6
    snprintf(buffer, kLength - 1, file_template.c_str(), vv.c_str());
1118
6
    parser.Reset();
1119
6
    ASSERT_OK(env_->WriteToNewFile(vv, buffer));
1120
6
    ASSERT_OK(parser.Parse(vv, env_.get()));
1121
6
  }
1122
1
}
1123
1124
void VerifyCFPointerTypedOptions(
1125
    ColumnFamilyOptions* base_cf_opt, const ColumnFamilyOptions* new_cf_opt,
1126
6
    const std::unordered_map<std::string, std::string>* new_cf_opt_map) {
1127
6
  std::string name_buffer;
1128
6
  ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1129
6
                                                  new_cf_opt_map));
1130
1131
  // change the name of merge operator back-and-forth
1132
6
  {
1133
6
    auto* merge_operator = dynamic_cast<test::ChanglingMergeOperator*>(
1134
6
        base_cf_opt->merge_operator.get());
1135
6
    if (merge_operator != nullptr) {
1136
6
      name_buffer = merge_operator->Name();
1137
      // change the name  and expect non-ok status
1138
6
      merge_operator->SetName("some-other-name");
1139
6
      ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1140
6
          *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1141
      // change the name back and expect ok status
1142
6
      merge_operator->SetName(name_buffer);
1143
6
      ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1144
6
                                                      new_cf_opt_map));
1145
6
    }
1146
6
  }
1147
1148
  // change the name of the compaction filter factory back-and-forth
1149
6
  {
1150
6
    auto* compaction_filter_factory =
1151
6
        dynamic_cast<test::ChanglingCompactionFilterFactory*>(
1152
6
            base_cf_opt->compaction_filter_factory.get());
1153
6
    if (compaction_filter_factory != nullptr) {
1154
6
      name_buffer = compaction_filter_factory->Name();
1155
      // change the name and expect non-ok status
1156
6
      compaction_filter_factory->SetName("some-other-name");
1157
6
      ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1158
6
          *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1159
      // change the name back and expect ok status
1160
6
      compaction_filter_factory->SetName(name_buffer);
1161
6
      ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1162
6
                                                      new_cf_opt_map));
1163
6
    }
1164
6
  }
1165
1166
  // test by setting compaction_filter to nullptr
1167
6
  {
1168
6
    auto* tmp_compaction_filter = base_cf_opt->compaction_filter;
1169
6
    if (tmp_compaction_filter != nullptr) {
1170
6
      base_cf_opt->compaction_filter = nullptr;
1171
      // set compaction_filter to nullptr and expect non-ok status
1172
6
      ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1173
6
          *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1174
      // set the value back and expect ok status
1175
6
      base_cf_opt->compaction_filter = tmp_compaction_filter;
1176
6
      ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1177
6
                                                      new_cf_opt_map));
1178
6
    }
1179
6
  }
1180
1181
  // test by setting table_factory to nullptr
1182
6
  {
1183
6
    auto tmp_table_factory = base_cf_opt->table_factory;
1184
6
    if (tmp_table_factory != nullptr) {
1185
6
      base_cf_opt->table_factory.reset();
1186
      // set table_factory to nullptr and expect non-ok status
1187
6
      ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1188
6
          *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1189
      // set the value back and expect ok status
1190
6
      base_cf_opt->table_factory = tmp_table_factory;
1191
6
      ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1192
6
                                                      new_cf_opt_map));
1193
6
    }
1194
6
  }
1195
1196
  // test by setting memtable_factory to nullptr
1197
6
  {
1198
6
    auto tmp_memtable_factory = base_cf_opt->memtable_factory;
1199
6
    if (tmp_memtable_factory != nullptr) {
1200
6
      base_cf_opt->memtable_factory.reset();
1201
      // set memtable_factory to nullptr and expect non-ok status
1202
6
      ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1203
6
          *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1204
      // set the value back and expect ok status
1205
6
      base_cf_opt->memtable_factory = tmp_memtable_factory;
1206
6
      ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1207
6
                                                      new_cf_opt_map));
1208
6
    }
1209
6
  }
1210
6
}
1211
1212
1
TEST_F(OptionsParserTest, DumpAndParse) {
1213
1
  DBOptions base_db_opt;
1214
1
  std::vector<ColumnFamilyOptions> base_cf_opts;
1215
1
  std::vector<std::string> cf_names = {"default", "cf1", "cf2", "cf3",
1216
1
                                       "c:f:4:4:4"
1217
1
                                       "p\\i\\k\\a\\chu\\\\\\",
1218
1
                                       "###rocksdb#1-testcf#2###"};
1219
1
  const int num_cf = static_cast<int>(cf_names.size());
1220
1
  Random rnd(302);
1221
1
  test::RandomInitDBOptions(&base_db_opt, &rnd);
1222
1
  base_db_opt.db_log_dir += "/#odd #but #could #happen #path #/\\\\#OMG";
1223
7
  for (int c = 0; c < num_cf; ++c) {
1224
6
    ColumnFamilyOptions cf_opt;
1225
6
    Random cf_rnd(0xFB + c);
1226
6
    test::RandomInitCFOptions(&cf_opt, &cf_rnd);
1227
6
    if (c < 4) {
1228
4
      cf_opt.prefix_extractor.reset(test::RandomSliceTransform(&rnd, c));
1229
4
    }
1230
6
    if (c < 3) {
1231
3
      cf_opt.table_factory.reset(test::RandomTableFactory(&rnd, c));
1232
3
    }
1233
6
    base_cf_opts.emplace_back(cf_opt);
1234
6
  }
1235
1236
1
  const std::string kOptionsFileName = "test-persisted-options.ini";
1237
1
  ASSERT_OK(PersistRocksDBOptions(base_db_opt, cf_names, base_cf_opts,
1238
1
                                  kOptionsFileName, env_.get()));
1239
1240
1
  RocksDBOptionsParser parser;
1241
1
  ASSERT_OK(parser.Parse(kOptionsFileName, env_.get()));
1242
1243
1
  ASSERT_OK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
1244
1
      base_db_opt, cf_names, base_cf_opts, kOptionsFileName, env_.get()));
1245
1246
1
  ASSERT_OK(
1247
1
      RocksDBOptionsParser::VerifyDBOptions(*parser.db_opt(), base_db_opt));
1248
7
  for (int c = 0; c < num_cf; ++c) {
1249
6
    const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
1250
6
    ASSERT_NE(cf_opt, nullptr);
1251
6
    ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
1252
6
        base_cf_opts[c], *cf_opt, &(parser.cf_opt_maps()->at(c))));
1253
6
  }
1254
1255
  // Further verify pointer-typed options
1256
7
  for (int c = 0; c < num_cf; ++c) {
1257
6
    const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
1258
6
    ASSERT_NE(cf_opt, nullptr);
1259
6
    VerifyCFPointerTypedOptions(&base_cf_opts[c], cf_opt,
1260
6
                                &(parser.cf_opt_maps()->at(c)));
1261
6
  }
1262
1263
1
  ASSERT_EQ(parser.GetCFOptions("does not exist"), nullptr);
1264
1265
1
  base_db_opt.max_open_files++;
1266
1
  ASSERT_NOK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
1267
1
      base_db_opt, cf_names, base_cf_opts, kOptionsFileName, env_.get()));
1268
1269
7
  for (int c = 0; c < num_cf; ++c) {
1270
6
    if (base_cf_opts[c].compaction_filter) {
1271
6
      delete base_cf_opts[c].compaction_filter;
1272
6
    }
1273
6
  }
1274
1
}
1275
1276
1
TEST_F(OptionsParserTest, DifferentDefault) {
1277
1
  const std::string kOptionsFileName = "test-persisted-options.ini";
1278
1279
1
  ColumnFamilyOptions cf_level_opts;
1280
1
  cf_level_opts.OptimizeLevelStyleCompaction();
1281
1282
1
  ColumnFamilyOptions cf_univ_opts;
1283
1
  cf_univ_opts.OptimizeUniversalStyleCompaction();
1284
1285
1
  ASSERT_OK(PersistRocksDBOptions(DBOptions(), {"default", "universal"},
1286
1
                                  {cf_level_opts, cf_univ_opts},
1287
1
                                  kOptionsFileName, env_.get()));
1288
1289
1
  RocksDBOptionsParser parser;
1290
1
  ASSERT_OK(parser.Parse(kOptionsFileName, env_.get()));
1291
1
}
1292
1293
class OptionsSanityCheckTest : public OptionsParserTest {
1294
 public:
1295
1
  OptionsSanityCheckTest() {}
1296
1297
 protected:
1298
  Status SanityCheckCFOptions(const ColumnFamilyOptions& cf_opts,
1299
65
                              OptionsSanityCheckLevel level) {
1300
65
    return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
1301
65
        DBOptions(), {"default"}, {cf_opts}, kOptionsFileName, env_.get(),
1302
65
        level);
1303
65
  }
1304
1305
21
  Status PersistCFOptions(const ColumnFamilyOptions& cf_opts) {
1306
21
    Status s = env_->DeleteFile(kOptionsFileName);
1307
21
    if (!s.ok()) {
1308
0
      return s;
1309
0
    }
1310
21
    return PersistRocksDBOptions(DBOptions(), {"default"}, {cf_opts},
1311
21
                                 kOptionsFileName, env_.get());
1312
21
  }
1313
1314
  const std::string kOptionsFileName = "OPTIONS";
1315
};
1316
1317
1
TEST_F(OptionsSanityCheckTest, SanityCheck) {
1318
1
  ColumnFamilyOptions opts;
1319
1
  Random rnd(301);
1320
1321
  // default ColumnFamilyOptions
1322
1
  {
1323
1
    ASSERT_OK(PersistCFOptions(opts));
1324
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1325
1
  }
1326
1327
  // prefix_extractor
1328
1
  {
1329
    // Okay to change prefix_extractor form nullptr to non-nullptr
1330
1
    ASSERT_EQ(opts.prefix_extractor.get(), nullptr);
1331
1
    opts.prefix_extractor.reset(NewCappedPrefixTransform(10));
1332
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1333
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1334
1335
    // persist the change
1336
1
    ASSERT_OK(PersistCFOptions(opts));
1337
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1338
1339
    // use same prefix extractor but with different parameter
1340
1
    opts.prefix_extractor.reset(NewCappedPrefixTransform(15));
1341
    // expect pass only in kSanityLevelNone
1342
1
    ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1343
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1344
1345
    // repeat the test with FixedPrefixTransform
1346
1
    opts.prefix_extractor.reset(NewFixedPrefixTransform(10));
1347
1
    ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1348
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1349
1350
    // persist the change of prefix_extractor
1351
1
    ASSERT_OK(PersistCFOptions(opts));
1352
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1353
1354
    // use same prefix extractor but with different parameter
1355
1
    opts.prefix_extractor.reset(NewFixedPrefixTransform(15));
1356
    // expect pass only in kSanityLevelNone
1357
1
    ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1358
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1359
1360
    // Change prefix extractor from non-nullptr to nullptr
1361
1
    opts.prefix_extractor.reset();
1362
    // expect pass as it's safe to change prefix_extractor
1363
    // from non-null to null
1364
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1365
1
    ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1366
1
  }
1367
  // persist the change
1368
1
  ASSERT_OK(PersistCFOptions(opts));
1369
1
  ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1370
1371
  // table_factory
1372
1
  {
1373
3
    for (int tb = 0; tb <= 1; ++tb) {
1374
      // change the table factory
1375
2
      opts.table_factory.reset(test::RandomTableFactory(&rnd, tb));
1376
2
      ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1377
2
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1378
1379
      // persist the change
1380
2
      ASSERT_OK(PersistCFOptions(opts));
1381
2
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1382
2
    }
1383
1
  }
1384
1385
  // merge_operator
1386
1
  {
1387
6
    for (int test = 0; test < 5; ++test) {
1388
      // change the merge operator
1389
5
      opts.merge_operator.reset(test::RandomMergeOperator(&rnd));
1390
5
      ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1391
5
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1392
1393
      // persist the change
1394
5
      ASSERT_OK(PersistCFOptions(opts));
1395
5
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1396
5
    }
1397
1
  }
1398
1399
  // compaction_filter
1400
1
  {
1401
6
    for (int test = 0; test < 5; ++test) {
1402
      // change the compaction filter
1403
5
      opts.compaction_filter = test::RandomCompactionFilter(&rnd);
1404
5
      ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1405
5
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1406
1407
      // persist the change
1408
5
      ASSERT_OK(PersistCFOptions(opts));
1409
5
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1410
5
      delete opts.compaction_filter;
1411
5
      opts.compaction_filter = nullptr;
1412
5
    }
1413
1
  }
1414
1415
  // compaction_filter_factory
1416
1
  {
1417
6
    for (int test = 0; test < 5; ++test) {
1418
      // change the compaction filter factory
1419
5
      opts.compaction_filter_factory.reset(
1420
5
          test::RandomCompactionFilterFactory(&rnd));
1421
5
      ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1422
5
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1423
1424
      // persist the change
1425
5
      ASSERT_OK(PersistCFOptions(opts));
1426
5
      ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1427
5
    }
1428
1
  }
1429
1
}
1430
1431
namespace {
1432
2
bool IsEscapedString(const std::string& str) {
1433
438
  for (size_t i = 0; i < str.size(); ++i) {
1434
436
    if (str[i] == '\\') {
1435
      // since we already handle those two consecutive '\'s in
1436
      // the next if-then branch, any '\' appear at the end
1437
      // of an escaped string in such case is not valid.
1438
21
      if (i == str.size() - 1) {
1439
0
        return false;
1440
0
      }
1441
21
      if (str[i + 1] == '\\') {
1442
        // if there're two consecutive '\'s, skip the second one.
1443
10
        i++;
1444
10
        continue;
1445
10
      }
1446
11
      switch (str[i + 1]) {
1447
3
        case ':':
1448
3
        case '\\':
1449
9
        case '#':
1450
9
          continue;
1451
2
        default:
1452
          // if true, '\' together with str[i + 1] is not a valid escape.
1453
2
          if (UnescapeChar(str[i + 1]) == str[i + 1]) {
1454
0
            return false;
1455
0
          }
1456
415
      }
1457
415
    } else if (isSpecialChar(str[i]) && (i == 0 || str[i - 1] != '\\')) {
1458
0
      return false;
1459
0
    }
1460
436
  }
1461
2
  return true;
1462
2
}
1463
}  // namespace
1464
1465
1
TEST_F(OptionsParserTest, EscapeOptionString) {
1466
1
  ASSERT_EQ(UnescapeOptionString(
1467
1
                "This is a test string with \\# \\: and \\\\ escape chars."),
1468
1
            "This is a test string with # : and \\ escape chars.");
1469
1470
1
  ASSERT_EQ(
1471
1
      EscapeOptionString("This is a test string with # : and \\ escape chars."),
1472
1
      "This is a test string with \\# \\: and \\\\ escape chars.");
1473
1474
1
  std::string readible_chars =
1475
1
      "A String like this \"1234567890-=_)(*&^%$#@!ertyuiop[]{POIU"
1476
1
      "YTREWQasdfghjkl;':LKJHGFDSAzxcvbnm,.?>"
1477
1
      "<MNBVCXZ\\\" should be okay to \\#\\\\\\:\\#\\#\\#\\ "
1478
1
      "be serialized and deserialized";
1479
1480
1
  std::string escaped_string = EscapeOptionString(readible_chars);
1481
1
  ASSERT_TRUE(IsEscapedString(escaped_string));
1482
  // This two transformations should be canceled and should output
1483
  // the original input.
1484
1
  ASSERT_EQ(UnescapeOptionString(escaped_string), readible_chars);
1485
1486
1
  std::string all_chars;
1487
256
  for (unsigned char c = 0;; ++c) {
1488
256
    all_chars += c;
1489
256
    if (c == 255) {
1490
1
      break;
1491
1
    }
1492
256
  }
1493
1
  escaped_string = EscapeOptionString(all_chars);
1494
1
  ASSERT_TRUE(IsEscapedString(escaped_string));
1495
1
  ASSERT_EQ(UnescapeOptionString(escaped_string), all_chars);
1496
1497
1
  ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
1498
1
                "     A simple statement with a comment.  # like this :)"),
1499
1
            "A simple statement with a comment.");
1500
1501
1
  ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
1502
1
                "Escape \\# and # comment together   ."),
1503
1
            "Escape \\# and");
1504
1
}
1505
1506
// Only run the tests to verify new fields in options are settable through
1507
// string on limited platforms as it depends on behavior of compilers.
1508
#if defined(__linux__) && !defined(__clang__)
1509
1510
struct OffsetGap {
1511
  size_t begin_offset;
1512
  size_t end_offset;
1513
  const char* name;
1514
1515
  std::string ToString() const {
1516
    return yb::Format("{$0, $1, $2}", begin_offset, end_offset, name);
1517
  }
1518
};
1519
1520
typedef std::vector<OffsetGap> OffsetGaps;
1521
1522
std::vector<Slice> SettableSlices(const char* start_ptr,
1523
                                  size_t total_size,
1524
                                  const OffsetGaps& blacklist) {
1525
  size_t offset = 0;
1526
  std::vector<Slice> result;
1527
  result.reserve(blacklist.size() + 1);
1528
  for (auto& tuple : blacklist) {
1529
    result.emplace_back(start_ptr + offset, tuple.begin_offset - offset);
1530
    offset = tuple.end_offset;
1531
  }
1532
  result.emplace_back(start_ptr + offset, total_size - offset);
1533
  return result;
1534
}
1535
1536
constexpr char kUnsetMark1 = 'R';
1537
constexpr char kUnsetMark2 = ~kUnsetMark1;
1538
1539
void FillWithSpecialChar(char* start_ptr,
1540
                         size_t total_size,
1541
                         const OffsetGaps& blacklist,
1542
                         char unset_mark) {
1543
  for (auto& slice : SettableSlices(start_ptr, total_size, blacklist)) {
1544
    std::memset(slice.mutable_data(), unset_mark, slice.size());
1545
  }
1546
}
1547
1548
// Finds offsets of bytes that are different for
1549
// [start_ptr1, start_ptr1 + total_size) and [start_ptr2, start_ptr2 + total_size)
1550
// Do not check offsets specified by blacklist
1551
std::vector<size_t> DifferentBytes(const char* start_ptr1,
1552
                                   const char* start_ptr2,
1553
                                   size_t total_size,
1554
                                   const OffsetGaps& blacklist) {
1555
  std::vector<size_t> result;
1556
  for (auto& slice : SettableSlices(start_ptr1, total_size, blacklist)) {
1557
    for (const char* ptr = slice.cdata(); ptr != slice.cend(); ++ptr) {
1558
      if (*ptr != start_ptr2[ptr - start_ptr1]) {
1559
        result.push_back(ptr - start_ptr1);
1560
      }
1561
    }
1562
  }
1563
  return result;
1564
}
1565
1566
// Stores object of T in POD-buffer. After object is created all its bytes, except blacklist are
1567
// filled with unset_mark.
1568
template<class T>
1569
class UnsetHolder {
1570
 public:
1571
  template<class... Args>
1572
  UnsetHolder(char unset_mark, const OffsetGaps& blacklist, Args&&... args)
1573
      : blacklist_(blacklist) {
1574
    new (&storage_) T(std::forward<Args>(args)...);
1575
    FillWithSpecialChar(raw_data(), sizeof(T), blacklist, unset_mark);
1576
  }
1577
1578
  UnsetHolder(const UnsetHolder&) = delete;
1579
  void operator=(const UnsetHolder&) = delete;
1580
1581
  ~UnsetHolder() {
1582
    get()->~T();
1583
  }
1584
1585
  std::vector<size_t> DifferentBytes(const UnsetHolder& rhs) const {
1586
    return rocksdb::DifferentBytes(raw_data(), rhs.raw_data(), sizeof(T), blacklist_);
1587
  }
1588
1589
  T* operator->() {
1590
    return get();
1591
  }
1592
1593
  T* get() {
1594
    return reinterpret_cast<T*>(&storage_);;
1595
  }
1596
1597
  char* raw_data() {
1598
    return reinterpret_cast<char*>(&storage_);;
1599
  }
1600
1601
  const char* raw_data() const {
1602
    return reinterpret_cast<const char*>(&storage_);;
1603
  }
1604
1605
  T& operator*() {
1606
    return *get();
1607
  }
1608
1609
 private:
1610
  const OffsetGaps& blacklist_;
1611
  typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
1612
};
1613
1614
void InitDefault(BlockBasedTableOptions*) {}
1615
1616
Status GetFromString(BlockBasedTableOptions* source, BlockBasedTableOptions* destination) {
1617
  const char* const kOptionsString =
1618
      "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
1619
      "checksum=kxxHash;hash_index_allow_collision=1;no_block_cache=1;"
1620
      "block_cache=1M;block_cache_compressed=1k;block_size=1024;filter_block_size=16384;"
1621
      "block_size_deviation=8;block_restart_interval=4; "
1622
      "index_block_restart_interval=4;index_block_size=16384;min_keys_per_index_block=16;"
1623
      "filter_policy=bloomfilter:4:true;whole_key_filtering=1;"
1624
      "skip_table_builder_flush=1;format_version=1;"
1625
      "hash_index_allow_collision=false;";
1626
1627
  RETURN_NOT_OK(GetBlockBasedTableOptionsFromString(*source, kOptionsString, destination));
1628
1629
  // This option is not setable:
1630
  destination->use_delta_encoding = false;
1631
1632
  EXPECT_NE(nullptr, destination->block_cache.get());
1633
  EXPECT_NE(nullptr, destination->block_cache_compressed.get());
1634
  EXPECT_NE(nullptr, destination->filter_policy.get());
1635
1636
  return Status::OK();
1637
}
1638
1639
void InitDefault(DBOptions*) {}
1640
1641
Status GetFromString(DBOptions* source, DBOptions* destination) {
1642
  const char* const kOptionsString =
1643
      "wal_bytes_per_sync=4295048118;"
1644
      "delete_obsolete_files_period_micros=4294967758;"
1645
      "WAL_ttl_seconds=4295008036;"
1646
      "WAL_size_limit_MB=4295036161;"
1647
      "wal_dir=path/to/wal_dir;"
1648
      "db_write_buffer_size=2587;"
1649
      "max_subcompactions=64330;"
1650
      "table_cache_numshardbits=28;"
1651
      "max_open_files=72;"
1652
      "max_file_opening_threads=35;"
1653
      "base_background_compactions=3;"
1654
      "max_background_compactions=33;"
1655
      "use_fsync=true;"
1656
      "use_adaptive_mutex=true;"
1657
      "max_total_wal_size=4295005604;"
1658
      "compaction_readahead_size=0;"
1659
      "new_table_reader_for_compaction_inputs=true;"
1660
      "keep_log_file_num=4890;"
1661
      "skip_stats_update_on_db_open=true;"
1662
      "max_manifest_file_size=4295009941;"
1663
      "db_log_dir=path/to/db_log_dir;"
1664
      "skip_log_error_on_recovery=true;"
1665
      "writable_file_max_buffer_size=1048576;"
1666
      "paranoid_checks=false;"
1667
      "is_fd_close_on_exec=true;"
1668
      "bytes_per_sync=4295013613;"
1669
      "enable_thread_tracking=true;"
1670
      "disable_data_sync=true;"
1671
      "recycle_log_file_num=0;"
1672
      "disableDataSync=true;"
1673
      "create_missing_column_families=true;"
1674
      "log_file_time_to_roll=3097;"
1675
      "max_background_flushes=35;"
1676
      "create_if_missing=true;"
1677
      "error_if_exists=true;"
1678
      "allow_os_buffer=true;"
1679
      "delayed_write_rate=4294976214;"
1680
      "manifest_preallocation_size=1222;"
1681
      "allow_mmap_writes=true;"
1682
      "stats_dump_period_sec=70127;"
1683
      "allow_fallocate=true;"
1684
      "allow_mmap_reads=true;"
1685
      "max_log_file_size=4607;"
1686
      "random_access_max_buffer_size=1048576;"
1687
      "advise_random_on_open=true;"
1688
      "fail_if_options_file_error=true;"
1689
      "allow_concurrent_memtable_write=true;"
1690
      "wal_recovery_mode=kPointInTimeRecovery;"
1691
      "enable_write_thread_adaptive_yield=true;"
1692
      "write_thread_slow_yield_usec=5;"
1693
      "write_thread_max_yield_usec=1000;"
1694
      "access_hint_on_compaction_start=NONE;"
1695
      "max_file_size_for_compaction=123;"
1696
      "initial_seqno=432;"
1697
      "num_reserved_small_compaction_threads=-1;"
1698
      "compaction_size_threshold_bytes=18446744073709551615;"
1699
      "info_log_level=DEBUG_LEVEL;";
1700
1701
  return GetDBOptionsFromString(*source, kOptionsString, destination);
1702
}
1703
1704
// We want padding bytes to have the same values for test purposes, therefore we need to use
1705
// exactly the same saved default value instead of using CompactionOptionsUniversal().
1706
static CompactionOptionsUniversal kCompactionOptionsUniversalDefault;
1707
1708
void InitDefault(ColumnFamilyOptions* options) {
1709
  options->compaction_options_universal = kCompactionOptionsUniversalDefault;
1710
  // Deprecatd option which is not initialized. Need to set it to avoid
1711
  // Valgrind error
1712
  options->max_mem_compaction_level = 0;
1713
}
1714
1715
Status GetFromString(ColumnFamilyOptions* source, ColumnFamilyOptions* destination) {
1716
  // Need to update the option string if a new option is added.
1717
  const char* const kOptionsString =
1718
      "compaction_filter_factory=mpudlojcujCompactionFilterFactory;"
1719
      "table_factory=PlainTable;"
1720
      "prefix_extractor=rocksdb.CappedPrefix.13;"
1721
      "comparator=leveldb.BytewiseComparator;"
1722
      "compression_per_level=kBZip2Compression:kBZip2Compression:"
1723
      "kBZip2Compression:kNoCompression:kZlibCompression:kBZip2Compression:"
1724
      "kSnappyCompression;"
1725
      "max_bytes_for_level_base=986;"
1726
      "bloom_locality=8016;"
1727
      "target_file_size_base=4294976376;"
1728
      "memtable_prefix_bloom_huge_page_tlb_size=2557;"
1729
      "max_successive_merges=5497;"
1730
      "max_sequential_skip_in_iterations=4294971408;"
1731
      "arena_block_size=1893;"
1732
      "target_file_size_multiplier=35;"
1733
      "source_compaction_factor=54;"
1734
      "min_write_buffer_number_to_merge=9;"
1735
      "max_write_buffer_number=84;"
1736
      "write_buffer_size=1653;"
1737
      "max_grandparent_overlap_factor=64;"
1738
      "max_bytes_for_level_multiplier=60;"
1739
      "memtable_factory=SkipListFactory;"
1740
      "compression=kNoCompression;"
1741
      "min_partial_merge_operands=7576;"
1742
      "level0_stop_writes_trigger=33;"
1743
      "num_levels=99;"
1744
      "level0_slowdown_writes_trigger=22;"
1745
      "level0_file_num_compaction_trigger=14;"
1746
      "expanded_compaction_factor=34;"
1747
      "compaction_filter=urxcqstuwnCompactionFilter;"
1748
      "soft_rate_limit=530.615385;"
1749
      "soft_pending_compaction_bytes_limit=0;"
1750
      "max_write_buffer_number_to_maintain=84;"
1751
      "verify_checksums_in_compaction=false;"
1752
      "merge_operator=aabcxehazrMergeOperator;"
1753
      "memtable_prefix_bloom_bits=4642;"
1754
      "paranoid_file_checks=true;"
1755
      "inplace_update_num_locks=7429;"
1756
      "optimize_filters_for_hits=false;"
1757
      "level_compaction_dynamic_level_bytes=false;"
1758
      "inplace_update_support=false;"
1759
      "compaction_style=kCompactionStyleFIFO;"
1760
      "memtable_prefix_bloom_probes=2511;"
1761
      "purge_redundant_kvs_while_flush=true;"
1762
      "filter_deletes=false;"
1763
      "hard_pending_compaction_bytes_limit=0;"
1764
      "disable_auto_compactions=false;"
1765
      "compaction_measure_io_stats=true;";
1766
1767
  RETURN_NOT_OK(GetColumnFamilyOptionsFromString(*source, kOptionsString, destination));
1768
1769
  // Following options are not settable through
1770
  // GetColumnFamilyOptionsFromString():
1771
  destination->rate_limit_delay_max_milliseconds = 33;
1772
  destination->compaction_pri = CompactionPri::kOldestSmallestSeqFirst;
1773
  destination->compaction_options_universal = kCompactionOptionsUniversalDefault;
1774
  destination->compression_opts = CompressionOptions();
1775
  destination->hard_rate_limit = 0;
1776
  destination->soft_rate_limit = 0;
1777
  destination->compaction_options_fifo = CompactionOptionsFIFO();
1778
  destination->max_mem_compaction_level = 0;
1779
1780
  return Status::OK();
1781
}
1782
1783
// Takes 2 sorted collections and removes all entries that is present in both of them.
1784
// After execution of this function c1 contains entries there were not contained in c2.
1785
// And vise versa.
1786
template<class Collection>
1787
void FindDelta(Collection* c1, Collection* c2) {
1788
  auto w1 = c1->begin();
1789
  auto w2 = c2->begin();
1790
  auto i1 = c1->begin();
1791
  auto i2 = c2->begin();
1792
  while (i1 != c1->end() && i2 != c2->end()) {
1793
    if (*i1 < *i2) {
1794
      *w1 = *i1;
1795
      ++w1;
1796
      ++i1;
1797
    } else if (*i2 < *i1) {
1798
      *w2 = *i2;
1799
      ++w2;
1800
      ++i2;
1801
    } else {
1802
      ++i1;
1803
      ++i2;
1804
    }
1805
  }
1806
  c1->erase(w1, i1);
1807
  c2->erase(w2, i2);
1808
}
1809
1810
template<class T>
1811
void TestAllFieldsSettable(const OffsetGaps& blacklist) {
1812
  std::vector<size_t> unset_bytes_base;
1813
  {
1814
    UnsetHolder<T> value1(kUnsetMark1, blacklist);
1815
    UnsetHolder<T> value2(kUnsetMark2, blacklist);
1816
1817
    *value1 = T();
1818
    *value2 = T();
1819
1820
    InitDefault(value1.get());
1821
    InitDefault(value2.get());
1822
1823
    // Count padding bytes by setting all bytes in the memory to a special char,
1824
    // copy a well constructed struct to this memory and see how many special
1825
    // bytes left.
1826
1827
    unset_bytes_base = value1.DifferentBytes(value2);
1828
  }
1829
1830
  {
1831
    UnsetHolder<T> value1(kUnsetMark1, blacklist);
1832
    UnsetHolder<T> value2(kUnsetMark2, blacklist);
1833
1834
    UnsetHolder<T> new_value1(kUnsetMark1, blacklist);
1835
    UnsetHolder<T> new_value2(kUnsetMark2, blacklist);
1836
1837
    // Need to update the option string if a new option is added.
1838
    ASSERT_OK(GetFromString(value1.get(), new_value1.get()));
1839
    ASSERT_OK(GetFromString(value2.get(), new_value2.get()));
1840
1841
    auto actual_unset_bytes = new_value1.DifferentBytes(new_value2);
1842
    auto unset_bytes_copy = unset_bytes_base;
1843
    FindDelta(&unset_bytes_copy, &actual_unset_bytes);
1844
    ASSERT_EQ(unset_bytes_copy, actual_unset_bytes) << yb::Format("Blacklist: $0", blacklist);
1845
1846
    // The byte could be one of 3 types:
1847
    // 1) Padding byte, then it is same in new_value1 and value1.
1848
    // 2) Settable by from string, then it should differ in new_value1 and value1.
1849
    // 3) Blacklisted
1850
    auto parsed_bytes = new_value1.DifferentBytes(value1);
1851
    auto all_non_blacklisted = parsed_bytes;
1852
    all_non_blacklisted.insert(all_non_blacklisted.end(),
1853
                               unset_bytes_base.begin(),
1854
                               unset_bytes_base.end());
1855
    std::sort(all_non_blacklisted.begin(), all_non_blacklisted.end());
1856
    all_non_blacklisted.erase(std::unique(all_non_blacklisted.begin(), all_non_blacklisted.end()),
1857
                              all_non_blacklisted.end());
1858
1859
    // Check that (1) and (2) does not overlap.
1860
    ASSERT_EQ(unset_bytes_base.size() + parsed_bytes.size(), all_non_blacklisted.size())
1861
         << yb::Format("Blacklist: $0", blacklist);
1862
1863
    // Check that (1) + (2) is all that is not (3).
1864
    std::vector<Slice> settable_slices = SettableSlices(value1.raw_data(), sizeof(T), blacklist);
1865
    std::vector<size_t> all_settable;
1866
    for (auto& slice : settable_slices) {
1867
      for (const char* p = slice.cdata(); p != slice.cend(); ++p)
1868
        all_settable.push_back(p - value1.raw_data());
1869
    }
1870
    FindDelta(&all_settable, &all_non_blacklisted);
1871
    ASSERT_EQ(all_settable, all_non_blacklisted) << yb::Format("Blacklist: $0", blacklist);
1872
  }
1873
}
1874
1875
#define BLACKLIST_ENTRY(type, field) \
1876
    {offsetof(type, field), \
1877
     offsetof(type, field) + sizeof(static_cast<type*>(nullptr)->field), \
1878
     BOOST_PP_STRINGIZE(field)}
1879
1880
// If the test fails, likely a new option is added to BlockBasedTableOptions
1881
// but it cannot be set through GetBlockBasedTableOptionsFromString(), or the
1882
// test is not updated accordingly.
1883
// After adding an option, we need to make sure it is settable by
1884
// GetBlockBasedTableOptionsFromString() and add the option to the input string
1885
// passed to the GetBlockBasedTableOptionsFromString() in this test.
1886
// If it is a complicated type, you also need to add the field to
1887
// kBbtoBlacklist, and maybe add customized verification for it.
1888
TEST_F(OptionsParserTest, BlockBasedTableOptionsAllFieldsSettable) {
1889
  // Items in the form of <offset, size>. Need to be in ascending order
1890
  // and not overlapping. Need to updated if new pointer-option is added.
1891
  const OffsetGaps kBbtoBlacklist = {
1892
      BLACKLIST_ENTRY(BlockBasedTableOptions, flush_block_policy_factory),
1893
      BLACKLIST_ENTRY(BlockBasedTableOptions, block_cache),
1894
      BLACKLIST_ENTRY(BlockBasedTableOptions, block_cache_compressed),
1895
      BLACKLIST_ENTRY(BlockBasedTableOptions, data_block_key_value_encoding_format),
1896
      BLACKLIST_ENTRY(BlockBasedTableOptions, filter_policy),
1897
      BLACKLIST_ENTRY(BlockBasedTableOptions, supported_filter_policies),
1898
  };
1899
1900
  // In this test, we catch a new option of BlockBasedTableOptions that is not
1901
  // settable through GetBlockBasedTableOptionsFromString().
1902
  // We count padding bytes of the option struct, and assert it to be the same
1903
  // as unset bytes of an option struct initialized by
1904
  // GetBlockBasedTableOptionsFromString().
1905
  TestAllFieldsSettable<BlockBasedTableOptions>(kBbtoBlacklist);
1906
}
1907
1908
// If the test fails, likely a new option is added to DBOptions
1909
// but it cannot be set through GetDBOptionsFromString(), or the test is not
1910
// updated accordingly.
1911
// After adding an option, we need to make sure it is settable by
1912
// GetDBOptionsFromString() and add the option to the input string passed to
1913
// DBOptionsFromString()in this test.
1914
// If it is a complicated type, you also need to add the field to
1915
// kDBOptionsBlacklist, and maybe add customized verification for it.
1916
TEST_F(OptionsParserTest, DBOptionsAllFieldsSettable) {
1917
  const OffsetGaps kDBOptionsBlacklist = {
1918
      BLACKLIST_ENTRY(DBOptions, env),
1919
      BLACKLIST_ENTRY(DBOptions, checkpoint_env),
1920
      BLACKLIST_ENTRY(DBOptions, priority_thread_pool_for_compactions_and_flushes),
1921
      BLACKLIST_ENTRY(DBOptions, rate_limiter),
1922
      BLACKLIST_ENTRY(DBOptions, sst_file_manager),
1923
      BLACKLIST_ENTRY(DBOptions, info_log),
1924
      BLACKLIST_ENTRY(DBOptions, statistics),
1925
      BLACKLIST_ENTRY(DBOptions, db_paths),
1926
      BLACKLIST_ENTRY(DBOptions, db_log_dir),
1927
      BLACKLIST_ENTRY(DBOptions, wal_dir),
1928
      BLACKLIST_ENTRY(DBOptions, memory_monitor),
1929
      BLACKLIST_ENTRY(DBOptions, listeners),
1930
      BLACKLIST_ENTRY(DBOptions, row_cache),
1931
      BLACKLIST_ENTRY(DBOptions, wal_filter),
1932
      BLACKLIST_ENTRY(DBOptions, boundary_extractor),
1933
      BLACKLIST_ENTRY(DBOptions, max_file_size_for_compaction),
1934
      BLACKLIST_ENTRY(DBOptions, mem_table_flush_filter_factory),
1935
      BLACKLIST_ENTRY(DBOptions, log_prefix),
1936
      BLACKLIST_ENTRY(DBOptions, mem_tracker),
1937
      BLACKLIST_ENTRY(DBOptions, block_based_table_mem_tracker),
1938
      BLACKLIST_ENTRY(DBOptions, iterator_replacer),
1939
      BLACKLIST_ENTRY(DBOptions, compaction_file_filter_factory),
1940
  };
1941
1942
  TestAllFieldsSettable<DBOptions>(kDBOptionsBlacklist);
1943
}
1944
1945
// If the test fails, likely a new option is added to ColumnFamilyOptions
1946
// but it cannot be set through GetColumnFamilyOptionsFromString(), or the
1947
// test is not updated accordingly.
1948
// After adding an option, we need to make sure it is settable by
1949
// GetColumnFamilyOptionsFromString() and add the option to the input
1950
// string passed to GetColumnFamilyOptionsFromString()in this test.
1951
// If it is a complicated type, you also need to add the field to
1952
// kColumnFamilyOptionsBlacklist, and maybe add customized verification
1953
// for it.
1954
TEST_F(OptionsParserTest, ColumnFamilyOptionsAllFieldsSettable) {
1955
  const OffsetGaps kColumnFamilyOptionsBlacklist = {
1956
      BLACKLIST_ENTRY(ColumnFamilyOptions, comparator),
1957
      BLACKLIST_ENTRY(ColumnFamilyOptions, merge_operator),
1958
      BLACKLIST_ENTRY(ColumnFamilyOptions, compaction_filter),
1959
      BLACKLIST_ENTRY(ColumnFamilyOptions, compaction_filter_factory),
1960
      BLACKLIST_ENTRY(ColumnFamilyOptions, compression_per_level),
1961
      BLACKLIST_ENTRY(ColumnFamilyOptions, prefix_extractor),
1962
      BLACKLIST_ENTRY(ColumnFamilyOptions, max_bytes_for_level_multiplier_additional),
1963
      BLACKLIST_ENTRY(ColumnFamilyOptions, memtable_factory),
1964
      BLACKLIST_ENTRY(ColumnFamilyOptions, table_factory),
1965
      BLACKLIST_ENTRY(ColumnFamilyOptions, table_properties_collector_factories),
1966
      BLACKLIST_ENTRY(ColumnFamilyOptions, inplace_callback),
1967
  };
1968
1969
  TestAllFieldsSettable<ColumnFamilyOptions>(kColumnFamilyOptionsBlacklist);
1970
}
1971
#endif // __linux__ && !clang
1972
#endif // !ROCKSDB_LITE
1973
1974
}  // namespace rocksdb
1975
1976
13.2k
int main(int argc, char** argv) {
1977
13.2k
  ::testing::InitGoogleTest(&argc, argv);
1978
13.2k
#ifdef GFLAGS
1979
13.2k
  ParseCommandLineFlags(&argc, &argv, true);
1980
13.2k
#endif  // GFLAGS
1981
13.2k
  return RUN_ALL_TESTS();
1982
13.2k
}