YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/utilities/options/options_util_test.cc
Line
Count
Source (jump to first uncovered line)
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under the BSD-style license found in the
3
//  LICENSE file in the root directory of this source tree. An additional grant
4
//  of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
21
#ifndef ROCKSDB_LITE
22
#ifndef __STDC_FORMAT_MACROS
23
#define __STDC_FORMAT_MACROS
24
#endif
25
26
#include <inttypes.h>
27
28
#include <cctype>
29
#include <unordered_map>
30
31
#include "yb/rocksdb/db.h"
32
#include "yb/rocksdb/table.h"
33
#include "yb/rocksdb/utilities/options_util.h"
34
#include "yb/rocksdb/util/options_parser.h"
35
#include "yb/rocksdb/util/random.h"
36
#include "yb/rocksdb/util/testharness.h"
37
#include "yb/rocksdb/util/testutil.h"
38
39
#include "yb/util/test_util.h"
40
41
#ifndef GFLAGS
42
bool FLAGS_enable_print = false;
43
#else
44
#include <gflags/gflags.h>
45
using GFLAGS::ParseCommandLineFlags;
46
DEFINE_bool(enable_print, false, "Print options generated to console.");
47
#endif  // GFLAGS
48
49
namespace rocksdb {
50
class OptionsUtilTest : public RocksDBTest {
51
 public:
52
3
  OptionsUtilTest() : rnd_(0xFB) {
53
3
    env_.reset(new test::StringEnv(Env::Default()));
54
3
    dbname_ = test::TmpDir() + "/options_util_test";
55
3
  }
56
57
 protected:
58
  std::unique_ptr<test::StringEnv> env_;
59
  std::string dbname_;
60
  Random rnd_;
61
};
62
63
25
bool IsBlockBasedTableFactory(TableFactory* tf) {
64
25
  return tf->Name() == BlockBasedTableFactory().Name();
65
25
}
66
67
1
TEST_F(OptionsUtilTest, SaveAndLoad) {
68
1
  const size_t kCFCount = 5;
69
70
1
  DBOptions db_opt;
71
1
  std::vector<std::string> cf_names;
72
1
  std::vector<ColumnFamilyOptions> cf_opts;
73
1
  test::RandomInitDBOptions(&db_opt, &rnd_);
74
6
  for (size_t i = 0; i < kCFCount; ++i) {
75
1
    cf_names.push_back(i == 0 ? kDefaultColumnFamilyName
76
4
                              : test::RandomName(&rnd_, 10));
77
5
    cf_opts.emplace_back();
78
5
    test::RandomInitCFOptions(&cf_opts.back(), &rnd_);
79
5
  }
80
81
1
  const std::string kFileName = "OPTIONS-123456";
82
1
  ASSERT_OK(PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName, env_.get()));
83
84
1
  DBOptions loaded_db_opt;
85
1
  std::vector<ColumnFamilyDescriptor> loaded_cf_descs;
86
1
  ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt,
87
1
                                &loaded_cf_descs));
88
89
1
  ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt));
90
1
  test::RandomInitDBOptions(&db_opt, &rnd_);
91
1
  ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt));
92
93
6
  for (size_t i = 0; i < kCFCount; ++i) {
94
5
    ASSERT_EQ(cf_names[i], loaded_cf_descs[i].name);
95
5
    ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
96
5
        cf_opts[i], loaded_cf_descs[i].options));
97
5
    if (IsBlockBasedTableFactory(cf_opts[i].table_factory.get())) {
98
0
      ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory(
99
0
          cf_opts[i].table_factory.get(),
100
0
          loaded_cf_descs[i].options.table_factory.get()));
101
0
    }
102
5
    test::RandomInitCFOptions(&cf_opts[i], &rnd_);
103
5
    ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
104
5
        cf_opts[i], loaded_cf_descs[i].options));
105
5
  }
106
107
6
  for (size_t i = 0; i < kCFCount; ++i) {
108
5
    if (cf_opts[i].compaction_filter) {
109
5
      delete cf_opts[i].compaction_filter;
110
5
    }
111
5
  }
112
1
}
113
114
1
TEST_F(OptionsUtilTest, CompareIncludeOptions) {
115
1
  const size_t kCFCount = 5;
116
117
1
  DBOptions db_opt;
118
1
  std::vector<std::string> cf_names;
119
1
  std::vector<ColumnFamilyOptions> cf_opts;
120
1
  test::RandomInitDBOptions(&db_opt, &rnd_);
121
6
  for (size_t i = 0; i < kCFCount; ++i) {
122
1
    cf_names.push_back(i == 0 ? kDefaultColumnFamilyName
123
4
                              : test::RandomName(&rnd_, 10));
124
5
    cf_opts.emplace_back();
125
5
    test::RandomInitCFOptions(&cf_opts.back(), &rnd_);
126
5
  }
127
128
1
  std::tuple<IncludeHeader, IncludeFileVersion> include_opts[] {
129
1
    std::make_tuple(IncludeHeader::kTrue, IncludeFileVersion::kTrue),
130
1
    std::make_tuple(IncludeHeader::kTrue, IncludeFileVersion::kFalse),
131
1
    std::make_tuple(IncludeHeader::kFalse, IncludeFileVersion::kTrue),
132
1
    std::make_tuple(IncludeHeader::kFalse, IncludeFileVersion::kFalse)
133
1
  };
134
135
1
  const std::string kFileName = "OPTIONS-123456";
136
137
4
  for (auto const& opts : include_opts) {
138
4
    if (env_->FileExists(kFileName).ok()) {
139
0
      ASSERT_OK(env_->DeleteFile(kFileName));
140
0
    }
141
4
    ASSERT_OK(PersistRocksDBOptions(
142
4
      db_opt, cf_names, cf_opts, kFileName, env_.get(), std::get<0>(opts), std::get<1>(opts)));
143
144
4
    DBOptions loaded_db_opt;
145
4
    std::vector<ColumnFamilyDescriptor> loaded_cf_descs;
146
4
    ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt, &loaded_cf_descs));
147
4
    ASSERT_OK(env_->DeleteFile(kFileName));
148
149
4
    ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt));
150
4
    ASSERT_EQ(loaded_cf_descs.size(), cf_names.size());
151
4
    ASSERT_EQ(loaded_cf_descs.size(), cf_opts.size());
152
24
    for (size_t i = 0; i < loaded_cf_descs.size(); ++i) {
153
20
      ASSERT_EQ(cf_names[i], loaded_cf_descs[i].name);
154
20
      ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(cf_opts[i], loaded_cf_descs[i].options));
155
20
      if (IsBlockBasedTableFactory(cf_opts[i].table_factory.get())) {
156
0
        ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory(
157
0
            cf_opts[i].table_factory.get(), loaded_cf_descs[i].options.table_factory.get()));
158
0
      }
159
20
    }
160
4
  }
161
162
6
  for (size_t i = 0; i < cf_opts.size(); ++i) {
163
5
    if (cf_opts[i].compaction_filter) {
164
5
      delete cf_opts[i].compaction_filter;
165
5
    }
166
5
  }
167
1
}
168
169
namespace {
170
class DummyTableFactory : public TableFactory {
171
 public:
172
1
  DummyTableFactory() {}
173
1
  virtual ~DummyTableFactory() {}
174
175
2
  const char* Name() const override { return "DummyTableFactory"; }
176
177
  virtual Status NewTableReader(const TableReaderOptions& table_reader_options,
178
                                unique_ptr<RandomAccessFileReader>&& file,
179
                                uint64_t file_size,
180
0
                                unique_ptr<TableReader>* table_reader) const override  {
181
0
    return STATUS(NotSupported, "");
182
0
  }
183
184
0
  bool IsSplitSstForWriteSupported() const override { return false; }
185
186
  virtual TableBuilder* NewTableBuilder(
187
      const TableBuilderOptions& table_builder_options, uint32_t column_family_id,
188
0
      WritableFileWriter* metadata_file, WritableFileWriter* data_file = nullptr) const override {
189
0
    return nullptr;
190
0
  }
191
192
  virtual Status SanitizeOptions(const DBOptions& db_opts,
193
0
                                 const ColumnFamilyOptions& cf_opts) const override {
194
0
    return STATUS(NotSupported, "");
195
0
  }
196
197
0
  std::string GetPrintableTableOptions() const override { return ""; }
198
};
199
200
class DummyMergeOperator : public MergeOperator {
201
 public:
202
1
  DummyMergeOperator() {}
203
1
  virtual ~DummyMergeOperator() {}
204
205
  virtual bool FullMerge(const Slice& key, const Slice* existing_value,
206
                         const std::deque<std::string>& operand_list,
207
0
                         std::string* new_value, Logger* logger) const {
208
0
    return false;
209
0
  }
210
211
  virtual bool PartialMergeMulti(const Slice& key,
212
                                 const std::deque<Slice>& operand_list,
213
0
                                 std::string* new_value, Logger* logger) const {
214
0
    return false;
215
0
  }
216
217
2
  virtual const char* Name() const { return "DummyMergeOperator"; }
218
};
219
220
class DummySliceTransform : public SliceTransform {
221
 public:
222
2
  DummySliceTransform() {}
223
2
  virtual ~DummySliceTransform() {}
224
225
  // Return the name of this transformation.
226
3
  virtual const char* Name() const { return "DummySliceTransform"; }
227
228
  // transform a src in domain to a dst in the range
229
0
  virtual Slice Transform(const Slice& src) const { return src; }
230
231
  // determine whether this is a valid src upon the function applies
232
0
  virtual bool InDomain(const Slice& src) const { return false; }
233
234
  // determine whether dst=Transform(src) for some src
235
0
  virtual bool InRange(const Slice& dst) const { return false; }
236
};
237
238
}  // namespace
239
240
1
TEST_F(OptionsUtilTest, SanityCheck) {
241
1
  DBOptions db_opt;
242
1
  std::vector<ColumnFamilyDescriptor> cf_descs;
243
1
  const size_t kCFCount = 5;
244
6
  for (size_t i = 0; i < kCFCount; ++i) {
245
5
    cf_descs.emplace_back();
246
5
    cf_descs.back().name =
247
4
        (i == 0) ? kDefaultColumnFamilyName : test::RandomName(&rnd_, 10);
248
249
5
    cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory());
250
    // Assign non-null values to prefix_extractors except the first cf.
251
5
    cf_descs.back().options.prefix_extractor.reset(
252
4
        i != 0 ? test::RandomSliceTransform(&rnd_) : nullptr);
253
5
    cf_descs.back().options.merge_operator.reset(
254
5
        test::RandomMergeOperator(&rnd_));
255
5
  }
256
257
1
  db_opt.create_missing_column_families = true;
258
1
  db_opt.create_if_missing = true;
259
260
1
  ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_descs[0].options)));
261
1
  DB* db;
262
1
  std::vector<ColumnFamilyHandle*> handles;
263
  // open and persist the options
264
1
  ASSERT_OK(DB::Open(db_opt, dbname_, cf_descs, &handles, &db));
265
266
  // close the db
267
5
  for (auto* handle : handles) {
268
5
    delete handle;
269
5
  }
270
1
  delete db;
271
272
  // perform sanity check
273
1
  ASSERT_OK(
274
1
      CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
275
276
1
  ASSERT_GE(kCFCount, 5);
277
  // merge operator
278
1
  {
279
1
    std::shared_ptr<MergeOperator> merge_op =
280
1
        cf_descs[0].options.merge_operator;
281
282
1
    ASSERT_NE(merge_op.get(), nullptr);
283
1
    cf_descs[0].options.merge_operator.reset();
284
1
    ASSERT_NOK(
285
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
286
287
1
    cf_descs[0].options.merge_operator.reset(new DummyMergeOperator());
288
1
    ASSERT_NOK(
289
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
290
291
1
    cf_descs[0].options.merge_operator = merge_op;
292
1
    ASSERT_OK(
293
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
294
1
  }
295
296
  // prefix extractor
297
1
  {
298
1
    std::shared_ptr<const SliceTransform> prefix_extractor =
299
1
        cf_descs[1].options.prefix_extractor;
300
301
    // It's okay to set prefix_extractor to nullptr.
302
1
    ASSERT_NE(prefix_extractor, nullptr);
303
1
    cf_descs[1].options.prefix_extractor.reset();
304
1
    ASSERT_OK(
305
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
306
307
1
    cf_descs[1].options.prefix_extractor.reset(new DummySliceTransform());
308
1
    ASSERT_NOK(
309
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
310
311
1
    cf_descs[1].options.prefix_extractor = prefix_extractor;
312
1
    ASSERT_OK(
313
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
314
1
  }
315
316
  // prefix extractor nullptr case
317
1
  {
318
1
    std::shared_ptr<const SliceTransform> prefix_extractor =
319
1
        cf_descs[0].options.prefix_extractor;
320
321
    // It's okay to set prefix_extractor to nullptr.
322
1
    ASSERT_EQ(prefix_extractor, nullptr);
323
1
    cf_descs[0].options.prefix_extractor.reset();
324
1
    ASSERT_OK(
325
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
326
327
    // It's okay to change prefix_extractor from nullptr to non-nullptr
328
1
    cf_descs[0].options.prefix_extractor.reset(new DummySliceTransform());
329
1
    ASSERT_OK(
330
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
331
332
1
    cf_descs[0].options.prefix_extractor = prefix_extractor;
333
1
    ASSERT_OK(
334
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
335
1
  }
336
337
  // comparator
338
1
  {
339
1
    test::SimpleSuffixReverseComparator comparator;
340
341
1
    auto* prev_comparator = cf_descs[2].options.comparator;
342
1
    cf_descs[2].options.comparator = &comparator;
343
1
    ASSERT_NOK(
344
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
345
346
1
    cf_descs[2].options.comparator = prev_comparator;
347
1
    ASSERT_OK(
348
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
349
1
  }
350
351
  // table factory
352
1
  {
353
1
    std::shared_ptr<TableFactory> table_factory =
354
1
        cf_descs[3].options.table_factory;
355
356
1
    ASSERT_NE(table_factory, nullptr);
357
1
    cf_descs[3].options.table_factory.reset(new DummyTableFactory());
358
1
    ASSERT_NOK(
359
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
360
361
1
    cf_descs[3].options.table_factory = table_factory;
362
1
    ASSERT_OK(
363
1
        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));
364
1
  }
365
1
}
366
367
}  // namespace rocksdb
368
369
13.2k
int main(int argc, char** argv) {
370
13.2k
  ::testing::InitGoogleTest(&argc, argv);
371
13.2k
#ifdef GFLAGS
372
13.2k
  ParseCommandLineFlags(&argc, &argv, true);
373
13.2k
#endif  // GFLAGS
374
13.2k
  return RUN_ALL_TESTS();
375
13.2k
}
376
377
#else
378
#include <cstdio>
379
380
int main(int argc, char** argv) {
381
  printf("Skipped in RocksDBLite as utilities are not supported.\n");
382
  return 0;
383
}
384
#endif  // !ROCKSDB_LITE