YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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.19M
Slice SliceDup(Arena* arena, Slice input) {
55
2.19M
  auto* mem = arena->AllocateAligned(input.size());
56
2.19M
  memcpy(mem, input.data(), input.size());
57
2.19M
  return Slice(mem, input.size());
58
2.19M
}
59
60
}
61
62
LightweightBoundaries::LightweightBoundaries(Arena* arena,
63
2.01M
                                             const FileMetaData::BoundaryValues& source) {
64
2.01M
  key = SliceDup(arena, source.key.Encode());
65
2.01M
  num_user_values = source.user_values.size();
66
2.01M
  user_tags = reinterpret_cast<UserBoundaryTag*>(
67
2.01M
    arena->AllocateAligned(sizeof(UserBoundaryTag) * num_user_values));
68
2.01M
  user_values = reinterpret_cast<Slice*>(
69
2.01M
    arena->AllocateAligned(sizeof(Slice) * num_user_values));
70
2.19M
  for (size_t i = 0; i != num_user_values; ++i) {
71
170k
    UserBoundaryValue& value = *source.user_values[i];
72
170k
    new (user_tags + i) UserBoundaryTag(value.Tag());
73
170k
    new (user_values + i) Slice(SliceDup(arena, value.Encode()));
74
170k
  }
75
2.01M
}
76
77
FdWithBoundaries::FdWithBoundaries(Arena* arena, const FileMetaData& source)
78
1.00M
    : fd(source.fd), smallest(arena, source.smallest), largest(arena, source.largest) {
79
1.00M
  if (source.largest.user_frontier) {
80
29.7k
    auto filter = source.largest.user_frontier->Filter();
81
29.7k
    if (!filter.empty()) {
82
0
      user_filter_data = SliceDup(arena, filter);
83
0
    }
84
29.7k
  }
85
1.00M
}
86
87
466k
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
88
466k
  uint64_t sum = 0;
89
1.10M
  for (size_t i = 0; i < files.size() && files[i]; i++) {
90
640k
    sum += files[i]->fd.GetTotalFileSize();
91
640k
  }
92
466k
  return sum;
93
466k
}
94
95
1.61M
bool Compaction::IsCompactionStyleUniversal() const {
96
1.61M
  return cfd_->ioptions()->compaction_style == kCompactionStyleUniversal;
97
1.61M
}
98
99
23.0k
void Compaction::SetInputVersion(Version* input_version) {
100
23.0k
  input_version_number_ = input_version->GetVersionNumber();
101
23.0k
  input_version_level0_non_overlapping_ = input_version->storage_info()->level0_non_overlapping();
102
23.0k
  vset_ = input_version->version_set();
103
23.0k
  cfd_ = input_version->cfd();
104
23.0k
  cfd_->Ref();
105
106
23.0k
  if (IsCompactionStyleUniversal()) {
107
    // We don't need to lock the whole input version for universal compaction, only need input
108
    // files.
109
21.8k
    for (auto& input_level : inputs_) {
110
19.0k
      for (auto* f : input_level.files) {
111
19.0k
        ++f->refs;
112
19.0k
      }
113
21.8k
    }
114
18.2k
  } else {
115
18.2k
    input_version_ = input_version;
116
18.2k
    input_version_->Ref();
117
18.2k
  }
118
119
23.0k
  edit_.SetColumnFamily(cfd_->GetID());
120
23.0k
}
121
122
void Compaction::GetBoundaryKeys(
123
    VersionStorageInfo* vstorage,
124
    const std::vector<CompactionInputFiles>& inputs, Slice* smallest_user_key,
125
42.2k
    Slice* largest_user_key) {
126
42.2k
  bool initialized = false;
127
42.2k
  const Comparator* ucmp = vstorage->InternalComparator()->user_comparator();
128
127k
  for (size_t i = 0; i < inputs.size(); ++i) {
129
85.3k
    if (inputs[i].files.empty()) {
130
28.1k
      continue;
131
28.1k
    }
132
57.1k
    if (inputs[i].level == 0) {
133
      // we need to consider all files on level 0
134
54.0k
      for (const auto* f : inputs[i].files) {
135
54.0k
        Slice start_user_key = f->smallest.key.user_key();
136
54.0k
        if (!initialized ||
137
32.5k
            ucmp->Compare(start_user_key, *smallest_user_key) < 0) {
138
31.5k
          *smallest_user_key = start_user_key;
139
31.5k
        }
140
54.0k
        Slice end_user_key = f->largest.key.user_key();
141
54.0k
        if (!initialized ||
142
32.5k
            ucmp->Compare(end_user_key, *largest_user_key) > 0) {
143
27.8k
          *largest_user_key = end_user_key;
144
27.8k
        }
145
54.0k
        initialized = true;
146
54.0k
      }
147
35.6k
    } else {
148
      // we only need to consider the first and last file
149
35.6k
      Slice start_user_key = inputs[i].files[0]->smallest.key.user_key();
150
35.6k
      if (!initialized ||
151
25.5k
          ucmp->Compare(start_user_key, *smallest_user_key) < 0) {
152
25.5k
        *smallest_user_key = start_user_key;
153
25.5k
      }
154
35.6k
      Slice end_user_key = inputs[i].files.back()->largest.key.user_key();
155
35.6k
      if (!initialized || ucmp->Compare(end_user_key, *largest_user_key) > 0) {
156
25.7k
        *largest_user_key = end_user_key;
157
25.7k
      }
158
35.6k
      initialized = true;
159
35.6k
    }
160
57.1k
  }
161
42.2k
}
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.0k
    const std::vector<CompactionInputFiles>& inputs) {
168
23.0k
  if (inputs[0].level == 0 &&
169
12.6k
      inputs[0].files.back() != vstorage->LevelFiles(0).back()) {
170
3.76k
    return false;
171
3.76k
  }
172
173
19.2k
  Slice smallest_key, largest_key;
174
19.2k
  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.0k
  for (int i = output_level + 1; i < vstorage->num_levels(); i++) {
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.3k
    if (vstorage->NumLevelFiles(i) > 0 &&
191
22.8k
        (output_level == 0 ||
192
22.7k
         vstorage->OverlapInLevel(i, &smallest_key, &largest_key))) {
193
5.51k
      return false;
194
5.51k
    }
195
48.3k
  }
196
13.7k
  return true;
197
19.2k
}
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.0k
    const std::vector<CompactionInputFiles>& inputs) {
210
23.0k
  size_t num_files_in_compaction = 0;
211
23.0k
  size_t total_num_files = 0;
212
179k
  for (int l = 0; l < vstorage->num_levels(); l++) {
213
156k
    total_num_files += vstorage->NumLevelFiles(l);
214
156k
  }
215
68.5k
  for (size_t i = 0; i < inputs.size(); i++) {
216
45.5k
    num_files_in_compaction += inputs[i].size();
217
45.5k
  }
218
23.0k
  return num_files_in_compaction == total_num_files;
219
23.0k
}
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.0k
    bool deletion_compaction, CompactionReason compaction_reason) {
227
23.0k
  bool has_input_files = false;
228
45.5k
  for (auto& input : inputs) {
229
60.3k
    yb::EraseIf([info_log](FileMetaData* file) {
230
60.3k
      bool being_deleted = file->being_deleted;
231
60.3k
      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
60.3k
      return being_deleted;
237
60.3k
    }, &input.files);
238
45.5k
    has_input_files |= !input.empty();
239
45.5k
  }
240
23.0k
  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.0k
  if (inputs[0].level == 0 && inputs[0].empty()) {
251
0
    inputs.erase(inputs.begin());
252
0
  }
253
254
23.0k
  return std::unique_ptr<Compaction>(new Compaction(
255
23.0k
      vstorage, _mutable_cf_options, inputs, output_level, target_file_size,
256
23.0k
      max_grandparent_overlap_bytes, output_path_id, compression, grandparents, manual_compaction,
257
23.0k
      score, deletion_compaction, compaction_reason));
258
23.0k
}
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.0k
      compaction_reason_(_compaction_reason) {
290
23.0k
  seen_key_.store(false, std::memory_order_release);
291
23.0k
  MarkFilesBeingCompacted(true);
292
23.0k
  if (is_manual_compaction_) {
293
4.89k
    compaction_reason_ = CompactionReason::kManualCompaction;
294
4.89k
  }
295
296
23.0k
#ifndef NDEBUG
297
45.5k
  for (size_t i = 1; i < inputs_.size(); ++i) {
298
22.5k
    assert(inputs_[i].level > inputs_[i - 1].level);
299
22.5k
  }
300
23.0k
#endif
301
302
  // setup input_levels_
303
23.0k
  {
304
23.0k
    input_levels_.resize(num_input_levels());
305
68.5k
    for (size_t which = 0; which < num_input_levels(); which++) {
306
45.5k
      DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
307
45.5k
                                &arena_);
308
45.5k
    }
309
23.0k
  }
310
311
23.0k
  Slice smallest_user_key;
312
23.0k
  GetBoundaryKeys(vstorage, inputs_, &smallest_user_key, &largest_user_key_);
313
23.0k
}
314
315
23.0k
Compaction::~Compaction() {
316
23.0k
  if (input_version_ != nullptr) {
317
18.2k
    input_version_->Unref();
318
4.74k
  } else if (cfd_ != nullptr) {
319
    // If we don't hold input_version_, unref each input file separately.
320
21.8k
    for (auto& input_level : inputs_) {
321
18.9k
      for (auto f : input_level.files) {
322
18.9k
        vset_->UnrefFile(cfd_, f);
323
18.9k
      }
324
21.8k
    }
325
4.72k
  }
326
23.0k
  if (cfd_ != nullptr) {
327
22.9k
    if (cfd_->Unref()) {
328
0
      delete cfd_;
329
0
    }
330
22.9k
  }
331
23.0k
}
332
333
12.2k
bool Compaction::InputCompressionMatchesOutput() const {
334
12.2k
  int base_level = IsCompactionStyleUniversal()
335
0
      ? -1
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
124
  TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:DidntMatch");
344
124
  return matches;
345
124
}
346
347
33.5k
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
33.5k
  if (start_level_ == 0 && !input_version_level0_non_overlapping_) {
356
    // We cannot move files from L0 to L1 if the files are overlapping
357
7.15k
    return false;
358
7.15k
  }
359
360
26.4k
  if (is_manual_compaction_ &&
361
5.38k
      (cfd_->ioptions()->compaction_filter != nullptr ||
362
5.37k
       cfd_->ioptions()->compaction_filter_factory != nullptr)) {
363
    // This is a manual compaction and we have a compaction filter that should
364
    // be executed, we cannot do a trivial move
365
256
    return false;
366
256
  }
367
368
  // Used in universal compaction, where trivial move can be done if the
369
  // input files are non overlapping
370
26.1k
  if ((cfd_->ioptions()->compaction_options_universal.allow_trivial_move) &&
371
978
      (output_level_ != 0)) {
372
224
    return is_trivial_move_;
373
224
  }
374
375
25.9k
  return (start_level_ != output_level_ && num_input_levels() == 1 &&
376
12.4k
          input(0, 0)->fd.GetPathId() == output_path_id() &&
377
12.2k
          InputCompressionMatchesOutput() &&
378
12.1k
          TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_);
379
25.9k
}
380
381
10.5k
void Compaction::AddInputDeletions(VersionEdit* out_edit) {
382
41.4k
  for (size_t which = 0; which < num_input_levels(); which++) {
383
75.9k
    for (size_t i = 0; i < inputs_[which].size(); i++) {
384
45.0k
      out_edit->DeleteFile(level(which), inputs_[which][i]->fd.GetNumber());
385
45.0k
    }
386
30.9k
  }
387
10.5k
}
388
389
bool Compaction::KeyNotExistsBeyondOutputLevel(
390
1.58M
    const Slice& user_key, std::vector<size_t>* level_ptrs) const {
391
1.58M
  assert(level_ptrs != nullptr);
392
1.58M
  assert(level_ptrs->size() == static_cast<size_t>(number_levels_));
393
1.58M
  assert(cfd_->ioptions()->compaction_style != kCompactionStyleFIFO);
394
1.58M
  if (IsCompactionStyleUniversal()) {
395
40.1k
    return bottommost_level_;
396
40.1k
  }
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++) {
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)++) {
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
245k
        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
126k
          return false;
411
126k
        }
412
119k
        break;
413
119k
      }
414
258k
    }
415
6.73M
  }
416
1.41M
  return true;
417
1.54M
}
418
419
33.1M
bool Compaction::ShouldStopBefore(const Slice& internal_key) {
420
  // Scan to find earliest grandparent file that contains key.
421
33.1M
  const InternalKeyComparator* icmp = cfd_->internal_comparator().get();
422
33.1M
  while (grandparent_index_ < grandparents_.size() &&
423
3.45M
      icmp->Compare(internal_key,
424
11.9k
                    grandparents_[grandparent_index_]->largest.key.Encode()) > 0) {
425
11.9k
    if (seen_key_.load(std::memory_order_acquire)) {
426
11.9k
      overlapped_bytes_ += grandparents_[grandparent_index_]->fd.GetTotalFileSize();
427
11.9k
    }
428
11.9k
    assert(grandparent_index_ + 1 >= grandparents_.size() ||
429
11.9k
           icmp->Compare(grandparents_[grandparent_index_]->largest.key.Encode(),
430
11.9k
                         grandparents_[grandparent_index_ + 1]->smallest.key.Encode())
431
11.9k
                         < 0);
432
11.9k
    grandparent_index_++;
433
11.9k
  }
434
33.1M
  seen_key_.store(true, std::memory_order_release);
435
436
33.1M
  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
33.1M
  } else {
441
33.1M
    return false;
442
33.1M
  }
443
33.1M
}
444
445
// Mark (or clear) each file that is being compacted
446
46.0k
void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
447
137k
  for (size_t i = 0; i < num_input_levels(); i++) {
448
211k
    for (size_t j = 0; j < inputs_[i].size(); j++) {
449
120k
      assert(mark_as_compacted ? !inputs_[i][j]->being_compacted :
450
120k
                                  inputs_[i][j]->being_compacted);
451
120k
      inputs_[i][j]->being_compacted = mark_as_compacted;
452
120k
    }
453
91.0k
  }
454
46.0k
}
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
21.2k
    InputLevelSummaryBuffer* scratch) const {
461
21.2k
  int len = 0;
462
21.2k
  bool is_first = true;
463
62.0k
  for (auto& input_level : inputs_) {
464
62.0k
    if (input_level.empty()) {
465
25.7k
      continue;
466
25.7k
    }
467
36.3k
    if (!is_first) {
468
15.0k
      len +=
469
15.0k
          snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, " + ");
470
21.2k
    } else {
471
21.2k
      is_first = false;
472
21.2k
    }
473
36.3k
    len += snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
474
36.3k
                    "%" ROCKSDB_PRIszt "@%d", input_level.size(),
475
36.3k
                    input_level.level);
476
36.3k
  }
477
21.2k
  snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
478
21.2k
           " files to L%d", output_level());
479
480
21.2k
  return scratch->buffer;
481
21.2k
}
482
483
32.0k
uint64_t Compaction::CalculateTotalInputSize() const {
484
32.0k
  uint64_t size = 0;
485
72.9k
  for (auto& input_level : inputs_) {
486
111k
    for (auto f : input_level.files) {
487
111k
      size += f->fd.GetTotalFileSize();
488
111k
    }
489
72.9k
  }
490
32.0k
  return size;
491
32.0k
}
492
493
22.9k
void Compaction::ReleaseCompactionFiles(Status status) {
494
22.9k
  MarkFilesBeingCompacted(false);
495
22.9k
  cfd_->compaction_picker()->ReleaseCompactionFiles(this, status);
496
22.9k
}
497
498
112
void Compaction::ResetNextCompactionIndex() {
499
112
  if (!IsCompactionStyleUniversal()) {
500
71
    DCHECK_ONLY_NOTNULL(input_version_);
501
71
    input_version_->storage_info()->ResetNextCompactionIndex(start_level_);
502
71
  }
503
112
}
504
505
namespace {
506
int InputSummary(const std::vector<FileMetaData*>& files, char* output,
507
31.1k
                 int len) {
508
31.1k
  *output = '\0';
509
31.1k
  int write = 0;
510
76.7k
  for (size_t i = 0; i < files.size(); i++) {
511
45.6k
    int sz = len - write;
512
45.6k
    int ret;
513
45.6k
    char sztxt[16];
514
45.6k
    AppendHumanBytes(files.at(i)->fd.GetTotalFileSize(), sztxt, 16);
515
45.6k
    ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
516
45.6k
                   files.at(i)->fd.GetNumber(), sztxt);
517
45.6k
    if (ret < 0 || ret >= sz) break;
518
45.6k
    write += ret;
519
45.6k
  }
520
  // if files.size() is non-zero, overwrite the last space
521
31.1k
  return write - !!files.size();
522
31.1k
}
523
}  // namespace
524
525
10.6k
void Compaction::Summary(char* output, int len) {
526
10.6k
  int write =
527
10.6k
      snprintf(output, len, "Base version %" PRIu64
528
10.6k
                            " Base level %d, inputs: [",
529
10.6k
               input_version_number_, start_level_);
530
10.6k
  if (write < 0 || write >= len) {
531
0
    return;
532
0
  }
533
534
41.8k
  for (size_t level_iter = 0; level_iter < num_input_levels(); ++level_iter) {
535
31.1k
    if (level_iter > 0) {
536
20.4k
      write += snprintf(output + write, len - write, "], [");
537
20.4k
      if (write < 0 || write >= len) {
538
0
        return;
539
0
      }
540
31.1k
    }
541
31.1k
    write +=
542
31.1k
        InputSummary(inputs_[level_iter].files, output + write, len - write);
543
31.1k
    if (write < 0 || write >= len) {
544
0
      return;
545
0
    }
546
31.1k
  }
547
548
10.6k
  snprintf(output + write, len - write, "]");
549
10.6k
}
550
551
22.0k
uint64_t Compaction::OutputFilePreallocationSize() {
552
22.0k
  uint64_t preallocation_size = 0;
553
554
22.0k
  if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
555
19.9k
      output_level() > 0) {
556
19.9k
    preallocation_size = max_output_file_size_;
557
2.19k
  } else {
558
    // output_level() == 0
559
2.19k
    assert(num_input_levels() > 0);
560
7.37k
    for (const auto& f : inputs_[0].files) {
561
7.37k
      preallocation_size += f->fd.GetTotalFileSize();
562
7.37k
    }
563
2.19k
  }
564
22.0k
  constexpr uint64_t kMaxPreAllocationSize = 1_GB;
565
  // Over-estimate slightly so we don't end up just barely crossing
566
  // the threshold
567
22.0k
  return std::min(kMaxPreAllocationSize, preallocation_size + (preallocation_size / 10));
568
22.0k
}
569
570
10.6k
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
571
10.6k
  if (!cfd_->ioptions()->compaction_filter_factory) {
572
8.25k
    return nullptr;
573
8.25k
  }
574
575
2.43k
  CompactionFilter::Context context;
576
2.43k
  context.is_full_compaction = is_full_compaction_;
577
2.43k
  context.is_manual_compaction = is_manual_compaction_;
578
2.43k
  context.column_family_id = cfd_->GetID();
579
2.43k
  return cfd_->ioptions()->compaction_filter_factory->CreateCompactionFilter(
580
2.43k
      context);
581
2.43k
}
582
583
281
bool Compaction::IsOutputLevelEmpty() const {
584
281
  return inputs_.back().level != output_level_ || inputs_.back().empty();
585
281
}
586
587
21.3k
bool Compaction::ShouldFormSubcompactions() const {
588
21.3k
  if (mutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr) {
589
18.6k
    return false;
590
18.6k
  }
591
2.74k
  if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
592
499
    return start_level_ == 0 && !IsOutputLevelEmpty();
593
2.24k
  } else if (IsCompactionStyleUniversal()) {
594
2.24k
    return number_levels_ > 1 && output_level_ > 0;
595
0
  } else {
596
0
    return false;
597
0
  }
598
2.74k
}
599
600
}  // namespace rocksdb