options_util_test.cc 12 KB

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