]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
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). | |
7c673cae FG |
5 | |
6 | #include "rocksdb/utilities/option_change_migration.h" | |
7 | ||
8 | #ifndef ROCKSDB_LITE | |
9 | #include "rocksdb/db.h" | |
10 | ||
11 | namespace rocksdb { | |
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. | |
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 | ||
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 | ||
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 | cro.bottommost_level_compaction = BottommostLevelCompaction::kForce; | |
60 | } | |
61 | db->CompactRange(cro, nullptr, nullptr); | |
62 | ||
63 | if (need_reopen) { | |
64 | // Need to restart DB to rewrite the manifest file. | |
65 | // In order to open a DB with specific num_levels, the manifest file should | |
66 | // contain no record that mentiones any level beyond num_levels. Issuing a | |
67 | // full compaction will move all the data to a level not exceeding | |
68 | // num_levels, but the manifest may still contain previous record mentioning | |
69 | // a higher level. Reopening the DB will force the manifest to be rewritten | |
70 | // so that those records will be cleared. | |
71 | db.reset(); | |
72 | s = OpenDb(no_compact_opts, dbname, &db); | |
73 | } | |
74 | return s; | |
75 | } | |
76 | ||
77 | Status MigrateToUniversal(std::string dbname, const Options& old_opts, | |
78 | const Options& new_opts) { | |
79 | if (old_opts.num_levels <= new_opts.num_levels || | |
80 | old_opts.compaction_style == CompactionStyle::kCompactionStyleFIFO) { | |
81 | return Status::OK(); | |
82 | } else { | |
83 | bool need_compact = false; | |
84 | { | |
85 | std::unique_ptr<DB> db; | |
86 | Options opts = GetNoCompactionOptions(old_opts); | |
87 | Status s = OpenDb(opts, dbname, &db); | |
88 | if (!s.ok()) { | |
89 | return s; | |
90 | } | |
91 | ColumnFamilyMetaData metadata; | |
92 | db->GetColumnFamilyMetaData(&metadata); | |
93 | if (!metadata.levels.empty() && | |
94 | metadata.levels.back().level >= new_opts.num_levels) { | |
95 | need_compact = true; | |
96 | } | |
97 | } | |
98 | if (need_compact) { | |
99 | return CompactToLevel(old_opts, dbname, new_opts.num_levels - 1, true); | |
100 | } | |
101 | return Status::OK(); | |
102 | } | |
103 | } | |
104 | ||
105 | Status MigrateToLevelBase(std::string dbname, const Options& old_opts, | |
106 | const Options& new_opts) { | |
107 | if (!new_opts.level_compaction_dynamic_level_bytes) { | |
108 | if (old_opts.num_levels == 1) { | |
109 | return Status::OK(); | |
110 | } | |
111 | // Compact everything to level 1 to guarantee it can be safely opened. | |
112 | Options opts = old_opts; | |
113 | opts.target_file_size_base = new_opts.target_file_size_base; | |
114 | // Although sometimes we can open the DB with the new option without error, | |
115 | // We still want to compact the files to avoid the LSM tree to stuck | |
116 | // in bad shape. For example, if the user changed the level size | |
117 | // multiplier from 4 to 8, with the same data, we will have fewer | |
118 | // levels. Unless we issue a full comaction, the LSM tree may stuck | |
119 | // with more levels than needed and it won't recover automatically. | |
120 | return CompactToLevel(opts, dbname, 1, true); | |
121 | } else { | |
122 | // Compact everything to the last level to guarantee it can be safely | |
123 | // opened. | |
124 | if (old_opts.num_levels == 1) { | |
125 | return Status::OK(); | |
126 | } else if (new_opts.num_levels > old_opts.num_levels) { | |
127 | // Dynamic level mode requires data to be put in the last level first. | |
128 | return CompactToLevel(new_opts, dbname, new_opts.num_levels - 1, false); | |
129 | } else { | |
130 | Options opts = old_opts; | |
131 | opts.target_file_size_base = new_opts.target_file_size_base; | |
132 | return CompactToLevel(opts, dbname, new_opts.num_levels - 1, true); | |
133 | } | |
134 | } | |
135 | } | |
136 | } // namespace | |
137 | ||
138 | Status OptionChangeMigration(std::string dbname, const Options& old_opts, | |
139 | const Options& new_opts) { | |
140 | if (old_opts.compaction_style == CompactionStyle::kCompactionStyleFIFO) { | |
141 | // LSM generated by FIFO compation can be opened by any compaction. | |
142 | return Status::OK(); | |
143 | } else if (new_opts.compaction_style == | |
144 | CompactionStyle::kCompactionStyleUniversal) { | |
145 | return MigrateToUniversal(dbname, old_opts, new_opts); | |
146 | } else if (new_opts.compaction_style == | |
147 | CompactionStyle::kCompactionStyleLevel) { | |
148 | return MigrateToLevelBase(dbname, old_opts, new_opts); | |
149 | } else if (new_opts.compaction_style == | |
150 | CompactionStyle::kCompactionStyleFIFO) { | |
151 | return CompactToLevel(old_opts, dbname, 0, true); | |
152 | } else { | |
153 | return Status::NotSupported( | |
154 | "Do not how to migrate to this compaction style"); | |
155 | } | |
156 | } | |
157 | } // namespace rocksdb | |
158 | #else | |
159 | namespace rocksdb { | |
11fdf7f2 TL |
160 | Status OptionChangeMigration(std::string /*dbname*/, |
161 | const Options& /*old_opts*/, | |
162 | const Options& /*new_opts*/) { | |
7c673cae FG |
163 | return Status::NotSupported(); |
164 | } | |
165 | } // namespace rocksdb | |
166 | #endif // ROCKSDB_LITE |