option_change_migration.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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/option_change_migration.h"
  6. #include "rocksdb/db.h"
  7. namespace ROCKSDB_NAMESPACE {
  8. namespace {
  9. // Return a version of Options `opts` that allow us to open/write into a DB
  10. // without triggering an automatic compaction or stalling. This is guaranteed
  11. // by disabling automatic compactions and using huge values for stalling
  12. // triggers.
  13. Options GetNoCompactionOptions(const Options& opts) {
  14. Options ret_opts = opts;
  15. ret_opts.disable_auto_compactions = true;
  16. ret_opts.level0_slowdown_writes_trigger = 999999;
  17. ret_opts.level0_stop_writes_trigger = 999999;
  18. ret_opts.soft_pending_compaction_bytes_limit = 0;
  19. ret_opts.hard_pending_compaction_bytes_limit = 0;
  20. return ret_opts;
  21. }
  22. Status OpenDb(const Options& options, const std::string& dbname,
  23. std::unique_ptr<DB>* db) {
  24. db->reset();
  25. DB* tmpdb;
  26. Status s = DB::Open(options, dbname, &tmpdb);
  27. if (s.ok()) {
  28. db->reset(tmpdb);
  29. }
  30. return s;
  31. }
  32. // l0_file_size specifies size of file on L0. Files will be range partitioned
  33. // after a full compaction so they are likely qualified to put on L0. If
  34. // left as 0, the files are compacted in a single file and put to L0. Otherwise,
  35. // will try to compact the files as size l0_file_size.
  36. Status CompactToLevel(const Options& options, const std::string& dbname,
  37. int dest_level, uint64_t l0_file_size, bool need_reopen) {
  38. std::unique_ptr<DB> db;
  39. Options no_compact_opts = GetNoCompactionOptions(options);
  40. if (dest_level == 0) {
  41. if (l0_file_size == 0) {
  42. // Single file.
  43. l0_file_size = 999999999999999;
  44. }
  45. // L0 has strict sequenceID requirements to files to it. It's safer
  46. // to only put one compacted file to there.
  47. // This is only used for converting to universal compaction with
  48. // only one level. In this case, compacting to one file is also
  49. // optimal.
  50. no_compact_opts.target_file_size_base = l0_file_size;
  51. no_compact_opts.max_compaction_bytes = l0_file_size;
  52. }
  53. Status s = OpenDb(no_compact_opts, dbname, &db);
  54. if (!s.ok()) {
  55. return s;
  56. }
  57. CompactRangeOptions cro;
  58. cro.change_level = true;
  59. cro.target_level = dest_level;
  60. if (dest_level == 0) {
  61. // cannot use kForceOptimized because the compaction is expected to
  62. // generate one output file
  63. cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
  64. }
  65. s = db->CompactRange(cro, nullptr, nullptr);
  66. if (s.ok() && need_reopen) {
  67. // Need to restart DB to rewrite the manifest file.
  68. // In order to open a DB with specific num_levels, the manifest file should
  69. // contain no record that mentiones any level beyond num_levels. Issuing a
  70. // full compaction will move all the data to a level not exceeding
  71. // num_levels, but the manifest may still contain previous record mentioning
  72. // a higher level. Reopening the DB will force the manifest to be rewritten
  73. // so that those records will be cleared.
  74. db.reset();
  75. s = OpenDb(no_compact_opts, dbname, &db);
  76. }
  77. return s;
  78. }
  79. Status MigrateToUniversal(std::string dbname, const Options& old_opts,
  80. const Options& new_opts) {
  81. if (old_opts.num_levels <= new_opts.num_levels ||
  82. old_opts.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
  83. return Status::OK();
  84. } else {
  85. bool need_compact = false;
  86. {
  87. std::unique_ptr<DB> db;
  88. Options opts = GetNoCompactionOptions(old_opts);
  89. Status s = OpenDb(opts, dbname, &db);
  90. if (!s.ok()) {
  91. return s;
  92. }
  93. ColumnFamilyMetaData metadata;
  94. db->GetColumnFamilyMetaData(&metadata);
  95. if (!metadata.levels.empty() &&
  96. metadata.levels.back().level >= new_opts.num_levels) {
  97. need_compact = true;
  98. }
  99. }
  100. if (need_compact) {
  101. return CompactToLevel(old_opts, dbname, new_opts.num_levels - 1,
  102. /*l0_file_size=*/0, true);
  103. }
  104. return Status::OK();
  105. }
  106. }
  107. Status MigrateToLevelBase(std::string dbname, const Options& old_opts,
  108. const Options& new_opts) {
  109. if (!new_opts.level_compaction_dynamic_level_bytes) {
  110. if (old_opts.num_levels == 1) {
  111. return Status::OK();
  112. }
  113. // Compact everything to level 1 to guarantee it can be safely opened.
  114. Options opts = old_opts;
  115. opts.target_file_size_base = new_opts.target_file_size_base;
  116. // Although sometimes we can open the DB with the new option without error,
  117. // We still want to compact the files to avoid the LSM tree to stuck
  118. // in bad shape. For example, if the user changed the level size
  119. // multiplier from 4 to 8, with the same data, we will have fewer
  120. // levels. Unless we issue a full comaction, the LSM tree may stuck
  121. // with more levels than needed and it won't recover automatically.
  122. return CompactToLevel(opts, dbname, 1, /*l0_file_size=*/0, true);
  123. } else {
  124. // Compact everything to the last level to guarantee it can be safely
  125. // opened.
  126. if (old_opts.num_levels == 1) {
  127. return Status::OK();
  128. } else if (new_opts.num_levels > old_opts.num_levels) {
  129. // Dynamic level mode requires data to be put in the last level first.
  130. return CompactToLevel(new_opts, dbname, new_opts.num_levels - 1,
  131. /*l0_file_size=*/0, false);
  132. } else {
  133. Options opts = old_opts;
  134. opts.target_file_size_base = new_opts.target_file_size_base;
  135. return CompactToLevel(opts, dbname, new_opts.num_levels - 1,
  136. /*l0_file_size=*/0, true);
  137. }
  138. }
  139. }
  140. } // namespace
  141. Status OptionChangeMigration(std::string dbname, const Options& old_opts,
  142. const Options& new_opts) {
  143. if (old_opts.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
  144. // LSM generated by FIFO compaction can be opened by any compaction.
  145. return Status::OK();
  146. } else if (new_opts.compaction_style ==
  147. CompactionStyle::kCompactionStyleUniversal) {
  148. return MigrateToUniversal(dbname, old_opts, new_opts);
  149. } else if (new_opts.compaction_style ==
  150. CompactionStyle::kCompactionStyleLevel) {
  151. return MigrateToLevelBase(dbname, old_opts, new_opts);
  152. } else if (new_opts.compaction_style ==
  153. CompactionStyle::kCompactionStyleFIFO) {
  154. return CompactToLevel(old_opts, dbname, 0, 0 /* l0_file_size */, true);
  155. } else {
  156. return Status::NotSupported(
  157. "Do not how to migrate to this compaction style");
  158. }
  159. }
  160. } // namespace ROCKSDB_NAMESPACE