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
6 #include "rocksdb/utilities/option_change_migration.h"
7
8 #ifndef ROCKSDB_LITE
9 #include "rocksdb/db.h"
10
11 namespace ROCKSDB_NAMESPACE {
12 namespace {
13 // Return a version of Options `opts` that allow us to open/write into a DB
14 // without triggering an automatic compaction or stalling. This is guaranteed
15 // by disabling automatic compactions and using huge values for stalling
16 // triggers.
GetNoCompactionOptions(const Options & opts)17 Options GetNoCompactionOptions(const Options& opts) {
18 Options ret_opts = opts;
19 ret_opts.disable_auto_compactions = true;
20 ret_opts.level0_slowdown_writes_trigger = 999999;
21 ret_opts.level0_stop_writes_trigger = 999999;
22 ret_opts.soft_pending_compaction_bytes_limit = 0;
23 ret_opts.hard_pending_compaction_bytes_limit = 0;
24 return ret_opts;
25 }
26
OpenDb(const Options & options,const std::string & dbname,std::unique_ptr<DB> * db)27 Status OpenDb(const Options& options, const std::string& dbname,
28 std::unique_ptr<DB>* db) {
29 db->reset();
30 DB* tmpdb;
31 Status s = DB::Open(options, dbname, &tmpdb);
32 if (s.ok()) {
33 db->reset(tmpdb);
34 }
35 return s;
36 }
37
CompactToLevel(const Options & options,const std::string & dbname,int dest_level,bool need_reopen)38 Status CompactToLevel(const Options& options, const std::string& dbname,
39 int dest_level, bool need_reopen) {
40 std::unique_ptr<DB> db;
41 Options no_compact_opts = GetNoCompactionOptions(options);
42 if (dest_level == 0) {
43 // L0 has strict sequenceID requirements to files to it. It's safer
44 // to only put one compacted file to there.
45 // This is only used for converting to universal compaction with
46 // only one level. In this case, compacting to one file is also
47 // optimal.
48 no_compact_opts.target_file_size_base = 999999999999999;
49 no_compact_opts.max_compaction_bytes = 999999999999999;
50 }
51 Status s = OpenDb(no_compact_opts, dbname, &db);
52 if (!s.ok()) {
53 return s;
54 }
55 CompactRangeOptions cro;
56 cro.change_level = true;
57 cro.target_level = dest_level;
58 if (dest_level == 0) {
59 // cannot use kForceOptimized because the compaction is expected to
60 // generate one output file
61 cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
62 }
63 db->CompactRange(cro, nullptr, nullptr);
64
65 if (need_reopen) {
66 // Need to restart DB to rewrite the manifest file.
67 // In order to open a DB with specific num_levels, the manifest file should
68 // contain no record that mentiones any level beyond num_levels. Issuing a
69 // full compaction will move all the data to a level not exceeding
70 // num_levels, but the manifest may still contain previous record mentioning
71 // a higher level. Reopening the DB will force the manifest to be rewritten
72 // so that those records will be cleared.
73 db.reset();
74 s = OpenDb(no_compact_opts, dbname, &db);
75 }
76 return s;
77 }
78
MigrateToUniversal(std::string dbname,const Options & old_opts,const Options & new_opts)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, true);
102 }
103 return Status::OK();
104 }
105 }
106
MigrateToLevelBase(std::string dbname,const Options & old_opts,const Options & new_opts)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, 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, false);
131 } else {
132 Options opts = old_opts;
133 opts.target_file_size_base = new_opts.target_file_size_base;
134 return CompactToLevel(opts, dbname, new_opts.num_levels - 1, true);
135 }
136 }
137 }
138 } // namespace
139
OptionChangeMigration(std::string dbname,const Options & old_opts,const Options & new_opts)140 Status OptionChangeMigration(std::string dbname, const Options& old_opts,
141 const Options& new_opts) {
142 if (old_opts.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
143 // LSM generated by FIFO compation can be opened by any compaction.
144 return Status::OK();
145 } else if (new_opts.compaction_style ==
146 CompactionStyle::kCompactionStyleUniversal) {
147 return MigrateToUniversal(dbname, old_opts, new_opts);
148 } else if (new_opts.compaction_style ==
149 CompactionStyle::kCompactionStyleLevel) {
150 return MigrateToLevelBase(dbname, old_opts, new_opts);
151 } else if (new_opts.compaction_style ==
152 CompactionStyle::kCompactionStyleFIFO) {
153 return CompactToLevel(old_opts, dbname, 0, true);
154 } else {
155 return Status::NotSupported(
156 "Do not how to migrate to this compaction style");
157 }
158 }
159 } // namespace ROCKSDB_NAMESPACE
160 #else
161 namespace ROCKSDB_NAMESPACE {
OptionChangeMigration(std::string,const Options &,const Options &)162 Status OptionChangeMigration(std::string /*dbname*/,
163 const Options& /*old_opts*/,
164 const Options& /*new_opts*/) {
165 return Status::NotSupported();
166 }
167 } // namespace ROCKSDB_NAMESPACE
168 #endif // ROCKSDB_LITE
169