options_util_test.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  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. #include "rocksdb/utilities/options_util.h"
  6. #include <cctype>
  7. #include <cinttypes>
  8. #include <unordered_map>
  9. #include "env/mock_env.h"
  10. #include "file/filename.h"
  11. #include "options/options_parser.h"
  12. #include "rocksdb/convenience.h"
  13. #include "rocksdb/db.h"
  14. #include "rocksdb/table.h"
  15. #include "test_util/testharness.h"
  16. #include "test_util/testutil.h"
  17. #include "util/random.h"
  18. #ifndef GFLAGS
  19. bool FLAGS_enable_print = false;
  20. #else
  21. #include "util/gflags_compat.h"
  22. using GFLAGS_NAMESPACE::ParseCommandLineFlags;
  23. DEFINE_bool(enable_print, false, "Print options generated to console.");
  24. #endif // GFLAGS
  25. namespace ROCKSDB_NAMESPACE {
  26. class OptionsUtilTest : public testing::Test {
  27. public:
  28. OptionsUtilTest() : rnd_(0xFB) {
  29. env_.reset(NewMemEnv(Env::Default()));
  30. dbname_ = test::PerThreadDBPath("options_util_test");
  31. }
  32. protected:
  33. std::unique_ptr<Env> env_;
  34. std::string dbname_;
  35. Random rnd_;
  36. };
  37. TEST_F(OptionsUtilTest, SaveAndLoad) {
  38. const size_t kCFCount = 5;
  39. DBOptions db_opt;
  40. std::vector<std::string> cf_names;
  41. std::vector<ColumnFamilyOptions> cf_opts;
  42. test::RandomInitDBOptions(&db_opt, &rnd_);
  43. for (size_t i = 0; i < kCFCount; ++i) {
  44. cf_names.push_back(i == 0 ? kDefaultColumnFamilyName
  45. : test::RandomName(&rnd_, 10));
  46. cf_opts.emplace_back();
  47. test::RandomInitCFOptions(&cf_opts.back(), db_opt, &rnd_);
  48. }
  49. const std::string kFileName = "OPTIONS-123456";
  50. ASSERT_OK(PersistRocksDBOptions(WriteOptions(), db_opt, cf_names, cf_opts,
  51. kFileName, env_->GetFileSystem().get()));
  52. DBOptions loaded_db_opt;
  53. std::vector<ColumnFamilyDescriptor> loaded_cf_descs;
  54. ConfigOptions config_options;
  55. config_options.ignore_unknown_options = false;
  56. config_options.input_strings_escaped = true;
  57. config_options.env = env_.get();
  58. ASSERT_OK(LoadOptionsFromFile(config_options, kFileName, &loaded_db_opt,
  59. &loaded_cf_descs));
  60. ConfigOptions exact;
  61. exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
  62. ASSERT_OK(
  63. RocksDBOptionsParser::VerifyDBOptions(exact, db_opt, loaded_db_opt));
  64. test::RandomInitDBOptions(&db_opt, &rnd_);
  65. ASSERT_NOK(
  66. RocksDBOptionsParser::VerifyDBOptions(exact, db_opt, loaded_db_opt));
  67. for (size_t i = 0; i < kCFCount; ++i) {
  68. ASSERT_EQ(cf_names[i], loaded_cf_descs[i].name);
  69. ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
  70. exact, cf_opts[i], loaded_cf_descs[i].options));
  71. ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory(
  72. exact, cf_opts[i].table_factory.get(),
  73. loaded_cf_descs[i].options.table_factory.get()));
  74. test::RandomInitCFOptions(&cf_opts[i], db_opt, &rnd_);
  75. ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
  76. exact, cf_opts[i], loaded_cf_descs[i].options));
  77. }
  78. ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_opts[0])));
  79. for (size_t i = 0; i < kCFCount; ++i) {
  80. if (cf_opts[i].compaction_filter) {
  81. delete cf_opts[i].compaction_filter;
  82. }
  83. }
  84. }
  85. TEST_F(OptionsUtilTest, SaveAndLoadWithCacheCheck) {
  86. // creating db
  87. DBOptions db_opt;
  88. db_opt.create_if_missing = true;
  89. // initialize BlockBasedTableOptions
  90. std::shared_ptr<Cache> cache = NewLRUCache(1 * 1024);
  91. BlockBasedTableOptions bbt_opts;
  92. bbt_opts.block_size = 32 * 1024;
  93. // saving cf options
  94. std::vector<ColumnFamilyOptions> cf_opts;
  95. ColumnFamilyOptions default_column_family_opt = ColumnFamilyOptions();
  96. default_column_family_opt.table_factory.reset(
  97. NewBlockBasedTableFactory(bbt_opts));
  98. cf_opts.push_back(default_column_family_opt);
  99. ColumnFamilyOptions cf_opt_sample = ColumnFamilyOptions();
  100. cf_opt_sample.table_factory.reset(NewBlockBasedTableFactory(bbt_opts));
  101. cf_opts.push_back(cf_opt_sample);
  102. ColumnFamilyOptions cf_opt_plain_table_opt = ColumnFamilyOptions();
  103. cf_opt_plain_table_opt.table_factory.reset(NewPlainTableFactory());
  104. cf_opts.push_back(cf_opt_plain_table_opt);
  105. std::vector<std::string> cf_names;
  106. cf_names.push_back(kDefaultColumnFamilyName);
  107. cf_names.emplace_back("cf_sample");
  108. cf_names.emplace_back("cf_plain_table_sample");
  109. // Saving DB in file
  110. const std::string kFileName = "OPTIONS-LOAD_CACHE_123456";
  111. ASSERT_OK(PersistRocksDBOptions(WriteOptions(), db_opt, cf_names, cf_opts,
  112. kFileName, env_->GetFileSystem().get()));
  113. DBOptions loaded_db_opt;
  114. std::vector<ColumnFamilyDescriptor> loaded_cf_descs;
  115. ConfigOptions config_options;
  116. config_options.ignore_unknown_options = false;
  117. config_options.input_strings_escaped = true;
  118. config_options.env = env_.get();
  119. ASSERT_OK(LoadOptionsFromFile(config_options, kFileName, &loaded_db_opt,
  120. &loaded_cf_descs, &cache));
  121. for (size_t i = 0; i < loaded_cf_descs.size(); i++) {
  122. auto* loaded_bbt_opt =
  123. loaded_cf_descs[i]
  124. .options.table_factory->GetOptions<BlockBasedTableOptions>();
  125. // Expect the same cache will be loaded
  126. if (loaded_bbt_opt != nullptr) {
  127. ASSERT_EQ(loaded_bbt_opt->block_cache.get(), cache.get());
  128. }
  129. }
  130. ASSERT_OK(DestroyDB(dbname_, Options(loaded_db_opt, cf_opts[0])));
  131. }
  132. namespace {
  133. class DummyTableFactory : public TableFactory {
  134. public:
  135. DummyTableFactory() = default;
  136. ~DummyTableFactory() override = default;
  137. const char* Name() const override { return "DummyTableFactory"; }
  138. using TableFactory::NewTableReader;
  139. Status NewTableReader(
  140. const ReadOptions& /*ro*/,
  141. const TableReaderOptions& /*table_reader_options*/,
  142. std::unique_ptr<RandomAccessFileReader>&& /*file*/,
  143. uint64_t /*file_size*/, std::unique_ptr<TableReader>* /*table_reader*/,
  144. bool /*prefetch_index_and_filter_in_cache*/) const override {
  145. return Status::NotSupported();
  146. }
  147. TableBuilder* NewTableBuilder(
  148. const TableBuilderOptions& /*table_builder_options*/,
  149. WritableFileWriter* /*file*/) const override {
  150. return nullptr;
  151. }
  152. Status ValidateOptions(
  153. const DBOptions& /*db_opts*/,
  154. const ColumnFamilyOptions& /*cf_opts*/) const override {
  155. return Status::NotSupported();
  156. }
  157. std::string GetPrintableOptions() const override { return ""; }
  158. std::unique_ptr<TableFactory> Clone() const override { return nullptr; }
  159. };
  160. class DummyMergeOperator : public MergeOperator {
  161. public:
  162. DummyMergeOperator() = default;
  163. ~DummyMergeOperator() override = default;
  164. bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
  165. MergeOperationOutput* /*merge_out*/) const override {
  166. return false;
  167. }
  168. bool PartialMergeMulti(const Slice& /*key*/,
  169. const std::deque<Slice>& /*operand_list*/,
  170. std::string* /*new_value*/,
  171. Logger* /*logger*/) const override {
  172. return false;
  173. }
  174. const char* Name() const override { return "DummyMergeOperator"; }
  175. };
  176. class DummySliceTransform : public SliceTransform {
  177. public:
  178. DummySliceTransform() = default;
  179. ~DummySliceTransform() override = default;
  180. // Return the name of this transformation.
  181. const char* Name() const override { return "DummySliceTransform"; }
  182. // transform a src in domain to a dst in the range
  183. Slice Transform(const Slice& src) const override { return src; }
  184. // determine whether this is a valid src upon the function applies
  185. bool InDomain(const Slice& /*src*/) const override { return false; }
  186. // determine whether dst=Transform(src) for some src
  187. bool InRange(const Slice& /*dst*/) const override { return false; }
  188. };
  189. } // namespace
  190. TEST_F(OptionsUtilTest, SanityCheck) {
  191. DBOptions db_opt;
  192. std::vector<ColumnFamilyDescriptor> cf_descs;
  193. const size_t kCFCount = 5;
  194. for (size_t i = 0; i < kCFCount; ++i) {
  195. cf_descs.emplace_back();
  196. cf_descs.back().name =
  197. (i == 0) ? kDefaultColumnFamilyName : test::RandomName(&rnd_, 10);
  198. cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory());
  199. // Assign non-null values to prefix_extractors except the first cf.
  200. cf_descs.back().options.prefix_extractor.reset(
  201. i != 0 ? test::RandomSliceTransform(&rnd_) : nullptr);
  202. cf_descs.back().options.merge_operator.reset(
  203. test::RandomMergeOperator(&rnd_));
  204. }
  205. db_opt.create_missing_column_families = true;
  206. db_opt.create_if_missing = true;
  207. ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_descs[0].options)));
  208. DB* db;
  209. std::vector<ColumnFamilyHandle*> handles;
  210. // open and persist the options
  211. ASSERT_OK(DB::Open(db_opt, dbname_, cf_descs, &handles, &db));
  212. // close the db
  213. for (auto* handle : handles) {
  214. delete handle;
  215. }
  216. delete db;
  217. ConfigOptions config_options;
  218. config_options.ignore_unknown_options = false;
  219. config_options.input_strings_escaped = true;
  220. config_options.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
  221. // perform sanity check
  222. ASSERT_OK(
  223. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  224. ASSERT_GE(kCFCount, 5);
  225. // merge operator
  226. {
  227. std::shared_ptr<MergeOperator> merge_op =
  228. cf_descs[0].options.merge_operator;
  229. ASSERT_NE(merge_op.get(), nullptr);
  230. cf_descs[0].options.merge_operator.reset();
  231. ASSERT_NOK(
  232. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  233. cf_descs[0].options.merge_operator.reset(new DummyMergeOperator());
  234. ASSERT_NOK(
  235. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  236. cf_descs[0].options.merge_operator = merge_op;
  237. ASSERT_OK(
  238. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  239. }
  240. // prefix extractor
  241. {
  242. std::shared_ptr<const SliceTransform> prefix_extractor =
  243. cf_descs[1].options.prefix_extractor;
  244. // It's okay to set prefix_extractor to nullptr.
  245. ASSERT_NE(prefix_extractor, nullptr);
  246. cf_descs[1].options.prefix_extractor.reset();
  247. ASSERT_OK(
  248. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  249. cf_descs[1].options.prefix_extractor.reset(new DummySliceTransform());
  250. ASSERT_OK(
  251. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  252. cf_descs[1].options.prefix_extractor = prefix_extractor;
  253. ASSERT_OK(
  254. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  255. }
  256. // prefix extractor nullptr case
  257. {
  258. std::shared_ptr<const SliceTransform> prefix_extractor =
  259. cf_descs[0].options.prefix_extractor;
  260. // It's okay to set prefix_extractor to nullptr.
  261. ASSERT_EQ(prefix_extractor, nullptr);
  262. cf_descs[0].options.prefix_extractor.reset();
  263. ASSERT_OK(
  264. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  265. // It's okay to change prefix_extractor from nullptr to non-nullptr
  266. cf_descs[0].options.prefix_extractor.reset(new DummySliceTransform());
  267. ASSERT_OK(
  268. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  269. cf_descs[0].options.prefix_extractor = prefix_extractor;
  270. ASSERT_OK(
  271. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  272. }
  273. // comparator
  274. {
  275. test::SimpleSuffixReverseComparator comparator;
  276. auto* prev_comparator = cf_descs[2].options.comparator;
  277. cf_descs[2].options.comparator = &comparator;
  278. ASSERT_NOK(
  279. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  280. cf_descs[2].options.comparator = prev_comparator;
  281. ASSERT_OK(
  282. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  283. }
  284. // table factory
  285. {
  286. std::shared_ptr<TableFactory> table_factory =
  287. cf_descs[3].options.table_factory;
  288. ASSERT_NE(table_factory, nullptr);
  289. cf_descs[3].options.table_factory.reset(new DummyTableFactory());
  290. ASSERT_NOK(
  291. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  292. cf_descs[3].options.table_factory = table_factory;
  293. ASSERT_OK(
  294. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  295. }
  296. // persist_user_defined_timestamps
  297. {
  298. bool prev_persist_user_defined_timestamps =
  299. cf_descs[2].options.persist_user_defined_timestamps;
  300. cf_descs[2].options.persist_user_defined_timestamps = false;
  301. ASSERT_NOK(
  302. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  303. cf_descs[2].options.persist_user_defined_timestamps =
  304. prev_persist_user_defined_timestamps;
  305. ASSERT_OK(
  306. CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
  307. }
  308. ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_descs[0].options)));
  309. }
  310. TEST_F(OptionsUtilTest, LatestOptionsNotFound) {
  311. std::unique_ptr<Env> env(NewMemEnv(Env::Default()));
  312. Status s;
  313. Options options;
  314. ConfigOptions config_opts;
  315. std::vector<ColumnFamilyDescriptor> cf_descs;
  316. options.env = env.get();
  317. options.create_if_missing = true;
  318. config_opts.env = options.env;
  319. config_opts.ignore_unknown_options = false;
  320. std::vector<std::string> children;
  321. std::string options_file_name;
  322. ASSERT_OK(DestroyDB(dbname_, options));
  323. // First, test where the db directory does not exist
  324. ASSERT_NOK(options.env->GetChildren(dbname_, &children));
  325. s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
  326. ASSERT_TRUE(s.IsNotFound());
  327. ASSERT_TRUE(s.IsPathNotFound());
  328. s = LoadLatestOptions(config_opts, dbname_, &options, &cf_descs);
  329. ASSERT_TRUE(s.IsNotFound());
  330. ASSERT_TRUE(s.IsPathNotFound());
  331. s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
  332. ASSERT_TRUE(s.IsNotFound());
  333. ASSERT_TRUE(s.IsPathNotFound());
  334. // Second, test where the db directory exists but is empty
  335. ASSERT_OK(options.env->CreateDir(dbname_));
  336. s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
  337. ASSERT_TRUE(s.IsNotFound());
  338. ASSERT_TRUE(s.IsPathNotFound());
  339. s = LoadLatestOptions(config_opts, dbname_, &options, &cf_descs);
  340. ASSERT_TRUE(s.IsNotFound());
  341. ASSERT_TRUE(s.IsPathNotFound());
  342. // Finally, test where a file exists but is not an "Options" file
  343. std::unique_ptr<WritableFile> file;
  344. ASSERT_OK(
  345. options.env->NewWritableFile(dbname_ + "/temp.txt", &file, EnvOptions()));
  346. ASSERT_OK(file->Close());
  347. s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
  348. ASSERT_TRUE(s.IsNotFound());
  349. ASSERT_TRUE(s.IsPathNotFound());
  350. s = LoadLatestOptions(config_opts, dbname_, &options, &cf_descs);
  351. ASSERT_TRUE(s.IsNotFound());
  352. ASSERT_TRUE(s.IsPathNotFound());
  353. ASSERT_OK(options.env->DeleteFile(dbname_ + "/temp.txt"));
  354. ASSERT_OK(options.env->DeleteDir(dbname_));
  355. }
  356. TEST_F(OptionsUtilTest, LoadLatestOptions) {
  357. Options options;
  358. options.OptimizeForSmallDb();
  359. ColumnFamilyDescriptor cf_desc;
  360. ConfigOptions config_opts;
  361. DBOptions db_opts;
  362. std::vector<ColumnFamilyDescriptor> cf_descs;
  363. std::vector<ColumnFamilyHandle*> handles;
  364. DB* db;
  365. options.create_if_missing = true;
  366. ASSERT_OK(DestroyDB(dbname_, options));
  367. cf_descs.emplace_back();
  368. cf_descs.back().name = kDefaultColumnFamilyName;
  369. cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory());
  370. cf_descs.emplace_back();
  371. cf_descs.back().name = "Plain";
  372. cf_descs.back().options.table_factory.reset(NewPlainTableFactory());
  373. db_opts.create_missing_column_families = true;
  374. db_opts.create_if_missing = true;
  375. // open and persist the options
  376. ASSERT_OK(DB::Open(db_opts, dbname_, cf_descs, &handles, &db));
  377. std::string options_file_name;
  378. std::string new_options_file;
  379. ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &options_file_name));
  380. ASSERT_OK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  381. ASSERT_EQ(cf_descs.size(), 2U);
  382. ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_opts,
  383. db->GetDBOptions(), db_opts));
  384. ASSERT_OK(handles[0]->GetDescriptor(&cf_desc));
  385. ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
  386. cf_descs[0].options));
  387. ASSERT_OK(handles[1]->GetDescriptor(&cf_desc));
  388. ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
  389. cf_descs[1].options));
  390. // Now change some of the DBOptions
  391. ASSERT_OK(db->SetDBOptions(
  392. {{"delayed_write_rate", "1234"}, {"bytes_per_sync", "32768"}}));
  393. ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &new_options_file));
  394. ASSERT_NE(options_file_name, new_options_file);
  395. ASSERT_OK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  396. ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_opts,
  397. db->GetDBOptions(), db_opts));
  398. options_file_name = new_options_file;
  399. // Now change some of the ColumnFamilyOptions
  400. ASSERT_OK(db->SetOptions(handles[1], {{"write_buffer_size", "32768"}}));
  401. ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &new_options_file));
  402. ASSERT_NE(options_file_name, new_options_file);
  403. ASSERT_OK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  404. ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_opts,
  405. db->GetDBOptions(), db_opts));
  406. ASSERT_OK(handles[0]->GetDescriptor(&cf_desc));
  407. ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
  408. cf_descs[0].options));
  409. ASSERT_OK(handles[1]->GetDescriptor(&cf_desc));
  410. ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
  411. cf_descs[1].options));
  412. // close the db
  413. for (auto* handle : handles) {
  414. delete handle;
  415. }
  416. delete db;
  417. ASSERT_OK(DestroyDB(dbname_, options, cf_descs));
  418. }
  419. static void WriteOptionsFile(Env* env, const std::string& path,
  420. const std::string& options_file, int major,
  421. int minor, const std::string& db_opts,
  422. const std::string& cf_opts,
  423. const std::string& bbt_opts = "") {
  424. std::string options_file_header =
  425. "\n"
  426. "[Version]\n"
  427. " rocksdb_version=" +
  428. std::to_string(major) + "." + std::to_string(minor) +
  429. ".0\n"
  430. " options_file_version=1\n";
  431. std::unique_ptr<WritableFile> wf;
  432. ASSERT_OK(env->NewWritableFile(path + "/" + options_file, &wf, EnvOptions()));
  433. ASSERT_OK(
  434. wf->Append(options_file_header + "[ DBOptions ]\n" + db_opts + "\n"));
  435. ASSERT_OK(wf->Append(
  436. "[CFOptions \"default\"] # column family must be specified\n" +
  437. cf_opts + "\n"));
  438. ASSERT_OK(wf->Append("[TableOptions/BlockBasedTable \"default\"]\n" +
  439. bbt_opts + "\n"));
  440. ASSERT_OK(wf->Close());
  441. std::string latest_options_file;
  442. ASSERT_OK(GetLatestOptionsFileName(path, env, &latest_options_file));
  443. ASSERT_EQ(latest_options_file, options_file);
  444. }
  445. TEST_F(OptionsUtilTest, BadLatestOptions) {
  446. Status s;
  447. ConfigOptions config_opts;
  448. DBOptions db_opts;
  449. std::vector<ColumnFamilyDescriptor> cf_descs;
  450. Options options;
  451. options.env = env_.get();
  452. config_opts.env = env_.get();
  453. config_opts.ignore_unknown_options = false;
  454. config_opts.delimiter = "\n";
  455. ConfigOptions ignore_opts = config_opts;
  456. ignore_opts.ignore_unknown_options = true;
  457. std::string options_file_name;
  458. // Test where the db directory exists but is empty
  459. ASSERT_OK(options.env->CreateDir(dbname_));
  460. ASSERT_NOK(
  461. GetLatestOptionsFileName(dbname_, options.env, &options_file_name));
  462. ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  463. // Write an options file for a previous major release with an unknown DB
  464. // Option
  465. WriteOptionsFile(options.env, dbname_, "OPTIONS-0001", ROCKSDB_MAJOR - 1,
  466. ROCKSDB_MINOR, "unknown_db_opt=true", "");
  467. s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
  468. ASSERT_NOK(s);
  469. ASSERT_TRUE(s.IsInvalidArgument());
  470. // Even though ignore_unknown_options=true, we still return an error...
  471. s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
  472. ASSERT_NOK(s);
  473. ASSERT_TRUE(s.IsInvalidArgument());
  474. // Write an options file for a previous minor release with an unknown CF
  475. // Option
  476. WriteOptionsFile(options.env, dbname_, "OPTIONS-0002", ROCKSDB_MAJOR,
  477. ROCKSDB_MINOR - 1, "", "unknown_cf_opt=true");
  478. s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
  479. ASSERT_NOK(s);
  480. ASSERT_TRUE(s.IsInvalidArgument());
  481. // Even though ignore_unknown_options=true, we still return an error...
  482. s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
  483. ASSERT_NOK(s);
  484. ASSERT_TRUE(s.IsInvalidArgument());
  485. // Write an options file for a previous minor release with an unknown BBT
  486. // Option
  487. WriteOptionsFile(options.env, dbname_, "OPTIONS-0003", ROCKSDB_MAJOR,
  488. ROCKSDB_MINOR - 1, "", "", "unknown_bbt_opt=true");
  489. s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
  490. ASSERT_NOK(s);
  491. ASSERT_TRUE(s.IsInvalidArgument());
  492. // Even though ignore_unknown_options=true, we still return an error...
  493. s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
  494. ASSERT_NOK(s);
  495. ASSERT_TRUE(s.IsInvalidArgument());
  496. // Write an options file for the current release with an unknown DB Option
  497. WriteOptionsFile(options.env, dbname_, "OPTIONS-0004", ROCKSDB_MAJOR,
  498. ROCKSDB_MINOR, "unknown_db_opt=true", "");
  499. s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
  500. ASSERT_NOK(s);
  501. ASSERT_TRUE(s.IsInvalidArgument());
  502. // Even though ignore_unknown_options=true, we still return an error...
  503. s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
  504. ASSERT_NOK(s);
  505. ASSERT_TRUE(s.IsInvalidArgument());
  506. // Write an options file for the current release with an unknown CF Option
  507. WriteOptionsFile(options.env, dbname_, "OPTIONS-0005", ROCKSDB_MAJOR,
  508. ROCKSDB_MINOR, "", "unknown_cf_opt=true");
  509. s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
  510. ASSERT_NOK(s);
  511. ASSERT_TRUE(s.IsInvalidArgument());
  512. // Even though ignore_unknown_options=true, we still return an error...
  513. s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
  514. ASSERT_NOK(s);
  515. ASSERT_TRUE(s.IsInvalidArgument());
  516. // Write an options file for the current release with an invalid DB Option
  517. WriteOptionsFile(options.env, dbname_, "OPTIONS-0006", ROCKSDB_MAJOR,
  518. ROCKSDB_MINOR, "create_if_missing=hello", "");
  519. s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
  520. ASSERT_NOK(s);
  521. ASSERT_TRUE(s.IsInvalidArgument());
  522. // Even though ignore_unknown_options=true, we still return an error...
  523. s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
  524. ASSERT_NOK(s);
  525. ASSERT_TRUE(s.IsInvalidArgument());
  526. // Write an options file for the next release with an invalid DB Option
  527. WriteOptionsFile(options.env, dbname_, "OPTIONS-0007", ROCKSDB_MAJOR,
  528. ROCKSDB_MINOR + 1, "create_if_missing=hello", "");
  529. ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  530. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  531. // Write an options file for the next release with an unknown DB Option
  532. WriteOptionsFile(options.env, dbname_, "OPTIONS-0008", ROCKSDB_MAJOR,
  533. ROCKSDB_MINOR + 1, "unknown_db_opt=true", "");
  534. ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  535. // Ignore the errors for future releases when ignore_unknown_options=true
  536. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  537. // Write an options file for the next major release with an unknown CF Option
  538. WriteOptionsFile(options.env, dbname_, "OPTIONS-0009", ROCKSDB_MAJOR + 1,
  539. ROCKSDB_MINOR, "", "unknown_cf_opt=true");
  540. ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
  541. // Ignore the errors for future releases when ignore_unknown_options=true
  542. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  543. }
  544. TEST_F(OptionsUtilTest, RenameDatabaseDirectory) {
  545. DB* db;
  546. Options options;
  547. DBOptions db_opts;
  548. std::vector<ColumnFamilyDescriptor> cf_descs;
  549. std::vector<ColumnFamilyHandle*> handles;
  550. ConfigOptions ignore_opts;
  551. ignore_opts.ignore_unknown_options = false;
  552. ignore_opts.env = options.env;
  553. options.create_if_missing = true;
  554. ASSERT_OK(DB::Open(options, dbname_, &db));
  555. ASSERT_OK(db->Put(WriteOptions(), "foo", "value0"));
  556. delete db;
  557. auto new_dbname = dbname_ + "_2";
  558. ASSERT_OK(options.env->RenameFile(dbname_, new_dbname));
  559. ASSERT_OK(LoadLatestOptions(ignore_opts, new_dbname, &db_opts, &cf_descs));
  560. ASSERT_EQ(cf_descs.size(), 1U);
  561. db_opts.create_if_missing = false;
  562. ASSERT_OK(DB::Open(db_opts, new_dbname, cf_descs, &handles, &db));
  563. std::string value;
  564. ASSERT_OK(db->Get(ReadOptions(), "foo", &value));
  565. ASSERT_EQ("value0", value);
  566. // close the db
  567. for (auto* handle : handles) {
  568. delete handle;
  569. }
  570. delete db;
  571. Options new_options(db_opts, cf_descs[0].options);
  572. ASSERT_OK(DestroyDB(new_dbname, new_options, cf_descs));
  573. ASSERT_OK(DestroyDB(dbname_, options));
  574. }
  575. TEST_F(OptionsUtilTest, WalDirSettings) {
  576. DB* db;
  577. Options options;
  578. DBOptions db_opts;
  579. std::vector<ColumnFamilyDescriptor> cf_descs;
  580. std::vector<ColumnFamilyHandle*> handles;
  581. ConfigOptions ignore_opts;
  582. ignore_opts.ignore_unknown_options = false;
  583. ignore_opts.env = options.env;
  584. options.create_if_missing = true;
  585. // Open a DB with no wal dir set. The wal_dir should stay empty
  586. ASSERT_OK(DB::Open(options, dbname_, &db));
  587. delete db;
  588. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  589. ASSERT_EQ(db_opts.wal_dir, "");
  590. // Open a DB with wal_dir == dbname. The wal_dir should be set to empty
  591. options.wal_dir = dbname_;
  592. ASSERT_OK(DB::Open(options, dbname_, &db));
  593. delete db;
  594. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  595. ASSERT_EQ(db_opts.wal_dir, "");
  596. // Open a DB with no wal_dir but a db_path==dbname_. The wal_dir should be
  597. // empty
  598. options.wal_dir = "";
  599. options.db_paths.emplace_back(dbname_, std::numeric_limits<uint64_t>::max());
  600. ASSERT_OK(DB::Open(options, dbname_, &db));
  601. delete db;
  602. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  603. ASSERT_EQ(db_opts.wal_dir, "");
  604. // Open a DB with no wal_dir==dbname_ and db_path==dbname_. The wal_dir
  605. // should be empty
  606. options.wal_dir = dbname_ + "/";
  607. options.db_paths.emplace_back(dbname_, std::numeric_limits<uint64_t>::max());
  608. ASSERT_OK(DB::Open(options, dbname_, &db));
  609. delete db;
  610. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  611. ASSERT_EQ(db_opts.wal_dir, "");
  612. ASSERT_OK(DestroyDB(dbname_, options));
  613. // Open a DB with no wal_dir but db_path != db_name. The wal_dir == dbname_
  614. options.wal_dir = "";
  615. options.db_paths.clear();
  616. options.db_paths.emplace_back(dbname_ + "_0",
  617. std::numeric_limits<uint64_t>::max());
  618. ASSERT_OK(DB::Open(options, dbname_, &db));
  619. delete db;
  620. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  621. ASSERT_EQ(db_opts.wal_dir, dbname_);
  622. ASSERT_OK(DestroyDB(dbname_, options));
  623. // Open a DB with wal_dir != db_name. The wal_dir remains unchanged
  624. options.wal_dir = dbname_ + "/wal";
  625. options.db_paths.clear();
  626. ASSERT_OK(DB::Open(options, dbname_, &db));
  627. delete db;
  628. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  629. ASSERT_EQ(db_opts.wal_dir, dbname_ + "/wal");
  630. ASSERT_OK(DestroyDB(dbname_, options));
  631. }
  632. TEST_F(OptionsUtilTest, WalDirInOptins) {
  633. DB* db;
  634. Options options;
  635. DBOptions db_opts;
  636. std::vector<ColumnFamilyDescriptor> cf_descs;
  637. std::vector<ColumnFamilyHandle*> handles;
  638. ConfigOptions ignore_opts;
  639. ignore_opts.ignore_unknown_options = false;
  640. ignore_opts.env = options.env;
  641. // Store an options file with wal_dir=dbname_ and make sure it still loads
  642. // when the input wal_dir is empty
  643. options.create_if_missing = true;
  644. options.wal_dir = "";
  645. ASSERT_OK(DB::Open(options, dbname_, &db));
  646. delete db;
  647. options.wal_dir = dbname_;
  648. std::string options_file;
  649. ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &options_file));
  650. ASSERT_OK(PersistRocksDBOptions(WriteOptions(), options, {"default"},
  651. {options}, dbname_ + "/" + options_file,
  652. options.env->GetFileSystem().get()));
  653. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  654. ASSERT_EQ(db_opts.wal_dir, dbname_);
  655. options.wal_dir = "";
  656. ASSERT_OK(DB::Open(options, dbname_, &db));
  657. delete db;
  658. ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
  659. ASSERT_EQ(db_opts.wal_dir, "");
  660. }
  661. } // namespace ROCKSDB_NAMESPACE
  662. int main(int argc, char** argv) {
  663. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  664. ::testing::InitGoogleTest(&argc, argv);
  665. #ifdef GFLAGS
  666. ParseCommandLineFlags(&argc, &argv, true);
  667. #endif // GFLAGS
  668. return RUN_ALL_TESTS();
  669. }