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 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 7 // Use of this source code is governed by a BSD-style license that can be 8 // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 10 #pragma once 11 12 #include <memory> 13 #include <set> 14 #include <string> 15 #include <unordered_set> 16 #include <vector> 17 18 #include "db/compaction/compaction.h" 19 #include "db/version_set.h" 20 #include "options/cf_options.h" 21 #include "rocksdb/env.h" 22 #include "rocksdb/options.h" 23 #include "rocksdb/status.h" 24 25 namespace ROCKSDB_NAMESPACE { 26 27 // The file contains an abstract class CompactionPicker, and its two 28 // sub-classes LevelCompactionPicker and NullCompactionPicker, as 29 // well as some helper functions used by them. 30 31 class LogBuffer; 32 class Compaction; 33 class VersionStorageInfo; 34 struct CompactionInputFiles; 35 36 // An abstract class to pick compactions from an existing LSM-tree. 37 // 38 // Each compaction style inherits the class and implement the 39 // interface to form automatic compactions. If NeedCompaction() is true, 40 // then call PickCompaction() to find what files need to be compacted 41 // and where to put the output files. 42 // 43 // Non-virtual functions CompactRange() and CompactFiles() are used to 44 // pick files to compact based on users' DB::CompactRange() and 45 // DB::CompactFiles() requests, respectively. There is little 46 // compaction style specific logic for them. 47 class CompactionPicker { 48 public: 49 CompactionPicker(const ImmutableCFOptions& ioptions, 50 const InternalKeyComparator* icmp); 51 virtual ~CompactionPicker(); 52 53 // Pick level and inputs for a new compaction. 54 // Returns nullptr if there is no compaction to be done. 55 // Otherwise returns a pointer to a heap-allocated object that 56 // describes the compaction. Caller should delete the result. 57 virtual Compaction* PickCompaction( 58 const std::string& cf_name, const MutableCFOptions& mutable_cf_options, 59 VersionStorageInfo* vstorage, LogBuffer* log_buffer, 60 SequenceNumber earliest_memtable_seqno = kMaxSequenceNumber) = 0; 61 62 // Return a compaction object for compacting the range [begin,end] in 63 // the specified level. Returns nullptr if there is nothing in that 64 // level that overlaps the specified range. Caller should delete 65 // the result. 66 // 67 // The returned Compaction might not include the whole requested range. 68 // In that case, compaction_end will be set to the next key that needs 69 // compacting. In case the compaction will compact the whole range, 70 // compaction_end will be set to nullptr. 71 // Client is responsible for compaction_end storage -- when called, 72 // *compaction_end should point to valid InternalKey! 73 virtual Compaction* CompactRange( 74 const std::string& cf_name, const MutableCFOptions& mutable_cf_options, 75 VersionStorageInfo* vstorage, int input_level, int output_level, 76 const CompactRangeOptions& compact_range_options, 77 const InternalKey* begin, const InternalKey* end, 78 InternalKey** compaction_end, bool* manual_conflict, 79 uint64_t max_file_num_to_ignore); 80 81 // The maximum allowed output level. Default value is NumberLevels() - 1. MaxOutputLevel()82 virtual int MaxOutputLevel() const { return NumberLevels() - 1; } 83 84 virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const = 0; 85 86 // Sanitize the input set of compaction input files. 87 // When the input parameters do not describe a valid compaction, the 88 // function will try to fix the input_files by adding necessary 89 // files. If it's not possible to conver an invalid input_files 90 // into a valid one by adding more files, the function will return a 91 // non-ok status with specific reason. 92 #ifndef ROCKSDB_LITE 93 Status SanitizeCompactionInputFiles(std::unordered_set<uint64_t>* input_files, 94 const ColumnFamilyMetaData& cf_meta, 95 const int output_level) const; 96 #endif // ROCKSDB_LITE 97 98 // Free up the files that participated in a compaction 99 // 100 // Requirement: DB mutex held 101 void ReleaseCompactionFiles(Compaction* c, Status status); 102 103 // Returns true if any one of the specified files are being compacted 104 bool AreFilesInCompaction(const std::vector<FileMetaData*>& files); 105 106 // Takes a list of CompactionInputFiles and returns a (manual) Compaction 107 // object. 108 // 109 // Caller must provide a set of input files that has been passed through 110 // `SanitizeCompactionInputFiles` earlier. The lock should not be released 111 // between that call and this one. 112 Compaction* CompactFiles(const CompactionOptions& compact_options, 113 const std::vector<CompactionInputFiles>& input_files, 114 int output_level, VersionStorageInfo* vstorage, 115 const MutableCFOptions& mutable_cf_options, 116 uint32_t output_path_id); 117 118 // Converts a set of compaction input file numbers into 119 // a list of CompactionInputFiles. 120 Status GetCompactionInputsFromFileNumbers( 121 std::vector<CompactionInputFiles>* input_files, 122 std::unordered_set<uint64_t>* input_set, 123 const VersionStorageInfo* vstorage, 124 const CompactionOptions& compact_options) const; 125 126 // Is there currently a compaction involving level 0 taking place IsLevel0CompactionInProgress()127 bool IsLevel0CompactionInProgress() const { 128 return !level0_compactions_in_progress_.empty(); 129 } 130 131 // Return true if the passed key range overlap with a compaction output 132 // that is currently running. 133 bool RangeOverlapWithCompaction(const Slice& smallest_user_key, 134 const Slice& largest_user_key, 135 int level) const; 136 137 // Stores the minimal range that covers all entries in inputs in 138 // *smallest, *largest. 139 // REQUIRES: inputs is not empty 140 void GetRange(const CompactionInputFiles& inputs, InternalKey* smallest, 141 InternalKey* largest) const; 142 143 // Stores the minimal range that covers all entries in inputs1 and inputs2 144 // in *smallest, *largest. 145 // REQUIRES: inputs is not empty 146 void GetRange(const CompactionInputFiles& inputs1, 147 const CompactionInputFiles& inputs2, InternalKey* smallest, 148 InternalKey* largest) const; 149 150 // Stores the minimal range that covers all entries in inputs 151 // in *smallest, *largest. 152 // REQUIRES: inputs is not empty (at least on entry have one file) 153 void GetRange(const std::vector<CompactionInputFiles>& inputs, 154 InternalKey* smallest, InternalKey* largest) const; 155 NumberLevels()156 int NumberLevels() const { return ioptions_.num_levels; } 157 158 // Add more files to the inputs on "level" to make sure that 159 // no newer version of a key is compacted to "level+1" while leaving an older 160 // version in a "level". Otherwise, any Get() will search "level" first, 161 // and will likely return an old/stale value for the key, since it always 162 // searches in increasing order of level to find the value. This could 163 // also scramble the order of merge operands. This function should be 164 // called any time a new Compaction is created, and its inputs_[0] are 165 // populated. 166 // 167 // Will return false if it is impossible to apply this compaction. 168 bool ExpandInputsToCleanCut(const std::string& cf_name, 169 VersionStorageInfo* vstorage, 170 CompactionInputFiles* inputs, 171 InternalKey** next_smallest = nullptr); 172 173 // Returns true if any one of the parent files are being compacted 174 bool IsRangeInCompaction(VersionStorageInfo* vstorage, 175 const InternalKey* smallest, 176 const InternalKey* largest, int level, int* index); 177 178 // Returns true if the key range that `inputs` files cover overlap with the 179 // key range of a currently running compaction. 180 bool FilesRangeOverlapWithCompaction( 181 const std::vector<CompactionInputFiles>& inputs, int level) const; 182 183 bool SetupOtherInputs(const std::string& cf_name, 184 const MutableCFOptions& mutable_cf_options, 185 VersionStorageInfo* vstorage, 186 CompactionInputFiles* inputs, 187 CompactionInputFiles* output_level_inputs, 188 int* parent_index, int base_index); 189 190 void GetGrandparents(VersionStorageInfo* vstorage, 191 const CompactionInputFiles& inputs, 192 const CompactionInputFiles& output_level_inputs, 193 std::vector<FileMetaData*>* grandparents); 194 195 void PickFilesMarkedForCompaction(const std::string& cf_name, 196 VersionStorageInfo* vstorage, 197 int* start_level, int* output_level, 198 CompactionInputFiles* start_level_inputs); 199 200 bool GetOverlappingL0Files(VersionStorageInfo* vstorage, 201 CompactionInputFiles* start_level_inputs, 202 int output_level, int* parent_index); 203 204 // Register this compaction in the set of running compactions 205 void RegisterCompaction(Compaction* c); 206 207 // Remove this compaction from the set of running compactions 208 void UnregisterCompaction(Compaction* c); 209 level0_compactions_in_progress()210 std::set<Compaction*>* level0_compactions_in_progress() { 211 return &level0_compactions_in_progress_; 212 } compactions_in_progress()213 std::unordered_set<Compaction*>* compactions_in_progress() { 214 return &compactions_in_progress_; 215 } 216 217 protected: 218 const ImmutableCFOptions& ioptions_; 219 220 // A helper function to SanitizeCompactionInputFiles() that 221 // sanitizes "input_files" by adding necessary files. 222 #ifndef ROCKSDB_LITE 223 virtual Status SanitizeCompactionInputFilesForAllLevels( 224 std::unordered_set<uint64_t>* input_files, 225 const ColumnFamilyMetaData& cf_meta, const int output_level) const; 226 #endif // ROCKSDB_LITE 227 228 // Keeps track of all compactions that are running on Level0. 229 // Protected by DB mutex 230 std::set<Compaction*> level0_compactions_in_progress_; 231 232 // Keeps track of all compactions that are running. 233 // Protected by DB mutex 234 std::unordered_set<Compaction*> compactions_in_progress_; 235 236 const InternalKeyComparator* const icmp_; 237 }; 238 239 #ifndef ROCKSDB_LITE 240 // A dummy compaction that never triggers any automatic 241 // compaction. 242 class NullCompactionPicker : public CompactionPicker { 243 public: NullCompactionPicker(const ImmutableCFOptions & ioptions,const InternalKeyComparator * icmp)244 NullCompactionPicker(const ImmutableCFOptions& ioptions, 245 const InternalKeyComparator* icmp) 246 : CompactionPicker(ioptions, icmp) {} ~NullCompactionPicker()247 virtual ~NullCompactionPicker() {} 248 249 // Always return "nullptr" PickCompaction(const std::string &,const MutableCFOptions &,VersionStorageInfo *,LogBuffer *,SequenceNumber)250 Compaction* PickCompaction( 251 const std::string& /*cf_name*/, 252 const MutableCFOptions& /*mutable_cf_options*/, 253 VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */, 254 SequenceNumber /* earliest_memtable_seqno */) override { 255 return nullptr; 256 } 257 258 // Always return "nullptr" CompactRange(const std::string &,const MutableCFOptions &,VersionStorageInfo *,int,int,const CompactRangeOptions &,const InternalKey *,const InternalKey *,InternalKey **,bool *,uint64_t)259 Compaction* CompactRange(const std::string& /*cf_name*/, 260 const MutableCFOptions& /*mutable_cf_options*/, 261 VersionStorageInfo* /*vstorage*/, 262 int /*input_level*/, int /*output_level*/, 263 const CompactRangeOptions& /*compact_range_options*/, 264 const InternalKey* /*begin*/, 265 const InternalKey* /*end*/, 266 InternalKey** /*compaction_end*/, 267 bool* /*manual_conflict*/, 268 uint64_t /*max_file_num_to_ignore*/) override { 269 return nullptr; 270 } 271 272 // Always returns false. NeedsCompaction(const VersionStorageInfo *)273 virtual bool NeedsCompaction( 274 const VersionStorageInfo* /*vstorage*/) const override { 275 return false; 276 } 277 }; 278 #endif // !ROCKSDB_LITE 279 280 // Attempts to find an intra L0 compaction conforming to the given parameters. 281 // 282 // @param level_files Metadata for L0 files. 283 // @param min_files_to_compact Minimum number of files required to 284 // do the compaction. 285 // @param max_compact_bytes_per_del_file Maximum average size in bytes per 286 // file that is going to get deleted by 287 // the compaction. 288 // @param max_compaction_bytes Maximum total size in bytes (in terms 289 // of compensated file size) for files 290 // to be compacted. 291 // @param [out] comp_inputs If a compaction was found, will be 292 // initialized with corresponding input 293 // files. Cannot be nullptr. 294 // 295 // @return true iff compaction was found. 296 bool FindIntraL0Compaction( 297 const std::vector<FileMetaData*>& level_files, size_t min_files_to_compact, 298 uint64_t max_compact_bytes_per_del_file, uint64_t max_compaction_bytes, 299 CompactionInputFiles* comp_inputs, 300 SequenceNumber earliest_mem_seqno = kMaxSequenceNumber); 301 302 CompressionType GetCompressionType(const ImmutableCFOptions& ioptions, 303 const VersionStorageInfo* vstorage, 304 const MutableCFOptions& mutable_cf_options, 305 int level, int base_level, 306 const bool enable_compression = true); 307 308 CompressionOptions GetCompressionOptions(const ImmutableCFOptions& ioptions, 309 const VersionStorageInfo* vstorage, 310 int level, 311 const bool enable_compression = true); 312 313 } // namespace ROCKSDB_NAMESPACE 314