/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/compaction.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under the BSD-style license found in the |
3 | | // LICENSE file in the root directory of this source tree. An additional grant |
4 | | // of patent rights can be found in the PATENTS file in the same directory. |
5 | | // |
6 | | // The following only applies to changes made to this file as part of YugaByte development. |
7 | | // |
8 | | // Portions Copyright (c) YugaByte, Inc. |
9 | | // |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
11 | | // in compliance with the License. You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
16 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
17 | | // or implied. See the License for the specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
21 | | // Use of this source code is governed by a BSD-style license that can be |
22 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
23 | | |
24 | | #include "yb/rocksdb/db/compaction.h" |
25 | | |
26 | | #ifndef __STDC_FORMAT_MACROS |
27 | | #define __STDC_FORMAT_MACROS |
28 | | #endif |
29 | | |
30 | | #include <inttypes.h> |
31 | | |
32 | | #include <algorithm> |
33 | | #include <vector> |
34 | | |
35 | | #include "yb/gutil/stl_util.h" |
36 | | |
37 | | #include "yb/rocksdb/compaction_filter.h" |
38 | | #include "yb/rocksdb/db/column_family.h" |
39 | | #include "yb/rocksdb/db/compaction_picker.h" |
40 | | #include "yb/rocksdb/db/version_set.h" |
41 | | #include "yb/rocksdb/util/logging.h" |
42 | | #include "yb/rocksdb/util/sync_point.h" |
43 | | |
44 | | #include "yb/util/format.h" |
45 | | #include "yb/util/logging.h" |
46 | | #include "yb/util/size_literals.h" |
47 | | |
48 | | using namespace yb::size_literals; |
49 | | |
50 | | namespace rocksdb { |
51 | | |
52 | | namespace { |
53 | | |
54 | 2.45M | Slice SliceDup(Arena* arena, Slice input) { |
55 | 2.45M | auto* mem = arena->AllocateAligned(input.size()); |
56 | 2.45M | memcpy(mem, input.data(), input.size()); |
57 | 2.45M | return Slice(mem, input.size()); |
58 | 2.45M | } |
59 | | |
60 | | } |
61 | | |
62 | | LightweightBoundaries::LightweightBoundaries(Arena* arena, |
63 | 2.06M | const FileMetaData::BoundaryValues& source) { |
64 | 2.06M | key = SliceDup(arena, source.key.Encode()); |
65 | 2.06M | num_user_values = source.user_values.size(); |
66 | 2.06M | user_tags = reinterpret_cast<UserBoundaryTag*>( |
67 | 2.06M | arena->AllocateAligned(sizeof(UserBoundaryTag) * num_user_values)); |
68 | 2.06M | user_values = reinterpret_cast<Slice*>( |
69 | 2.06M | arena->AllocateAligned(sizeof(Slice) * num_user_values)); |
70 | 2.45M | for (size_t i = 0; i != num_user_values; ++i390k ) { |
71 | 390k | UserBoundaryValue& value = *source.user_values[i]; |
72 | 390k | new (user_tags + i) UserBoundaryTag(value.Tag()); |
73 | 390k | new (user_values + i) Slice(SliceDup(arena, value.Encode())); |
74 | 390k | } |
75 | 2.06M | } |
76 | | |
77 | | FdWithBoundaries::FdWithBoundaries(Arena* arena, const FileMetaData& source) |
78 | 1.03M | : fd(source.fd), smallest(arena, source.smallest), largest(arena, source.largest) { |
79 | 1.03M | if (source.largest.user_frontier) { |
80 | 41.1k | auto filter = source.largest.user_frontier->Filter(); |
81 | 41.1k | if (!filter.empty()) { |
82 | 49 | user_filter_data = SliceDup(arena, filter); |
83 | 49 | } |
84 | 41.1k | } |
85 | 1.03M | } |
86 | | |
87 | 466k | uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) { |
88 | 466k | uint64_t sum = 0; |
89 | 1.11M | for (size_t i = 0; i < files.size() && files[i]643k ; i++643k ) { |
90 | 643k | sum += files[i]->fd.GetTotalFileSize(); |
91 | 643k | } |
92 | 466k | return sum; |
93 | 466k | } |
94 | | |
95 | 1.75M | bool Compaction::IsCompactionStyleUniversal() const { |
96 | 1.75M | return cfd_->ioptions()->compaction_style == kCompactionStyleUniversal; |
97 | 1.75M | } |
98 | | |
99 | 23.7k | void Compaction::SetInputVersion(Version* input_version) { |
100 | 23.7k | input_version_number_ = input_version->GetVersionNumber(); |
101 | 23.7k | input_version_level0_non_overlapping_ = input_version->storage_info()->level0_non_overlapping(); |
102 | 23.7k | vset_ = input_version->version_set(); |
103 | 23.7k | cfd_ = input_version->cfd(); |
104 | 23.7k | cfd_->Ref(); |
105 | | |
106 | 23.7k | if (IsCompactionStyleUniversal()) { |
107 | | // We don't need to lock the whole input version for universal compaction, only need input |
108 | | // files. |
109 | 22.4k | for (auto& input_level : inputs_) { |
110 | 22.4k | for (auto* f : input_level.files) { |
111 | 21.5k | ++f->refs; |
112 | 21.5k | } |
113 | 22.4k | } |
114 | 18.3k | } else { |
115 | 18.3k | input_version_ = input_version; |
116 | 18.3k | input_version_->Ref(); |
117 | 18.3k | } |
118 | | |
119 | 23.7k | edit_.SetColumnFamily(cfd_->GetID()); |
120 | 23.7k | } |
121 | | |
122 | | void Compaction::GetBoundaryKeys( |
123 | | VersionStorageInfo* vstorage, |
124 | | const std::vector<CompactionInputFiles>& inputs, Slice* smallest_user_key, |
125 | 43.8k | Slice* largest_user_key) { |
126 | 43.8k | bool initialized = false; |
127 | 43.8k | const Comparator* ucmp = vstorage->InternalComparator()->user_comparator(); |
128 | 131k | for (size_t i = 0; i < inputs.size(); ++i87.2k ) { |
129 | 87.2k | if (inputs[i].files.empty()) { |
130 | 28.3k | continue; |
131 | 28.3k | } |
132 | 58.9k | if (inputs[i].level == 0) { |
133 | | // we need to consider all files on level 0 |
134 | 59.1k | for (const auto* f : inputs[i].files) { |
135 | 59.1k | Slice start_user_key = f->smallest.key.user_key(); |
136 | 59.1k | if (!initialized || |
137 | 59.1k | ucmp->Compare(start_user_key, *smallest_user_key) < 036.1k ) { |
138 | 34.0k | *smallest_user_key = start_user_key; |
139 | 34.0k | } |
140 | 59.1k | Slice end_user_key = f->largest.key.user_key(); |
141 | 59.1k | if (!initialized || |
142 | 59.1k | ucmp->Compare(end_user_key, *largest_user_key) > 036.1k ) { |
143 | 31.0k | *largest_user_key = end_user_key; |
144 | 31.0k | } |
145 | 59.1k | initialized = true; |
146 | 59.1k | } |
147 | 35.9k | } else { |
148 | | // we only need to consider the first and last file |
149 | 35.9k | Slice start_user_key = inputs[i].files[0]->smallest.key.user_key(); |
150 | 35.9k | if (!initialized || |
151 | 35.9k | ucmp->Compare(start_user_key, *smallest_user_key) < 015.0k ) { |
152 | 25.6k | *smallest_user_key = start_user_key; |
153 | 25.6k | } |
154 | 35.9k | Slice end_user_key = inputs[i].files.back()->largest.key.user_key(); |
155 | 35.9k | if (!initialized || ucmp->Compare(end_user_key, *largest_user_key) > 015.0k ) { |
156 | 25.9k | *largest_user_key = end_user_key; |
157 | 25.9k | } |
158 | 35.9k | initialized = true; |
159 | 35.9k | } |
160 | 58.9k | } |
161 | 43.8k | } |
162 | | |
163 | | // helper function to determine if compaction is creating files at the |
164 | | // bottommost level |
165 | | bool Compaction::IsBottommostLevel( |
166 | | int output_level, VersionStorageInfo* vstorage, |
167 | 23.7k | const std::vector<CompactionInputFiles>& inputs) { |
168 | 23.7k | if (inputs[0].level == 0 && |
169 | 23.7k | inputs[0].files.back() != vstorage->LevelFiles(0).back()13.3k ) { |
170 | 3.71k | return false; |
171 | 3.71k | } |
172 | | |
173 | 20.0k | Slice smallest_key, largest_key; |
174 | 20.0k | GetBoundaryKeys(vstorage, inputs, &smallest_key, &largest_key); |
175 | | |
176 | | // Checks whether there are files living beyond the output_level. |
177 | | // If lower levels have files, it checks for overlap between files |
178 | | // if the compaction process and those files. |
179 | | // Bottomlevel optimizations can be made if there are no files in |
180 | | // lower levels or if there is no overlap with the files in |
181 | | // the lower levels. |
182 | 62.9k | for (int i = output_level + 1; i < vstorage->num_levels(); i++42.9k ) { |
183 | | // It is not the bottommost level if there are files in higher |
184 | | // levels when the output level is 0 or if there are files in |
185 | | // higher levels which overlap with files to be compacted. |
186 | | // output_level == 0 means that we want it to be considered |
187 | | // s the bottommost level only if the last file on the level |
188 | | // is a part of the files to be compacted - this is verified by |
189 | | // the first if condition in this function |
190 | 48.4k | if (vstorage->NumLevelFiles(i) > 0 && |
191 | 48.4k | (22.9k output_level == 022.9k || |
192 | 22.9k | vstorage->OverlapInLevel(i, &smallest_key, &largest_key)22.7k )) { |
193 | 5.53k | return false; |
194 | 5.53k | } |
195 | 48.4k | } |
196 | 14.5k | return true; |
197 | 20.0k | } |
198 | | |
199 | | // test function to validate the functionality of IsBottommostLevel() |
200 | | // function -- determines if compaction with inputs and storage is bottommost |
201 | | bool Compaction::TEST_IsBottommostLevel( |
202 | | int output_level, VersionStorageInfo* vstorage, |
203 | 8 | const std::vector<CompactionInputFiles>& inputs) { |
204 | 8 | return IsBottommostLevel(output_level, vstorage, inputs); |
205 | 8 | } |
206 | | |
207 | | bool Compaction::IsFullCompaction( |
208 | | VersionStorageInfo* vstorage, |
209 | 23.7k | const std::vector<CompactionInputFiles>& inputs) { |
210 | 23.7k | size_t num_files_in_compaction = 0; |
211 | 23.7k | size_t total_num_files = 0; |
212 | 181k | for (int l = 0; l < vstorage->num_levels(); l++157k ) { |
213 | 157k | total_num_files += vstorage->NumLevelFiles(l); |
214 | 157k | } |
215 | 70.0k | for (size_t i = 0; i < inputs.size(); i++46.2k ) { |
216 | 46.2k | num_files_in_compaction += inputs[i].size(); |
217 | 46.2k | } |
218 | 23.7k | return num_files_in_compaction == total_num_files; |
219 | 23.7k | } |
220 | | |
221 | | std::unique_ptr<Compaction> Compaction::Create( |
222 | | VersionStorageInfo* vstorage, const MutableCFOptions& _mutable_cf_options, |
223 | | std::vector<CompactionInputFiles> inputs, int output_level, uint64_t target_file_size, |
224 | | uint64_t max_grandparent_overlap_bytes, uint32_t output_path_id, CompressionType compression, |
225 | | std::vector<FileMetaData*> grandparents, Logger* info_log, bool manual_compaction, double score, |
226 | 23.7k | bool deletion_compaction, CompactionReason compaction_reason) { |
227 | 23.7k | bool has_input_files = false; |
228 | 46.2k | for (auto& input : inputs) { |
229 | 63.0k | yb::EraseIf([info_log](FileMetaData* file) { |
230 | 63.0k | bool being_deleted = file->being_deleted; |
231 | 63.0k | if (being_deleted) { |
232 | 5 | RLOG( |
233 | 5 | InfoLogLevel::INFO_LEVEL, info_log, |
234 | 5 | yb::Format("Skipping compaction of file that is being deleted: $0", file).c_str()); |
235 | 5 | } |
236 | 63.0k | return being_deleted; |
237 | 63.0k | }, &input.files); |
238 | 46.2k | has_input_files |= !input.empty(); |
239 | 46.2k | } |
240 | 23.7k | if (!has_input_files) { |
241 | 1 | RLOG( |
242 | 1 | InfoLogLevel::INFO_LEVEL, info_log, |
243 | 1 | "Skipping compaction creation, no input files to compact"); |
244 | 1 | return nullptr; |
245 | 1 | } |
246 | | // We don't remove empty input levels, because empty input levels are handled differently |
247 | | // than absent ones, for example by Compaction::IsTrivialMove. |
248 | | // But we need to remove inputs[0] if it is empty and has level 0, otherwise |
249 | | // Compaction::IsBottommostLevel will fail. |
250 | 23.7k | if (inputs[0].level == 0 && inputs[0].empty()13.3k ) { |
251 | 0 | inputs.erase(inputs.begin()); |
252 | 0 | } |
253 | | |
254 | 23.7k | return std::unique_ptr<Compaction>(new Compaction( |
255 | 23.7k | vstorage, _mutable_cf_options, inputs, output_level, target_file_size, |
256 | 23.7k | max_grandparent_overlap_bytes, output_path_id, compression, grandparents, manual_compaction, |
257 | 23.7k | score, deletion_compaction, compaction_reason)); |
258 | 23.7k | } |
259 | | |
260 | | Compaction::Compaction(VersionStorageInfo* vstorage, |
261 | | const MutableCFOptions& _mutable_cf_options, |
262 | | std::vector<CompactionInputFiles> _inputs, |
263 | | int _output_level, uint64_t _target_file_size, |
264 | | uint64_t _max_grandparent_overlap_bytes, |
265 | | uint32_t _output_path_id, CompressionType _compression, |
266 | | std::vector<FileMetaData*> _grandparents, |
267 | | bool _manual_compaction, double _score, |
268 | | bool _deletion_compaction, |
269 | | CompactionReason _compaction_reason) |
270 | | : start_level_(_inputs[0].level), |
271 | | output_level_(_output_level), |
272 | | max_output_file_size_(_target_file_size), |
273 | | max_grandparent_overlap_bytes_(_max_grandparent_overlap_bytes), |
274 | | mutable_cf_options_(_mutable_cf_options), |
275 | | input_version_(nullptr), |
276 | | number_levels_(vstorage->num_levels()), |
277 | | cfd_(nullptr), |
278 | | output_path_id_(_output_path_id), |
279 | | output_compression_(_compression), |
280 | | deletion_compaction_(_deletion_compaction), |
281 | | inputs_(std::move(_inputs)), |
282 | | grandparents_(std::move(_grandparents)), |
283 | | grandparent_index_(0), |
284 | | overlapped_bytes_(0), |
285 | | score_(_score), |
286 | | bottommost_level_(IsBottommostLevel(output_level_, vstorage, inputs_)), |
287 | | is_full_compaction_(IsFullCompaction(vstorage, inputs_)), |
288 | | is_manual_compaction_(_manual_compaction), |
289 | 23.7k | compaction_reason_(_compaction_reason) { |
290 | 23.7k | seen_key_.store(false, std::memory_order_release); |
291 | 23.7k | MarkFilesBeingCompacted(true); |
292 | 23.7k | if (is_manual_compaction_) { |
293 | 5.27k | compaction_reason_ = CompactionReason::kManualCompaction; |
294 | 5.27k | } |
295 | | |
296 | 23.7k | #ifndef NDEBUG |
297 | 46.2k | for (size_t i = 1; i < inputs_.size(); ++i22.4k ) { |
298 | 22.4k | assert(inputs_[i].level > inputs_[i - 1].level); |
299 | 22.4k | } |
300 | 23.7k | #endif |
301 | | |
302 | | // setup input_levels_ |
303 | 23.7k | { |
304 | 23.7k | input_levels_.resize(num_input_levels()); |
305 | 70.0k | for (size_t which = 0; which < num_input_levels(); which++46.2k ) { |
306 | 46.2k | DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files, |
307 | 46.2k | &arena_); |
308 | 46.2k | } |
309 | 23.7k | } |
310 | | |
311 | 23.7k | Slice smallest_user_key; |
312 | 23.7k | GetBoundaryKeys(vstorage, inputs_, &smallest_user_key, &largest_user_key_); |
313 | 23.7k | } |
314 | | |
315 | 23.7k | Compaction::~Compaction() { |
316 | 23.7k | if (input_version_ != nullptr) { |
317 | 18.3k | input_version_->Unref(); |
318 | 18.3k | } else if (5.41k cfd_ != nullptr5.41k ) { |
319 | | // If we don't hold input_version_, unref each input file separately. |
320 | 22.3k | for (auto& input_level : inputs_) { |
321 | 22.3k | for (auto f : input_level.files) { |
322 | 21.4k | vset_->UnrefFile(cfd_, f); |
323 | 21.4k | } |
324 | 22.3k | } |
325 | 5.39k | } |
326 | 23.7k | if (cfd_ != nullptr) { |
327 | 23.7k | if (cfd_->Unref()) { |
328 | 0 | delete cfd_; |
329 | 0 | } |
330 | 23.7k | } |
331 | 23.7k | } |
332 | | |
333 | 12.2k | bool Compaction::InputCompressionMatchesOutput() const { |
334 | 12.2k | int base_level = IsCompactionStyleUniversal() |
335 | 12.2k | ? -10 |
336 | 12.2k | : input_version_->storage_info()->base_level(); |
337 | 12.2k | bool matches = (GetCompressionType(*cfd_->ioptions(), start_level_, |
338 | 12.2k | base_level) == output_compression_); |
339 | 12.2k | if (matches) { |
340 | 12.1k | TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:Matches"); |
341 | 12.1k | return true; |
342 | 12.1k | } |
343 | 116 | TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:DidntMatch"); |
344 | 116 | return matches; |
345 | 12.2k | } |
346 | | |
347 | 35.0k | bool Compaction::IsTrivialMove() const { |
348 | | // Avoid a move if there is lots of overlapping grandparent data. |
349 | | // Otherwise, the move could create a parent file that will require |
350 | | // a very expensive merge later on. |
351 | | // If start_level_== output_level_, the purpose is to force compaction |
352 | | // filter to be applied to that level, and thus cannot be a trivial move. |
353 | | |
354 | | // Check if start level have files with overlapping ranges |
355 | 35.0k | if (start_level_ == 0 && !input_version_level0_non_overlapping_22.5k ) { |
356 | | // We cannot move files from L0 to L1 if the files are overlapping |
357 | 8.24k | return false; |
358 | 8.24k | } |
359 | | |
360 | 26.7k | if (is_manual_compaction_ && |
361 | 26.7k | (5.68k cfd_->ioptions()->compaction_filter != nullptr5.68k || |
362 | 5.68k | cfd_->ioptions()->compaction_filter_factory != nullptr5.68k )) { |
363 | | // This is a manual compaction and we have a compaction filter that should |
364 | | // be executed, we cannot do a trivial move |
365 | 564 | return false; |
366 | 564 | } |
367 | | |
368 | | // Used in universal compaction, where trivial move can be done if the |
369 | | // input files are non overlapping |
370 | 26.2k | if ((cfd_->ioptions()->compaction_options_universal.allow_trivial_move) && |
371 | 26.2k | (output_level_ != 0)980 ) { |
372 | 224 | return is_trivial_move_; |
373 | 224 | } |
374 | | |
375 | 25.9k | return (start_level_ != output_level_ && num_input_levels() == 123.7k && |
376 | 25.9k | input(0, 0)->fd.GetPathId() == output_path_id()12.4k && |
377 | 25.9k | InputCompressionMatchesOutput()12.2k && |
378 | 25.9k | TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_12.1k ); |
379 | 26.2k | } |
380 | | |
381 | 11.2k | void Compaction::AddInputDeletions(VersionEdit* out_edit) { |
382 | 42.8k | for (size_t which = 0; which < num_input_levels(); which++31.5k ) { |
383 | 79.1k | for (size_t i = 0; i < inputs_[which].size(); i++47.6k ) { |
384 | 47.6k | out_edit->DeleteFile(level(which), inputs_[which][i]->fd.GetNumber()); |
385 | 47.6k | } |
386 | 31.5k | } |
387 | 11.2k | } |
388 | | |
389 | | bool Compaction::KeyNotExistsBeyondOutputLevel( |
390 | 1.71M | const Slice& user_key, std::vector<size_t>* level_ptrs) const { |
391 | 1.71M | assert(level_ptrs != nullptr); |
392 | 0 | assert(level_ptrs->size() == static_cast<size_t>(number_levels_)); |
393 | 0 | assert(cfd_->ioptions()->compaction_style != kCompactionStyleFIFO); |
394 | 1.71M | if (IsCompactionStyleUniversal()) { |
395 | 174k | return bottommost_level_; |
396 | 174k | } |
397 | 1.54M | DCHECK_ONLY_NOTNULL(input_version_); |
398 | | // Maybe use binary search to find right entry instead of linear search? |
399 | 1.54M | const Comparator* user_cmp = cfd_->user_comparator(); |
400 | 8.14M | for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++6.60M ) { |
401 | 6.73M | const std::vector<FileMetaData*>& files = |
402 | 6.73M | input_version_->storage_info()->LevelFiles(lvl); |
403 | 6.74M | for (; level_ptrs->at(lvl) < files.size(); level_ptrs->at(lvl)++12.6k ) { |
404 | 258k | auto* f = files[level_ptrs->at(lvl)]; |
405 | 258k | if (user_cmp->Compare(user_key, f->largest.key.user_key()) <= 0) { |
406 | | // We've advanced far enough |
407 | 246k | if (user_cmp->Compare(user_key, f->smallest.key.user_key()) >= 0) { |
408 | | // Key falls in this file's range, so definitely |
409 | | // exists beyond output level |
410 | 127k | return false; |
411 | 127k | } |
412 | 119k | break; |
413 | 246k | } |
414 | 258k | } |
415 | 6.73M | } |
416 | 1.41M | return true; |
417 | 1.54M | } |
418 | | |
419 | 47.2M | bool Compaction::ShouldStopBefore(const Slice& internal_key) { |
420 | | // Scan to find earliest grandparent file that contains key. |
421 | 47.2M | const InternalKeyComparator* icmp = cfd_->internal_comparator().get(); |
422 | 47.2M | while (grandparent_index_ < grandparents_.size() && |
423 | 47.2M | icmp->Compare(internal_key, |
424 | 3.45M | grandparents_[grandparent_index_]->largest.key.Encode()) > 0) { |
425 | 11.8k | if (seen_key_.load(std::memory_order_acquire)) { |
426 | 11.8k | overlapped_bytes_ += grandparents_[grandparent_index_]->fd.GetTotalFileSize(); |
427 | 11.8k | } |
428 | 11.8k | assert(grandparent_index_ + 1 >= grandparents_.size() || |
429 | 11.8k | icmp->Compare(grandparents_[grandparent_index_]->largest.key.Encode(), |
430 | 11.8k | grandparents_[grandparent_index_ + 1]->smallest.key.Encode()) |
431 | 11.8k | < 0); |
432 | 0 | grandparent_index_++; |
433 | 11.8k | } |
434 | 47.2M | seen_key_.store(true, std::memory_order_release); |
435 | | |
436 | 47.2M | if (overlapped_bytes_ > max_grandparent_overlap_bytes_) { |
437 | | // Too much overlap for current output; start new output |
438 | 1.07k | overlapped_bytes_ = 0; |
439 | 1.07k | return true; |
440 | 47.2M | } else { |
441 | 47.2M | return false; |
442 | 47.2M | } |
443 | 47.2M | } |
444 | | |
445 | | // Mark (or clear) each file that is being compacted |
446 | 47.5k | void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) { |
447 | 139k | for (size_t i = 0; i < num_input_levels(); i++92.4k ) { |
448 | 218k | for (size_t j = 0; j < inputs_[i].size(); j++125k ) { |
449 | 125k | assert(mark_as_compacted ? !inputs_[i][j]->being_compacted : |
450 | 125k | inputs_[i][j]->being_compacted); |
451 | 0 | inputs_[i][j]->being_compacted = mark_as_compacted; |
452 | 125k | } |
453 | 92.4k | } |
454 | 47.5k | } |
455 | | |
456 | | // Sample output: |
457 | | // If compacting 3 L0 files, 2 L3 files and 1 L4 file, and outputting to L5, |
458 | | // print: "3@0 + 2@3 + 1@4 files to L5" |
459 | | const char* Compaction::InputLevelSummary( |
460 | 22.7k | InputLevelSummaryBuffer* scratch) const { |
461 | 22.7k | int len = 0; |
462 | 22.7k | bool is_first = true; |
463 | 63.2k | for (auto& input_level : inputs_) { |
464 | 63.2k | if (input_level.empty()) { |
465 | 25.3k | continue; |
466 | 25.3k | } |
467 | 37.8k | if (!is_first) { |
468 | 15.1k | len += |
469 | 15.1k | snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, " + "); |
470 | 22.7k | } else { |
471 | 22.7k | is_first = false; |
472 | 22.7k | } |
473 | 37.8k | len += snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, |
474 | 37.8k | "%" ROCKSDB_PRIszt "@%d", input_level.size(), |
475 | 37.8k | input_level.level); |
476 | 37.8k | } |
477 | 22.7k | snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, |
478 | 22.7k | " files to L%d", output_level()); |
479 | | |
480 | 22.7k | return scratch->buffer; |
481 | 22.7k | } |
482 | | |
483 | 34.8k | uint64_t Compaction::CalculateTotalInputSize() const { |
484 | 34.8k | uint64_t size = 0; |
485 | 75.5k | for (auto& input_level : inputs_) { |
486 | 121k | for (auto f : input_level.files) { |
487 | 121k | size += f->fd.GetTotalFileSize(); |
488 | 121k | } |
489 | 75.5k | } |
490 | 34.8k | return size; |
491 | 34.8k | } |
492 | | |
493 | 23.7k | void Compaction::ReleaseCompactionFiles(Status status) { |
494 | 23.7k | MarkFilesBeingCompacted(false); |
495 | 23.7k | cfd_->compaction_picker()->ReleaseCompactionFiles(this, status); |
496 | 23.7k | } |
497 | | |
498 | 120 | void Compaction::ResetNextCompactionIndex() { |
499 | 120 | if (!IsCompactionStyleUniversal()) { |
500 | 69 | DCHECK_ONLY_NOTNULL(input_version_); |
501 | 69 | input_version_->storage_info()->ResetNextCompactionIndex(start_level_); |
502 | 69 | } |
503 | 120 | } |
504 | | |
505 | | namespace { |
506 | | int InputSummary(const std::vector<FileMetaData*>& files, char* output, |
507 | 31.7k | int len) { |
508 | 31.7k | *output = '\0'; |
509 | 31.7k | int write = 0; |
510 | 79.9k | for (size_t i = 0; i < files.size(); i++48.2k ) { |
511 | 48.2k | int sz = len - write; |
512 | 48.2k | int ret; |
513 | 48.2k | char sztxt[16]; |
514 | 48.2k | AppendHumanBytes(files.at(i)->fd.GetTotalFileSize(), sztxt, 16); |
515 | 48.2k | ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ", |
516 | 48.2k | files.at(i)->fd.GetNumber(), sztxt); |
517 | 48.2k | if (ret < 0 || ret >= sz) break0 ; |
518 | 48.2k | write += ret; |
519 | 48.2k | } |
520 | | // if files.size() is non-zero, overwrite the last space |
521 | 31.7k | return write - !!files.size(); |
522 | 31.7k | } |
523 | | } // namespace |
524 | | |
525 | 11.4k | void Compaction::Summary(char* output, int len) { |
526 | 11.4k | int write = |
527 | 11.4k | snprintf(output, len, "Base version %" PRIu64 |
528 | 11.4k | " Base level %d, inputs: [", |
529 | 11.4k | input_version_number_, start_level_); |
530 | 11.4k | if (write < 0 || write >= len) { |
531 | 0 | return; |
532 | 0 | } |
533 | | |
534 | 43.1k | for (size_t level_iter = 0; 11.4k level_iter < num_input_levels(); ++level_iter31.7k ) { |
535 | 31.7k | if (level_iter > 0) { |
536 | 20.3k | write += snprintf(output + write, len - write, "], ["); |
537 | 20.3k | if (write < 0 || write >= len) { |
538 | 0 | return; |
539 | 0 | } |
540 | 20.3k | } |
541 | 31.7k | write += |
542 | 31.7k | InputSummary(inputs_[level_iter].files, output + write, len - write); |
543 | 31.7k | if (write < 0 || write >= len) { |
544 | 0 | return; |
545 | 0 | } |
546 | 31.7k | } |
547 | | |
548 | 11.4k | snprintf(output + write, len - write, "]"); |
549 | 11.4k | } |
550 | | |
551 | 22.9k | uint64_t Compaction::OutputFilePreallocationSize() { |
552 | 22.9k | uint64_t preallocation_size = 0; |
553 | | |
554 | 22.9k | if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel || |
555 | 22.9k | output_level() > 07.84k ) { |
556 | 20.1k | preallocation_size = max_output_file_size_; |
557 | 20.1k | } else { |
558 | | // output_level() == 0 |
559 | 2.83k | assert(num_input_levels() > 0); |
560 | 9.73k | for (const auto& f : inputs_[0].files) { |
561 | 9.73k | preallocation_size += f->fd.GetTotalFileSize(); |
562 | 9.73k | } |
563 | 2.83k | } |
564 | 0 | constexpr uint64_t kMaxPreAllocationSize = 1_GB; |
565 | | // Over-estimate slightly so we don't end up just barely crossing |
566 | | // the threshold |
567 | 22.9k | return std::min(kMaxPreAllocationSize, preallocation_size + (preallocation_size / 10)); |
568 | 22.9k | } |
569 | | |
570 | 11.4k | std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const { |
571 | 11.4k | if (!cfd_->ioptions()->compaction_filter_factory) { |
572 | 8.25k | return nullptr; |
573 | 8.25k | } |
574 | | |
575 | 3.16k | CompactionFilter::Context context; |
576 | 3.16k | context.is_full_compaction = is_full_compaction_; |
577 | 3.16k | context.is_manual_compaction = is_manual_compaction_; |
578 | 3.16k | context.column_family_id = cfd_->GetID(); |
579 | 3.16k | return cfd_->ioptions()->compaction_filter_factory->CreateCompactionFilter( |
580 | 3.16k | context); |
581 | 11.4k | } |
582 | | |
583 | 279 | bool Compaction::IsOutputLevelEmpty() const { |
584 | 279 | return inputs_.back().level != output_level_ || inputs_.back().empty()151 ; |
585 | 279 | } |
586 | | |
587 | 22.8k | bool Compaction::ShouldFormSubcompactions() const { |
588 | 22.8k | if (mutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr2.71k ) { |
589 | 20.1k | return false; |
590 | 20.1k | } |
591 | 2.71k | if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) { |
592 | 497 | return start_level_ == 0 && !IsOutputLevelEmpty()279 ; |
593 | 2.21k | } else if (IsCompactionStyleUniversal()) { |
594 | 2.21k | return number_levels_ > 1 && output_level_ > 02.01k ; |
595 | 2.21k | } else { |
596 | 0 | return false; |
597 | 0 | } |
598 | 2.71k | } |
599 | | |
600 | | } // namespace rocksdb |