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 #include <algorithm>
11 #include <cinttypes>
12 #include <functional>
13 #include <list>
14 #include <memory>
15 #include <random>
16 #include <set>
17 #include <thread>
18 #include <utility>
19 #include <vector>
20 
21 #include "db/builder.h"
22 #include "db/compaction/compaction_job.h"
23 #include "db/db_impl/db_impl.h"
24 #include "db/db_iter.h"
25 #include "db/dbformat.h"
26 #include "db/error_handler.h"
27 #include "db/event_helpers.h"
28 #include "db/log_reader.h"
29 #include "db/log_writer.h"
30 #include "db/memtable.h"
31 #include "db/memtable_list.h"
32 #include "db/merge_context.h"
33 #include "db/merge_helper.h"
34 #include "db/range_del_aggregator.h"
35 #include "db/version_set.h"
36 #include "file/filename.h"
37 #include "file/read_write_util.h"
38 #include "file/sst_file_manager_impl.h"
39 #include "file/writable_file_writer.h"
40 #include "logging/log_buffer.h"
41 #include "logging/logging.h"
42 #include "monitoring/iostats_context_imp.h"
43 #include "monitoring/perf_context_imp.h"
44 #include "monitoring/thread_status_util.h"
45 #include "port/port.h"
46 #include "rocksdb/db.h"
47 #include "rocksdb/env.h"
48 #include "rocksdb/statistics.h"
49 #include "rocksdb/status.h"
50 #include "rocksdb/table.h"
51 #include "table/block_based/block.h"
52 #include "table/block_based/block_based_table_factory.h"
53 #include "table/merging_iterator.h"
54 #include "table/table_builder.h"
55 #include "test_util/sync_point.h"
56 #include "util/coding.h"
57 #include "util/mutexlock.h"
58 #include "util/random.h"
59 #include "util/stop_watch.h"
60 #include "util/string_util.h"
61 
62 namespace ROCKSDB_NAMESPACE {
63 
GetCompactionReasonString(CompactionReason compaction_reason)64 const char* GetCompactionReasonString(CompactionReason compaction_reason) {
65   switch (compaction_reason) {
66     case CompactionReason::kUnknown:
67       return "Unknown";
68     case CompactionReason::kLevelL0FilesNum:
69       return "LevelL0FilesNum";
70     case CompactionReason::kLevelMaxLevelSize:
71       return "LevelMaxLevelSize";
72     case CompactionReason::kUniversalSizeAmplification:
73       return "UniversalSizeAmplification";
74     case CompactionReason::kUniversalSizeRatio:
75       return "UniversalSizeRatio";
76     case CompactionReason::kUniversalSortedRunNum:
77       return "UniversalSortedRunNum";
78     case CompactionReason::kFIFOMaxSize:
79       return "FIFOMaxSize";
80     case CompactionReason::kFIFOReduceNumFiles:
81       return "FIFOReduceNumFiles";
82     case CompactionReason::kFIFOTtl:
83       return "FIFOTtl";
84     case CompactionReason::kManualCompaction:
85       return "ManualCompaction";
86     case CompactionReason::kFilesMarkedForCompaction:
87       return "FilesMarkedForCompaction";
88     case CompactionReason::kBottommostFiles:
89       return "BottommostFiles";
90     case CompactionReason::kTtl:
91       return "Ttl";
92     case CompactionReason::kFlush:
93       return "Flush";
94     case CompactionReason::kExternalSstIngestion:
95       return "ExternalSstIngestion";
96     case CompactionReason::kPeriodicCompaction:
97       return "PeriodicCompaction";
98     case CompactionReason::kNumOfReasons:
99       // fall through
100     default:
101       assert(false);
102       return "Invalid";
103   }
104 }
105 
106 // Maintains state for each sub-compaction
107 struct CompactionJob::SubcompactionState {
108   const Compaction* compaction;
109   std::unique_ptr<CompactionIterator> c_iter;
110 
111   // The boundaries of the key-range this compaction is interested in. No two
112   // subcompactions may have overlapping key-ranges.
113   // 'start' is inclusive, 'end' is exclusive, and nullptr means unbounded
114   Slice *start, *end;
115 
116   // The return status of this subcompaction
117   Status status;
118 
119   // Files produced by this subcompaction
120   struct Output {
121     FileMetaData meta;
122     bool finished;
123     std::shared_ptr<const TableProperties> table_properties;
124   };
125 
126   // State kept for output being generated
127   std::vector<Output> outputs;
128   std::unique_ptr<WritableFileWriter> outfile;
129   std::unique_ptr<TableBuilder> builder;
current_outputROCKSDB_NAMESPACE::CompactionJob::SubcompactionState130   Output* current_output() {
131     if (outputs.empty()) {
132       // This subcompaction's outptut could be empty if compaction was aborted
133       // before this subcompaction had a chance to generate any output files.
134       // When subcompactions are executed sequentially this is more likely and
135       // will be particulalry likely for the later subcompactions to be empty.
136       // Once they are run in parallel however it should be much rarer.
137       return nullptr;
138     } else {
139       return &outputs.back();
140     }
141   }
142 
143   uint64_t current_output_file_size;
144 
145   // State during the subcompaction
146   uint64_t total_bytes;
147   uint64_t num_output_records;
148   CompactionJobStats compaction_job_stats;
149   uint64_t approx_size;
150   // An index that used to speed up ShouldStopBefore().
151   size_t grandparent_index = 0;
152   // The number of bytes overlapping between the current output and
153   // grandparent files used in ShouldStopBefore().
154   uint64_t overlapped_bytes = 0;
155   // A flag determine whether the key has been seen in ShouldStopBefore()
156   bool seen_key = false;
157 
SubcompactionStateROCKSDB_NAMESPACE::CompactionJob::SubcompactionState158   SubcompactionState(Compaction* c, Slice* _start, Slice* _end,
159                      uint64_t size = 0)
160       : compaction(c),
161         start(_start),
162         end(_end),
163         outfile(nullptr),
164         builder(nullptr),
165         current_output_file_size(0),
166         total_bytes(0),
167         num_output_records(0),
168         approx_size(size),
169         grandparent_index(0),
170         overlapped_bytes(0),
171         seen_key(false) {
172     assert(compaction != nullptr);
173   }
174 
SubcompactionStateROCKSDB_NAMESPACE::CompactionJob::SubcompactionState175   SubcompactionState(SubcompactionState&& o) { *this = std::move(o); }
176 
operator =ROCKSDB_NAMESPACE::CompactionJob::SubcompactionState177   SubcompactionState& operator=(SubcompactionState&& o) {
178     compaction = std::move(o.compaction);
179     start = std::move(o.start);
180     end = std::move(o.end);
181     status = std::move(o.status);
182     outputs = std::move(o.outputs);
183     outfile = std::move(o.outfile);
184     builder = std::move(o.builder);
185     current_output_file_size = std::move(o.current_output_file_size);
186     total_bytes = std::move(o.total_bytes);
187     num_output_records = std::move(o.num_output_records);
188     compaction_job_stats = std::move(o.compaction_job_stats);
189     approx_size = std::move(o.approx_size);
190     grandparent_index = std::move(o.grandparent_index);
191     overlapped_bytes = std::move(o.overlapped_bytes);
192     seen_key = std::move(o.seen_key);
193     return *this;
194   }
195 
196   // Because member std::unique_ptrs do not have these.
197   SubcompactionState(const SubcompactionState&) = delete;
198 
199   SubcompactionState& operator=(const SubcompactionState&) = delete;
200 
201   // Returns true iff we should stop building the current output
202   // before processing "internal_key".
ShouldStopBeforeROCKSDB_NAMESPACE::CompactionJob::SubcompactionState203   bool ShouldStopBefore(const Slice& internal_key, uint64_t curr_file_size) {
204     const InternalKeyComparator* icmp =
205         &compaction->column_family_data()->internal_comparator();
206     const std::vector<FileMetaData*>& grandparents = compaction->grandparents();
207 
208     // Scan to find earliest grandparent file that contains key.
209     while (grandparent_index < grandparents.size() &&
210            icmp->Compare(internal_key,
211                          grandparents[grandparent_index]->largest.Encode()) >
212                0) {
213       if (seen_key) {
214         overlapped_bytes += grandparents[grandparent_index]->fd.GetFileSize();
215       }
216       assert(grandparent_index + 1 >= grandparents.size() ||
217              icmp->Compare(
218                  grandparents[grandparent_index]->largest.Encode(),
219                  grandparents[grandparent_index + 1]->smallest.Encode()) <= 0);
220       grandparent_index++;
221     }
222     seen_key = true;
223 
224     if (overlapped_bytes + curr_file_size >
225         compaction->max_compaction_bytes()) {
226       // Too much overlap for current output; start new output
227       overlapped_bytes = 0;
228       return true;
229     }
230 
231     return false;
232   }
233 };
234 
235 // Maintains state for the entire compaction
236 struct CompactionJob::CompactionState {
237   Compaction* const compaction;
238 
239   // REQUIRED: subcompaction states are stored in order of increasing
240   // key-range
241   std::vector<CompactionJob::SubcompactionState> sub_compact_states;
242   Status status;
243 
244   uint64_t total_bytes;
245   uint64_t num_output_records;
246 
CompactionStateROCKSDB_NAMESPACE::CompactionJob::CompactionState247   explicit CompactionState(Compaction* c)
248       : compaction(c),
249         total_bytes(0),
250         num_output_records(0) {}
251 
NumOutputFilesROCKSDB_NAMESPACE::CompactionJob::CompactionState252   size_t NumOutputFiles() {
253     size_t total = 0;
254     for (auto& s : sub_compact_states) {
255       total += s.outputs.size();
256     }
257     return total;
258   }
259 
SmallestUserKeyROCKSDB_NAMESPACE::CompactionJob::CompactionState260   Slice SmallestUserKey() {
261     for (const auto& sub_compact_state : sub_compact_states) {
262       if (!sub_compact_state.outputs.empty() &&
263           sub_compact_state.outputs[0].finished) {
264         return sub_compact_state.outputs[0].meta.smallest.user_key();
265       }
266     }
267     // If there is no finished output, return an empty slice.
268     return Slice(nullptr, 0);
269   }
270 
LargestUserKeyROCKSDB_NAMESPACE::CompactionJob::CompactionState271   Slice LargestUserKey() {
272     for (auto it = sub_compact_states.rbegin(); it < sub_compact_states.rend();
273          ++it) {
274       if (!it->outputs.empty() && it->current_output()->finished) {
275         assert(it->current_output() != nullptr);
276         return it->current_output()->meta.largest.user_key();
277       }
278     }
279     // If there is no finished output, return an empty slice.
280     return Slice(nullptr, 0);
281   }
282 };
283 
AggregateStatistics()284 void CompactionJob::AggregateStatistics() {
285   for (SubcompactionState& sc : compact_->sub_compact_states) {
286     compact_->total_bytes += sc.total_bytes;
287     compact_->num_output_records += sc.num_output_records;
288   }
289   if (compaction_job_stats_) {
290     for (SubcompactionState& sc : compact_->sub_compact_states) {
291       compaction_job_stats_->Add(sc.compaction_job_stats);
292     }
293   }
294 }
295 
CompactionJob(int job_id,Compaction * compaction,const ImmutableDBOptions & db_options,const FileOptions & file_options,VersionSet * versions,const std::atomic<bool> * shutting_down,const SequenceNumber preserve_deletes_seqnum,LogBuffer * log_buffer,FSDirectory * db_directory,FSDirectory * output_directory,Statistics * stats,InstrumentedMutex * db_mutex,ErrorHandler * db_error_handler,std::vector<SequenceNumber> existing_snapshots,SequenceNumber earliest_write_conflict_snapshot,const SnapshotChecker * snapshot_checker,std::shared_ptr<Cache> table_cache,EventLogger * event_logger,bool paranoid_file_checks,bool measure_io_stats,const std::string & dbname,CompactionJobStats * compaction_job_stats,Env::Priority thread_pri,const std::atomic<bool> * manual_compaction_paused)296 CompactionJob::CompactionJob(
297     int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
298     const FileOptions& file_options, VersionSet* versions,
299     const std::atomic<bool>* shutting_down,
300     const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
301     FSDirectory* db_directory, FSDirectory* output_directory, Statistics* stats,
302     InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
303     std::vector<SequenceNumber> existing_snapshots,
304     SequenceNumber earliest_write_conflict_snapshot,
305     const SnapshotChecker* snapshot_checker, std::shared_ptr<Cache> table_cache,
306     EventLogger* event_logger, bool paranoid_file_checks, bool measure_io_stats,
307     const std::string& dbname, CompactionJobStats* compaction_job_stats,
308     Env::Priority thread_pri, const std::atomic<bool>* manual_compaction_paused)
309     : job_id_(job_id),
310       compact_(new CompactionState(compaction)),
311       compaction_job_stats_(compaction_job_stats),
312       compaction_stats_(compaction->compaction_reason(), 1),
313       dbname_(dbname),
314       db_options_(db_options),
315       file_options_(file_options),
316       env_(db_options.env),
317       fs_(db_options.fs.get()),
318       file_options_for_read_(
319           fs_->OptimizeForCompactionTableRead(file_options, db_options_)),
320       versions_(versions),
321       shutting_down_(shutting_down),
322       manual_compaction_paused_(manual_compaction_paused),
323       preserve_deletes_seqnum_(preserve_deletes_seqnum),
324       log_buffer_(log_buffer),
325       db_directory_(db_directory),
326       output_directory_(output_directory),
327       stats_(stats),
328       db_mutex_(db_mutex),
329       db_error_handler_(db_error_handler),
330       existing_snapshots_(std::move(existing_snapshots)),
331       earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
332       snapshot_checker_(snapshot_checker),
333       table_cache_(std::move(table_cache)),
334       event_logger_(event_logger),
335       bottommost_level_(false),
336       paranoid_file_checks_(paranoid_file_checks),
337       measure_io_stats_(measure_io_stats),
338       write_hint_(Env::WLTH_NOT_SET),
339       thread_pri_(thread_pri) {
340   assert(log_buffer_ != nullptr);
341   const auto* cfd = compact_->compaction->column_family_data();
342   ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
343                                     db_options_.enable_thread_tracking);
344   ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
345   ReportStartedCompaction(compaction);
346 }
347 
~CompactionJob()348 CompactionJob::~CompactionJob() {
349   assert(compact_ == nullptr);
350   ThreadStatusUtil::ResetThreadStatus();
351 }
352 
ReportStartedCompaction(Compaction * compaction)353 void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
354   const auto* cfd = compact_->compaction->column_family_data();
355   ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
356                                     db_options_.enable_thread_tracking);
357 
358   ThreadStatusUtil::SetThreadOperationProperty(ThreadStatus::COMPACTION_JOB_ID,
359                                                job_id_);
360 
361   ThreadStatusUtil::SetThreadOperationProperty(
362       ThreadStatus::COMPACTION_INPUT_OUTPUT_LEVEL,
363       (static_cast<uint64_t>(compact_->compaction->start_level()) << 32) +
364           compact_->compaction->output_level());
365 
366   // In the current design, a CompactionJob is always created
367   // for non-trivial compaction.
368   assert(compaction->IsTrivialMove() == false ||
369          compaction->is_manual_compaction() == true);
370 
371   ThreadStatusUtil::SetThreadOperationProperty(
372       ThreadStatus::COMPACTION_PROP_FLAGS,
373       compaction->is_manual_compaction() +
374           (compaction->deletion_compaction() << 1));
375 
376   ThreadStatusUtil::SetThreadOperationProperty(
377       ThreadStatus::COMPACTION_TOTAL_INPUT_BYTES,
378       compaction->CalculateTotalInputSize());
379 
380   IOSTATS_RESET(bytes_written);
381   IOSTATS_RESET(bytes_read);
382   ThreadStatusUtil::SetThreadOperationProperty(
383       ThreadStatus::COMPACTION_BYTES_WRITTEN, 0);
384   ThreadStatusUtil::SetThreadOperationProperty(
385       ThreadStatus::COMPACTION_BYTES_READ, 0);
386 
387   // Set the thread operation after operation properties
388   // to ensure GetThreadList() can always show them all together.
389   ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
390 
391   if (compaction_job_stats_) {
392     compaction_job_stats_->is_manual_compaction =
393         compaction->is_manual_compaction();
394   }
395 }
396 
Prepare()397 void CompactionJob::Prepare() {
398   AutoThreadOperationStageUpdater stage_updater(
399       ThreadStatus::STAGE_COMPACTION_PREPARE);
400 
401   // Generate file_levels_ for compaction berfore making Iterator
402   auto* c = compact_->compaction;
403   assert(c->column_family_data() != nullptr);
404   assert(c->column_family_data()->current()->storage_info()->NumLevelFiles(
405              compact_->compaction->level()) > 0);
406 
407   write_hint_ =
408       c->column_family_data()->CalculateSSTWriteHint(c->output_level());
409   bottommost_level_ = c->bottommost_level();
410 
411   if (c->ShouldFormSubcompactions()) {
412     {
413       StopWatch sw(env_, stats_, SUBCOMPACTION_SETUP_TIME);
414       GenSubcompactionBoundaries();
415     }
416     assert(sizes_.size() == boundaries_.size() + 1);
417 
418     for (size_t i = 0; i <= boundaries_.size(); i++) {
419       Slice* start = i == 0 ? nullptr : &boundaries_[i - 1];
420       Slice* end = i == boundaries_.size() ? nullptr : &boundaries_[i];
421       compact_->sub_compact_states.emplace_back(c, start, end, sizes_[i]);
422     }
423     RecordInHistogram(stats_, NUM_SUBCOMPACTIONS_SCHEDULED,
424                       compact_->sub_compact_states.size());
425   } else {
426     compact_->sub_compact_states.emplace_back(c, nullptr, nullptr);
427   }
428 }
429 
430 struct RangeWithSize {
431   Range range;
432   uint64_t size;
433 
RangeWithSizeROCKSDB_NAMESPACE::RangeWithSize434   RangeWithSize(const Slice& a, const Slice& b, uint64_t s = 0)
435       : range(a, b), size(s) {}
436 };
437 
GenSubcompactionBoundaries()438 void CompactionJob::GenSubcompactionBoundaries() {
439   auto* c = compact_->compaction;
440   auto* cfd = c->column_family_data();
441   const Comparator* cfd_comparator = cfd->user_comparator();
442   std::vector<Slice> bounds;
443   int start_lvl = c->start_level();
444   int out_lvl = c->output_level();
445 
446   // Add the starting and/or ending key of certain input files as a potential
447   // boundary
448   for (size_t lvl_idx = 0; lvl_idx < c->num_input_levels(); lvl_idx++) {
449     int lvl = c->level(lvl_idx);
450     if (lvl >= start_lvl && lvl <= out_lvl) {
451       const LevelFilesBrief* flevel = c->input_levels(lvl_idx);
452       size_t num_files = flevel->num_files;
453 
454       if (num_files == 0) {
455         continue;
456       }
457 
458       if (lvl == 0) {
459         // For level 0 add the starting and ending key of each file since the
460         // files may have greatly differing key ranges (not range-partitioned)
461         for (size_t i = 0; i < num_files; i++) {
462           bounds.emplace_back(flevel->files[i].smallest_key);
463           bounds.emplace_back(flevel->files[i].largest_key);
464         }
465       } else {
466         // For all other levels add the smallest/largest key in the level to
467         // encompass the range covered by that level
468         bounds.emplace_back(flevel->files[0].smallest_key);
469         bounds.emplace_back(flevel->files[num_files - 1].largest_key);
470         if (lvl == out_lvl) {
471           // For the last level include the starting keys of all files since
472           // the last level is the largest and probably has the widest key
473           // range. Since it's range partitioned, the ending key of one file
474           // and the starting key of the next are very close (or identical).
475           for (size_t i = 1; i < num_files; i++) {
476             bounds.emplace_back(flevel->files[i].smallest_key);
477           }
478         }
479       }
480     }
481   }
482 
483   std::sort(bounds.begin(), bounds.end(),
484             [cfd_comparator](const Slice& a, const Slice& b) -> bool {
485               return cfd_comparator->Compare(ExtractUserKey(a),
486                                              ExtractUserKey(b)) < 0;
487             });
488   // Remove duplicated entries from bounds
489   bounds.erase(
490       std::unique(bounds.begin(), bounds.end(),
491                   [cfd_comparator](const Slice& a, const Slice& b) -> bool {
492                     return cfd_comparator->Compare(ExtractUserKey(a),
493                                                    ExtractUserKey(b)) == 0;
494                   }),
495       bounds.end());
496 
497   // Combine consecutive pairs of boundaries into ranges with an approximate
498   // size of data covered by keys in that range
499   uint64_t sum = 0;
500   std::vector<RangeWithSize> ranges;
501   // Get input version from CompactionState since it's already referenced
502   // earlier in SetInputVersioCompaction::SetInputVersion and will not change
503   // when db_mutex_ is released below
504   auto* v = compact_->compaction->input_version();
505   for (auto it = bounds.begin();;) {
506     const Slice a = *it;
507     ++it;
508 
509     if (it == bounds.end()) {
510       break;
511     }
512 
513     const Slice b = *it;
514 
515     // ApproximateSize could potentially create table reader iterator to seek
516     // to the index block and may incur I/O cost in the process. Unlock db
517     // mutex to reduce contention
518     db_mutex_->Unlock();
519     uint64_t size = versions_->ApproximateSize(SizeApproximationOptions(), v, a,
520                                                b, start_lvl, out_lvl + 1,
521                                                TableReaderCaller::kCompaction);
522     db_mutex_->Lock();
523     ranges.emplace_back(a, b, size);
524     sum += size;
525   }
526 
527   // Group the ranges into subcompactions
528   const double min_file_fill_percent = 4.0 / 5;
529   int base_level = v->storage_info()->base_level();
530   uint64_t max_output_files = static_cast<uint64_t>(std::ceil(
531       sum / min_file_fill_percent /
532       MaxFileSizeForLevel(*(c->mutable_cf_options()), out_lvl,
533           c->immutable_cf_options()->compaction_style, base_level,
534           c->immutable_cf_options()->level_compaction_dynamic_level_bytes)));
535   uint64_t subcompactions =
536       std::min({static_cast<uint64_t>(ranges.size()),
537                 static_cast<uint64_t>(c->max_subcompactions()),
538                 max_output_files});
539 
540   if (subcompactions > 1) {
541     double mean = sum * 1.0 / subcompactions;
542     // Greedily add ranges to the subcompaction until the sum of the ranges'
543     // sizes becomes >= the expected mean size of a subcompaction
544     sum = 0;
545     for (size_t i = 0; i < ranges.size() - 1; i++) {
546       sum += ranges[i].size;
547       if (subcompactions == 1) {
548         // If there's only one left to schedule then it goes to the end so no
549         // need to put an end boundary
550         continue;
551       }
552       if (sum >= mean) {
553         boundaries_.emplace_back(ExtractUserKey(ranges[i].range.limit));
554         sizes_.emplace_back(sum);
555         subcompactions--;
556         sum = 0;
557       }
558     }
559     sizes_.emplace_back(sum + ranges.back().size);
560   } else {
561     // Only one range so its size is the total sum of sizes computed above
562     sizes_.emplace_back(sum);
563   }
564 }
565 
Run()566 Status CompactionJob::Run() {
567   AutoThreadOperationStageUpdater stage_updater(
568       ThreadStatus::STAGE_COMPACTION_RUN);
569   TEST_SYNC_POINT("CompactionJob::Run():Start");
570   log_buffer_->FlushBufferToLog();
571   LogCompaction();
572 
573   const size_t num_threads = compact_->sub_compact_states.size();
574   assert(num_threads > 0);
575   const uint64_t start_micros = env_->NowMicros();
576 
577   // Launch a thread for each of subcompactions 1...num_threads-1
578   std::vector<port::Thread> thread_pool;
579   thread_pool.reserve(num_threads - 1);
580   for (size_t i = 1; i < compact_->sub_compact_states.size(); i++) {
581     thread_pool.emplace_back(&CompactionJob::ProcessKeyValueCompaction, this,
582                              &compact_->sub_compact_states[i]);
583   }
584 
585   // Always schedule the first subcompaction (whether or not there are also
586   // others) in the current thread to be efficient with resources
587   ProcessKeyValueCompaction(&compact_->sub_compact_states[0]);
588 
589   // Wait for all other threads (if there are any) to finish execution
590   for (auto& thread : thread_pool) {
591     thread.join();
592   }
593 
594   compaction_stats_.micros = env_->NowMicros() - start_micros;
595   compaction_stats_.cpu_micros = 0;
596   for (size_t i = 0; i < compact_->sub_compact_states.size(); i++) {
597     compaction_stats_.cpu_micros +=
598         compact_->sub_compact_states[i].compaction_job_stats.cpu_micros;
599   }
600 
601   RecordTimeToHistogram(stats_, COMPACTION_TIME, compaction_stats_.micros);
602   RecordTimeToHistogram(stats_, COMPACTION_CPU_TIME,
603                         compaction_stats_.cpu_micros);
604 
605   TEST_SYNC_POINT("CompactionJob::Run:BeforeVerify");
606 
607   // Check if any thread encountered an error during execution
608   Status status;
609   for (const auto& state : compact_->sub_compact_states) {
610     if (!state.status.ok()) {
611       status = state.status;
612       break;
613     }
614   }
615 
616   IOStatus io_s;
617   if (status.ok() && output_directory_) {
618     io_s = output_directory_->Fsync(IOOptions(), nullptr);
619   }
620   if (!io_s.ok()) {
621     io_status_ = io_s;
622     status = io_s;
623   }
624 
625   if (status.ok()) {
626     thread_pool.clear();
627     std::vector<const FileMetaData*> files_meta;
628     for (const auto& state : compact_->sub_compact_states) {
629       for (const auto& output : state.outputs) {
630         files_meta.emplace_back(&output.meta);
631       }
632     }
633     ColumnFamilyData* cfd = compact_->compaction->column_family_data();
634     auto prefix_extractor =
635         compact_->compaction->mutable_cf_options()->prefix_extractor.get();
636     std::atomic<size_t> next_file_meta_idx(0);
637     auto verify_table = [&](Status& output_status) {
638       while (true) {
639         size_t file_idx = next_file_meta_idx.fetch_add(1);
640         if (file_idx >= files_meta.size()) {
641           break;
642         }
643         // Verify that the table is usable
644         // We set for_compaction to false and don't OptimizeForCompactionTableRead
645         // here because this is a special case after we finish the table building
646         // No matter whether use_direct_io_for_flush_and_compaction is true,
647         // we will regard this verification as user reads since the goal is
648         // to cache it here for further user reads
649         InternalIterator* iter = cfd->table_cache()->NewIterator(
650             ReadOptions(), file_options_, cfd->internal_comparator(),
651             *files_meta[file_idx], /*range_del_agg=*/nullptr, prefix_extractor,
652             /*table_reader_ptr=*/nullptr,
653             cfd->internal_stats()->GetFileReadHist(
654                 compact_->compaction->output_level()),
655             TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
656             /*skip_filters=*/false, compact_->compaction->output_level(),
657             /*smallest_compaction_key=*/nullptr,
658             /*largest_compaction_key=*/nullptr);
659         auto s = iter->status();
660 
661         if (s.ok() && paranoid_file_checks_) {
662           for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {}
663           s = iter->status();
664         }
665 
666         delete iter;
667 
668         if (!s.ok()) {
669           output_status = s;
670           break;
671         }
672       }
673     };
674     for (size_t i = 1; i < compact_->sub_compact_states.size(); i++) {
675       thread_pool.emplace_back(verify_table,
676                                std::ref(compact_->sub_compact_states[i].status));
677     }
678     verify_table(compact_->sub_compact_states[0].status);
679     for (auto& thread : thread_pool) {
680       thread.join();
681     }
682     for (const auto& state : compact_->sub_compact_states) {
683       if (!state.status.ok()) {
684         status = state.status;
685         break;
686       }
687     }
688   }
689 
690   TablePropertiesCollection tp;
691   for (const auto& state : compact_->sub_compact_states) {
692     for (const auto& output : state.outputs) {
693       auto fn =
694           TableFileName(state.compaction->immutable_cf_options()->cf_paths,
695                         output.meta.fd.GetNumber(), output.meta.fd.GetPathId());
696       tp[fn] = output.table_properties;
697     }
698   }
699   compact_->compaction->SetOutputTableProperties(std::move(tp));
700 
701   // Finish up all book-keeping to unify the subcompaction results
702   AggregateStatistics();
703   UpdateCompactionStats();
704   RecordCompactionIOStats();
705   LogFlush(db_options_.info_log);
706   TEST_SYNC_POINT("CompactionJob::Run():End");
707 
708   compact_->status = status;
709   return status;
710 }
711 
Install(const MutableCFOptions & mutable_cf_options)712 Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
713   AutoThreadOperationStageUpdater stage_updater(
714       ThreadStatus::STAGE_COMPACTION_INSTALL);
715   db_mutex_->AssertHeld();
716   Status status = compact_->status;
717   ColumnFamilyData* cfd = compact_->compaction->column_family_data();
718   cfd->internal_stats()->AddCompactionStats(
719       compact_->compaction->output_level(), thread_pri_, compaction_stats_);
720 
721   versions_->SetIOStatusOK();
722   if (status.ok()) {
723     status = InstallCompactionResults(mutable_cf_options);
724   }
725   if (!versions_->io_status().ok()) {
726     io_status_ = versions_->io_status();
727   }
728   VersionStorageInfo::LevelSummaryStorage tmp;
729   auto vstorage = cfd->current()->storage_info();
730   const auto& stats = compaction_stats_;
731 
732   double read_write_amp = 0.0;
733   double write_amp = 0.0;
734   double bytes_read_per_sec = 0;
735   double bytes_written_per_sec = 0;
736 
737   if (stats.bytes_read_non_output_levels > 0) {
738     read_write_amp = (stats.bytes_written + stats.bytes_read_output_level +
739                       stats.bytes_read_non_output_levels) /
740                      static_cast<double>(stats.bytes_read_non_output_levels);
741     write_amp = stats.bytes_written /
742                 static_cast<double>(stats.bytes_read_non_output_levels);
743   }
744   if (stats.micros > 0) {
745     bytes_read_per_sec =
746         (stats.bytes_read_non_output_levels + stats.bytes_read_output_level) /
747         static_cast<double>(stats.micros);
748     bytes_written_per_sec =
749         stats.bytes_written / static_cast<double>(stats.micros);
750   }
751 
752   ROCKS_LOG_BUFFER(
753       log_buffer_,
754       "[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
755       "files in(%d, %d) out(%d) "
756       "MB in(%.1f, %.1f) out(%.1f), read-write-amplify(%.1f) "
757       "write-amplify(%.1f) %s, records in: %" PRIu64
758       ", records dropped: %" PRIu64 " output_compression: %s\n",
759       cfd->GetName().c_str(), vstorage->LevelSummary(&tmp), bytes_read_per_sec,
760       bytes_written_per_sec, compact_->compaction->output_level(),
761       stats.num_input_files_in_non_output_levels,
762       stats.num_input_files_in_output_level, stats.num_output_files,
763       stats.bytes_read_non_output_levels / 1048576.0,
764       stats.bytes_read_output_level / 1048576.0,
765       stats.bytes_written / 1048576.0, read_write_amp, write_amp,
766       status.ToString().c_str(), stats.num_input_records,
767       stats.num_dropped_records,
768       CompressionTypeToString(compact_->compaction->output_compression())
769           .c_str());
770 
771   UpdateCompactionJobStats(stats);
772 
773   auto stream = event_logger_->LogToBuffer(log_buffer_);
774   stream << "job" << job_id_ << "event"
775          << "compaction_finished"
776          << "compaction_time_micros" << stats.micros
777          << "compaction_time_cpu_micros" << stats.cpu_micros << "output_level"
778          << compact_->compaction->output_level() << "num_output_files"
779          << compact_->NumOutputFiles() << "total_output_size"
780          << compact_->total_bytes << "num_input_records"
781          << stats.num_input_records << "num_output_records"
782          << compact_->num_output_records << "num_subcompactions"
783          << compact_->sub_compact_states.size() << "output_compression"
784          << CompressionTypeToString(compact_->compaction->output_compression());
785 
786   if (compaction_job_stats_ != nullptr) {
787     stream << "num_single_delete_mismatches"
788            << compaction_job_stats_->num_single_del_mismatch;
789     stream << "num_single_delete_fallthrough"
790            << compaction_job_stats_->num_single_del_fallthru;
791   }
792 
793   if (measure_io_stats_ && compaction_job_stats_ != nullptr) {
794     stream << "file_write_nanos" << compaction_job_stats_->file_write_nanos;
795     stream << "file_range_sync_nanos"
796            << compaction_job_stats_->file_range_sync_nanos;
797     stream << "file_fsync_nanos" << compaction_job_stats_->file_fsync_nanos;
798     stream << "file_prepare_write_nanos"
799            << compaction_job_stats_->file_prepare_write_nanos;
800   }
801 
802   stream << "lsm_state";
803   stream.StartArray();
804   for (int level = 0; level < vstorage->num_levels(); ++level) {
805     stream << vstorage->NumLevelFiles(level);
806   }
807   stream.EndArray();
808 
809   CleanupCompaction();
810   return status;
811 }
812 
ProcessKeyValueCompaction(SubcompactionState * sub_compact)813 void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
814   assert(sub_compact != nullptr);
815 
816   uint64_t prev_cpu_micros = env_->NowCPUNanos() / 1000;
817 
818   ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
819 
820   // Create compaction filter and fail the compaction if
821   // IgnoreSnapshots() = false because it is not supported anymore
822   const CompactionFilter* compaction_filter =
823       cfd->ioptions()->compaction_filter;
824   std::unique_ptr<CompactionFilter> compaction_filter_from_factory = nullptr;
825   if (compaction_filter == nullptr) {
826     compaction_filter_from_factory =
827         sub_compact->compaction->CreateCompactionFilter();
828     compaction_filter = compaction_filter_from_factory.get();
829   }
830   if (compaction_filter != nullptr && !compaction_filter->IgnoreSnapshots()) {
831     sub_compact->status = Status::NotSupported(
832         "CompactionFilter::IgnoreSnapshots() = false is not supported "
833         "anymore.");
834     return;
835   }
836 
837   CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
838                                              existing_snapshots_);
839 
840   // Although the v2 aggregator is what the level iterator(s) know about,
841   // the AddTombstones calls will be propagated down to the v1 aggregator.
842   std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
843       sub_compact->compaction, &range_del_agg, file_options_for_read_));
844 
845   AutoThreadOperationStageUpdater stage_updater(
846       ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
847 
848   // I/O measurement variables
849   PerfLevel prev_perf_level = PerfLevel::kEnableTime;
850   const uint64_t kRecordStatsEvery = 1000;
851   uint64_t prev_write_nanos = 0;
852   uint64_t prev_fsync_nanos = 0;
853   uint64_t prev_range_sync_nanos = 0;
854   uint64_t prev_prepare_write_nanos = 0;
855   uint64_t prev_cpu_write_nanos = 0;
856   uint64_t prev_cpu_read_nanos = 0;
857   if (measure_io_stats_) {
858     prev_perf_level = GetPerfLevel();
859     SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
860     prev_write_nanos = IOSTATS(write_nanos);
861     prev_fsync_nanos = IOSTATS(fsync_nanos);
862     prev_range_sync_nanos = IOSTATS(range_sync_nanos);
863     prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
864     prev_cpu_write_nanos = IOSTATS(cpu_write_nanos);
865     prev_cpu_read_nanos = IOSTATS(cpu_read_nanos);
866   }
867 
868   MergeHelper merge(
869       env_, cfd->user_comparator(), cfd->ioptions()->merge_operator,
870       compaction_filter, db_options_.info_log.get(),
871       false /* internal key corruption is expected */,
872       existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
873       snapshot_checker_, compact_->compaction->level(),
874       db_options_.statistics.get());
875 
876   TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
877   TEST_SYNC_POINT_CALLBACK(
878       "CompactionJob::Run():PausingManualCompaction:1",
879       reinterpret_cast<void*>(
880           const_cast<std::atomic<bool>*>(manual_compaction_paused_)));
881 
882   Slice* start = sub_compact->start;
883   Slice* end = sub_compact->end;
884   if (start != nullptr) {
885     IterKey start_iter;
886     start_iter.SetInternalKey(*start, kMaxSequenceNumber, kValueTypeForSeek);
887     input->Seek(start_iter.GetInternalKey());
888   } else {
889     input->SeekToFirst();
890   }
891 
892   Status status;
893   sub_compact->c_iter.reset(new CompactionIterator(
894       input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
895       &existing_snapshots_, earliest_write_conflict_snapshot_,
896       snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_), false,
897       &range_del_agg, sub_compact->compaction, compaction_filter,
898       shutting_down_, preserve_deletes_seqnum_, manual_compaction_paused_,
899       db_options_.info_log));
900   auto c_iter = sub_compact->c_iter.get();
901   c_iter->SeekToFirst();
902   if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
903     // ShouldStopBefore() maintains state based on keys processed so far. The
904     // compaction loop always calls it on the "next" key, thus won't tell it the
905     // first key. So we do that here.
906     sub_compact->ShouldStopBefore(c_iter->key(),
907                                   sub_compact->current_output_file_size);
908   }
909   const auto& c_iter_stats = c_iter->iter_stats();
910 
911   while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
912     // Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
913     // returns true.
914     const Slice& key = c_iter->key();
915     const Slice& value = c_iter->value();
916 
917     // If an end key (exclusive) is specified, check if the current key is
918     // >= than it and exit if it is because the iterator is out of its range
919     if (end != nullptr &&
920         cfd->user_comparator()->Compare(c_iter->user_key(), *end) >= 0) {
921       break;
922     }
923     if (c_iter_stats.num_input_records % kRecordStatsEvery ==
924         kRecordStatsEvery - 1) {
925       RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
926       c_iter->ResetRecordCounts();
927       RecordCompactionIOStats();
928     }
929 
930     // Open output file if necessary
931     if (sub_compact->builder == nullptr) {
932       status = OpenCompactionOutputFile(sub_compact);
933       if (!status.ok()) {
934         break;
935       }
936     }
937     assert(sub_compact->builder != nullptr);
938     assert(sub_compact->current_output() != nullptr);
939     sub_compact->builder->Add(key, value);
940     sub_compact->current_output_file_size = sub_compact->builder->FileSize();
941     const ParsedInternalKey& ikey = c_iter->ikey();
942     sub_compact->current_output()->meta.UpdateBoundaries(
943         key, value, ikey.sequence, ikey.type);
944     sub_compact->num_output_records++;
945 
946     // Close output file if it is big enough. Two possibilities determine it's
947     // time to close it: (1) the current key should be this file's last key, (2)
948     // the next key should not be in this file.
949     //
950     // TODO(aekmekji): determine if file should be closed earlier than this
951     // during subcompactions (i.e. if output size, estimated by input size, is
952     // going to be 1.2MB and max_output_file_size = 1MB, prefer to have 0.6MB
953     // and 0.6MB instead of 1MB and 0.2MB)
954     bool output_file_ended = false;
955     Status input_status;
956     if (sub_compact->compaction->output_level() != 0 &&
957         sub_compact->current_output_file_size >=
958             sub_compact->compaction->max_output_file_size()) {
959       // (1) this key terminates the file. For historical reasons, the iterator
960       // status before advancing will be given to FinishCompactionOutputFile().
961       input_status = input->status();
962       output_file_ended = true;
963     }
964     TEST_SYNC_POINT_CALLBACK(
965         "CompactionJob::Run():PausingManualCompaction:2",
966         reinterpret_cast<void*>(
967             const_cast<std::atomic<bool>*>(manual_compaction_paused_)));
968     c_iter->Next();
969     if (c_iter->status().IsManualCompactionPaused()) {
970       break;
971     }
972     if (!output_file_ended && c_iter->Valid() &&
973         sub_compact->compaction->output_level() != 0 &&
974         sub_compact->ShouldStopBefore(c_iter->key(),
975                                       sub_compact->current_output_file_size) &&
976         sub_compact->builder != nullptr) {
977       // (2) this key belongs to the next file. For historical reasons, the
978       // iterator status after advancing will be given to
979       // FinishCompactionOutputFile().
980       input_status = input->status();
981       output_file_ended = true;
982     }
983     if (output_file_ended) {
984       const Slice* next_key = nullptr;
985       if (c_iter->Valid()) {
986         next_key = &c_iter->key();
987       }
988       CompactionIterationStats range_del_out_stats;
989       status =
990           FinishCompactionOutputFile(input_status, sub_compact, &range_del_agg,
991                                      &range_del_out_stats, next_key);
992       RecordDroppedKeys(range_del_out_stats,
993                         &sub_compact->compaction_job_stats);
994     }
995   }
996 
997   sub_compact->compaction_job_stats.num_input_deletion_records =
998       c_iter_stats.num_input_deletion_records;
999   sub_compact->compaction_job_stats.num_corrupt_keys =
1000       c_iter_stats.num_input_corrupt_records;
1001   sub_compact->compaction_job_stats.num_single_del_fallthru =
1002       c_iter_stats.num_single_del_fallthru;
1003   sub_compact->compaction_job_stats.num_single_del_mismatch =
1004       c_iter_stats.num_single_del_mismatch;
1005   sub_compact->compaction_job_stats.total_input_raw_key_bytes +=
1006       c_iter_stats.total_input_raw_key_bytes;
1007   sub_compact->compaction_job_stats.total_input_raw_value_bytes +=
1008       c_iter_stats.total_input_raw_value_bytes;
1009 
1010   RecordTick(stats_, FILTER_OPERATION_TOTAL_TIME,
1011              c_iter_stats.total_filter_time);
1012   RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
1013   RecordCompactionIOStats();
1014 
1015   if (status.ok() && cfd->IsDropped()) {
1016     status =
1017         Status::ColumnFamilyDropped("Column family dropped during compaction");
1018   }
1019   if ((status.ok() || status.IsColumnFamilyDropped()) &&
1020       shutting_down_->load(std::memory_order_relaxed)) {
1021     status = Status::ShutdownInProgress("Database shutdown");
1022   }
1023   if ((status.ok() || status.IsColumnFamilyDropped()) &&
1024       (manual_compaction_paused_ &&
1025        manual_compaction_paused_->load(std::memory_order_relaxed))) {
1026     status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
1027   }
1028   if (status.ok()) {
1029     status = input->status();
1030   }
1031   if (status.ok()) {
1032     status = c_iter->status();
1033   }
1034 
1035   if (status.ok() && sub_compact->builder == nullptr &&
1036       sub_compact->outputs.size() == 0 && !range_del_agg.IsEmpty()) {
1037     // handle subcompaction containing only range deletions
1038     status = OpenCompactionOutputFile(sub_compact);
1039   }
1040 
1041   // Call FinishCompactionOutputFile() even if status is not ok: it needs to
1042   // close the output file.
1043   if (sub_compact->builder != nullptr) {
1044     CompactionIterationStats range_del_out_stats;
1045     Status s = FinishCompactionOutputFile(status, sub_compact, &range_del_agg,
1046                                           &range_del_out_stats);
1047     if (status.ok()) {
1048       status = s;
1049     }
1050     RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
1051   }
1052 
1053   sub_compact->compaction_job_stats.cpu_micros =
1054       env_->NowCPUNanos() / 1000 - prev_cpu_micros;
1055 
1056   if (measure_io_stats_) {
1057     sub_compact->compaction_job_stats.file_write_nanos +=
1058         IOSTATS(write_nanos) - prev_write_nanos;
1059     sub_compact->compaction_job_stats.file_fsync_nanos +=
1060         IOSTATS(fsync_nanos) - prev_fsync_nanos;
1061     sub_compact->compaction_job_stats.file_range_sync_nanos +=
1062         IOSTATS(range_sync_nanos) - prev_range_sync_nanos;
1063     sub_compact->compaction_job_stats.file_prepare_write_nanos +=
1064         IOSTATS(prepare_write_nanos) - prev_prepare_write_nanos;
1065     sub_compact->compaction_job_stats.cpu_micros -=
1066         (IOSTATS(cpu_write_nanos) - prev_cpu_write_nanos +
1067          IOSTATS(cpu_read_nanos) - prev_cpu_read_nanos) /
1068         1000;
1069     if (prev_perf_level != PerfLevel::kEnableTimeAndCPUTimeExceptForMutex) {
1070       SetPerfLevel(prev_perf_level);
1071     }
1072   }
1073 
1074   sub_compact->c_iter.reset();
1075   input.reset();
1076   sub_compact->status = status;
1077 }
1078 
RecordDroppedKeys(const CompactionIterationStats & c_iter_stats,CompactionJobStats * compaction_job_stats)1079 void CompactionJob::RecordDroppedKeys(
1080     const CompactionIterationStats& c_iter_stats,
1081     CompactionJobStats* compaction_job_stats) {
1082   if (c_iter_stats.num_record_drop_user > 0) {
1083     RecordTick(stats_, COMPACTION_KEY_DROP_USER,
1084                c_iter_stats.num_record_drop_user);
1085   }
1086   if (c_iter_stats.num_record_drop_hidden > 0) {
1087     RecordTick(stats_, COMPACTION_KEY_DROP_NEWER_ENTRY,
1088                c_iter_stats.num_record_drop_hidden);
1089     if (compaction_job_stats) {
1090       compaction_job_stats->num_records_replaced +=
1091           c_iter_stats.num_record_drop_hidden;
1092     }
1093   }
1094   if (c_iter_stats.num_record_drop_obsolete > 0) {
1095     RecordTick(stats_, COMPACTION_KEY_DROP_OBSOLETE,
1096                c_iter_stats.num_record_drop_obsolete);
1097     if (compaction_job_stats) {
1098       compaction_job_stats->num_expired_deletion_records +=
1099           c_iter_stats.num_record_drop_obsolete;
1100     }
1101   }
1102   if (c_iter_stats.num_record_drop_range_del > 0) {
1103     RecordTick(stats_, COMPACTION_KEY_DROP_RANGE_DEL,
1104                c_iter_stats.num_record_drop_range_del);
1105   }
1106   if (c_iter_stats.num_range_del_drop_obsolete > 0) {
1107     RecordTick(stats_, COMPACTION_RANGE_DEL_DROP_OBSOLETE,
1108                c_iter_stats.num_range_del_drop_obsolete);
1109   }
1110   if (c_iter_stats.num_optimized_del_drop_obsolete > 0) {
1111     RecordTick(stats_, COMPACTION_OPTIMIZED_DEL_DROP_OBSOLETE,
1112                c_iter_stats.num_optimized_del_drop_obsolete);
1113   }
1114 }
1115 
FinishCompactionOutputFile(const Status & input_status,SubcompactionState * sub_compact,CompactionRangeDelAggregator * range_del_agg,CompactionIterationStats * range_del_out_stats,const Slice * next_table_min_key)1116 Status CompactionJob::FinishCompactionOutputFile(
1117     const Status& input_status, SubcompactionState* sub_compact,
1118     CompactionRangeDelAggregator* range_del_agg,
1119     CompactionIterationStats* range_del_out_stats,
1120     const Slice* next_table_min_key /* = nullptr */) {
1121   AutoThreadOperationStageUpdater stage_updater(
1122       ThreadStatus::STAGE_COMPACTION_SYNC_FILE);
1123   assert(sub_compact != nullptr);
1124   assert(sub_compact->outfile);
1125   assert(sub_compact->builder != nullptr);
1126   assert(sub_compact->current_output() != nullptr);
1127 
1128   uint64_t output_number = sub_compact->current_output()->meta.fd.GetNumber();
1129   assert(output_number != 0);
1130 
1131   ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
1132   const Comparator* ucmp = cfd->user_comparator();
1133 
1134   // Check for iterator errors
1135   Status s = input_status;
1136   auto meta = &sub_compact->current_output()->meta;
1137   assert(meta != nullptr);
1138   if (s.ok()) {
1139     Slice lower_bound_guard, upper_bound_guard;
1140     std::string smallest_user_key;
1141     const Slice *lower_bound, *upper_bound;
1142     bool lower_bound_from_sub_compact = false;
1143     if (sub_compact->outputs.size() == 1) {
1144       // For the first output table, include range tombstones before the min key
1145       // but after the subcompaction boundary.
1146       lower_bound = sub_compact->start;
1147       lower_bound_from_sub_compact = true;
1148     } else if (meta->smallest.size() > 0) {
1149       // For subsequent output tables, only include range tombstones from min
1150       // key onwards since the previous file was extended to contain range
1151       // tombstones falling before min key.
1152       smallest_user_key = meta->smallest.user_key().ToString(false /*hex*/);
1153       lower_bound_guard = Slice(smallest_user_key);
1154       lower_bound = &lower_bound_guard;
1155     } else {
1156       lower_bound = nullptr;
1157     }
1158     if (next_table_min_key != nullptr) {
1159       // This may be the last file in the subcompaction in some cases, so we
1160       // need to compare the end key of subcompaction with the next file start
1161       // key. When the end key is chosen by the subcompaction, we know that
1162       // it must be the biggest key in output file. Therefore, it is safe to
1163       // use the smaller key as the upper bound of the output file, to ensure
1164       // that there is no overlapping between different output files.
1165       upper_bound_guard = ExtractUserKey(*next_table_min_key);
1166       if (sub_compact->end != nullptr &&
1167           ucmp->Compare(upper_bound_guard, *sub_compact->end) >= 0) {
1168         upper_bound = sub_compact->end;
1169       } else {
1170         upper_bound = &upper_bound_guard;
1171       }
1172     } else {
1173       // This is the last file in the subcompaction, so extend until the
1174       // subcompaction ends.
1175       upper_bound = sub_compact->end;
1176     }
1177     auto earliest_snapshot = kMaxSequenceNumber;
1178     if (existing_snapshots_.size() > 0) {
1179       earliest_snapshot = existing_snapshots_[0];
1180     }
1181     bool has_overlapping_endpoints;
1182     if (upper_bound != nullptr && meta->largest.size() > 0) {
1183       has_overlapping_endpoints =
1184           ucmp->Compare(meta->largest.user_key(), *upper_bound) == 0;
1185     } else {
1186       has_overlapping_endpoints = false;
1187     }
1188 
1189     // The end key of the subcompaction must be bigger or equal to the upper
1190     // bound. If the end of subcompaction is null or the upper bound is null,
1191     // it means that this file is the last file in the compaction. So there
1192     // will be no overlapping between this file and others.
1193     assert(sub_compact->end == nullptr ||
1194            upper_bound == nullptr ||
1195            ucmp->Compare(*upper_bound , *sub_compact->end) <= 0);
1196     auto it = range_del_agg->NewIterator(lower_bound, upper_bound,
1197                                          has_overlapping_endpoints);
1198     // Position the range tombstone output iterator. There may be tombstone
1199     // fragments that are entirely out of range, so make sure that we do not
1200     // include those.
1201     if (lower_bound != nullptr) {
1202       it->Seek(*lower_bound);
1203     } else {
1204       it->SeekToFirst();
1205     }
1206     for (; it->Valid(); it->Next()) {
1207       auto tombstone = it->Tombstone();
1208       if (upper_bound != nullptr) {
1209         int cmp = ucmp->Compare(*upper_bound, tombstone.start_key_);
1210         if ((has_overlapping_endpoints && cmp < 0) ||
1211             (!has_overlapping_endpoints && cmp <= 0)) {
1212           // Tombstones starting after upper_bound only need to be included in
1213           // the next table. If the current SST ends before upper_bound, i.e.,
1214           // `has_overlapping_endpoints == false`, we can also skip over range
1215           // tombstones that start exactly at upper_bound. Such range tombstones
1216           // will be included in the next file and are not relevant to the point
1217           // keys or endpoints of the current file.
1218           break;
1219         }
1220       }
1221 
1222       if (bottommost_level_ && tombstone.seq_ <= earliest_snapshot) {
1223         // TODO(andrewkr): tombstones that span multiple output files are
1224         // counted for each compaction output file, so lots of double counting.
1225         range_del_out_stats->num_range_del_drop_obsolete++;
1226         range_del_out_stats->num_record_drop_obsolete++;
1227         continue;
1228       }
1229 
1230       auto kv = tombstone.Serialize();
1231       assert(lower_bound == nullptr ||
1232              ucmp->Compare(*lower_bound, kv.second) < 0);
1233       sub_compact->builder->Add(kv.first.Encode(), kv.second);
1234       InternalKey smallest_candidate = std::move(kv.first);
1235       if (lower_bound != nullptr &&
1236           ucmp->Compare(smallest_candidate.user_key(), *lower_bound) <= 0) {
1237         // Pretend the smallest key has the same user key as lower_bound
1238         // (the max key in the previous table or subcompaction) in order for
1239         // files to appear key-space partitioned.
1240         //
1241         // When lower_bound is chosen by a subcompaction, we know that
1242         // subcompactions over smaller keys cannot contain any keys at
1243         // lower_bound. We also know that smaller subcompactions exist, because
1244         // otherwise the subcompaction woud be unbounded on the left. As a
1245         // result, we know that no other files on the output level will contain
1246         // actual keys at lower_bound (an output file may have a largest key of
1247         // lower_bound@kMaxSequenceNumber, but this only indicates a large range
1248         // tombstone was truncated). Therefore, it is safe to use the
1249         // tombstone's sequence number, to ensure that keys at lower_bound at
1250         // lower levels are covered by truncated tombstones.
1251         //
1252         // If lower_bound was chosen by the smallest data key in the file,
1253         // choose lowest seqnum so this file's smallest internal key comes after
1254         // the previous file's largest. The fake seqnum is OK because the read
1255         // path's file-picking code only considers user key.
1256         smallest_candidate = InternalKey(
1257             *lower_bound, lower_bound_from_sub_compact ? tombstone.seq_ : 0,
1258             kTypeRangeDeletion);
1259       }
1260       InternalKey largest_candidate = tombstone.SerializeEndKey();
1261       if (upper_bound != nullptr &&
1262           ucmp->Compare(*upper_bound, largest_candidate.user_key()) <= 0) {
1263         // Pretend the largest key has the same user key as upper_bound (the
1264         // min key in the following table or subcompaction) in order for files
1265         // to appear key-space partitioned.
1266         //
1267         // Choose highest seqnum so this file's largest internal key comes
1268         // before the next file's/subcompaction's smallest. The fake seqnum is
1269         // OK because the read path's file-picking code only considers the user
1270         // key portion.
1271         //
1272         // Note Seek() also creates InternalKey with (user_key,
1273         // kMaxSequenceNumber), but with kTypeDeletion (0x7) instead of
1274         // kTypeRangeDeletion (0xF), so the range tombstone comes before the
1275         // Seek() key in InternalKey's ordering. So Seek() will look in the
1276         // next file for the user key.
1277         largest_candidate =
1278             InternalKey(*upper_bound, kMaxSequenceNumber, kTypeRangeDeletion);
1279       }
1280 #ifndef NDEBUG
1281       SequenceNumber smallest_ikey_seqnum = kMaxSequenceNumber;
1282       if (meta->smallest.size() > 0) {
1283         smallest_ikey_seqnum = GetInternalKeySeqno(meta->smallest.Encode());
1284       }
1285 #endif
1286       meta->UpdateBoundariesForRange(smallest_candidate, largest_candidate,
1287                                      tombstone.seq_,
1288                                      cfd->internal_comparator());
1289 
1290       // The smallest key in a file is used for range tombstone truncation, so
1291       // it cannot have a seqnum of 0 (unless the smallest data key in a file
1292       // has a seqnum of 0). Otherwise, the truncated tombstone may expose
1293       // deleted keys at lower levels.
1294       assert(smallest_ikey_seqnum == 0 ||
1295              ExtractInternalKeyFooter(meta->smallest.Encode()) !=
1296                  PackSequenceAndType(0, kTypeRangeDeletion));
1297     }
1298     meta->marked_for_compaction = sub_compact->builder->NeedCompact();
1299   }
1300   const uint64_t current_entries = sub_compact->builder->NumEntries();
1301   if (s.ok()) {
1302     s = sub_compact->builder->Finish();
1303   } else {
1304     sub_compact->builder->Abandon();
1305   }
1306   if (!sub_compact->builder->io_status().ok()) {
1307     io_status_ = sub_compact->builder->io_status();
1308     s = io_status_;
1309   }
1310   const uint64_t current_bytes = sub_compact->builder->FileSize();
1311   if (s.ok()) {
1312     meta->fd.file_size = current_bytes;
1313   }
1314   sub_compact->current_output()->finished = true;
1315   sub_compact->total_bytes += current_bytes;
1316 
1317   // Finish and check for file errors
1318   IOStatus io_s;
1319   if (s.ok()) {
1320     StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
1321     io_s = sub_compact->outfile->Sync(db_options_.use_fsync);
1322   }
1323   if (io_s.ok()) {
1324     io_s = sub_compact->outfile->Close();
1325   }
1326   if (io_s.ok()) {
1327     // Add the checksum information to file metadata.
1328     meta->file_checksum = sub_compact->outfile->GetFileChecksum();
1329     meta->file_checksum_func_name =
1330         sub_compact->outfile->GetFileChecksumFuncName();
1331   }
1332   if (!io_s.ok()) {
1333     io_status_ = io_s;
1334     s = io_s;
1335   }
1336   sub_compact->outfile.reset();
1337 
1338   TableProperties tp;
1339   if (s.ok()) {
1340     tp = sub_compact->builder->GetTableProperties();
1341   }
1342 
1343   if (s.ok() && current_entries == 0 && tp.num_range_deletions == 0) {
1344     // If there is nothing to output, no necessary to generate a sst file.
1345     // This happens when the output level is bottom level, at the same time
1346     // the sub_compact output nothing.
1347     std::string fname =
1348         TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
1349                       meta->fd.GetNumber(), meta->fd.GetPathId());
1350     env_->DeleteFile(fname);
1351 
1352     // Also need to remove the file from outputs, or it will be added to the
1353     // VersionEdit.
1354     assert(!sub_compact->outputs.empty());
1355     sub_compact->outputs.pop_back();
1356     meta = nullptr;
1357   }
1358 
1359   if (s.ok() && (current_entries > 0 || tp.num_range_deletions > 0)) {
1360     // Output to event logger and fire events.
1361     sub_compact->current_output()->table_properties =
1362         std::make_shared<TableProperties>(tp);
1363     ROCKS_LOG_INFO(db_options_.info_log,
1364                    "[%s] [JOB %d] Generated table #%" PRIu64 ": %" PRIu64
1365                    " keys, %" PRIu64 " bytes%s",
1366                    cfd->GetName().c_str(), job_id_, output_number,
1367                    current_entries, current_bytes,
1368                    meta->marked_for_compaction ? " (need compaction)" : "");
1369   }
1370   std::string fname;
1371   FileDescriptor output_fd;
1372   uint64_t oldest_blob_file_number = kInvalidBlobFileNumber;
1373   if (meta != nullptr) {
1374     fname =
1375         TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
1376                       meta->fd.GetNumber(), meta->fd.GetPathId());
1377     output_fd = meta->fd;
1378     oldest_blob_file_number = meta->oldest_blob_file_number;
1379   } else {
1380     fname = "(nil)";
1381   }
1382   EventHelpers::LogAndNotifyTableFileCreationFinished(
1383       event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname,
1384       job_id_, output_fd, oldest_blob_file_number, tp,
1385       TableFileCreationReason::kCompaction, s);
1386 
1387 #ifndef ROCKSDB_LITE
1388   // Report new file to SstFileManagerImpl
1389   auto sfm =
1390       static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
1391   if (sfm && meta != nullptr && meta->fd.GetPathId() == 0) {
1392     sfm->OnAddFile(fname);
1393     if (sfm->IsMaxAllowedSpaceReached()) {
1394       // TODO(ajkr): should we return OK() if max space was reached by the final
1395       // compaction output file (similarly to how flush works when full)?
1396       s = Status::SpaceLimit("Max allowed space was reached");
1397       TEST_SYNC_POINT(
1398           "CompactionJob::FinishCompactionOutputFile:"
1399           "MaxAllowedSpaceReached");
1400       InstrumentedMutexLock l(db_mutex_);
1401       db_error_handler_->SetBGError(s, BackgroundErrorReason::kCompaction);
1402     }
1403   }
1404 #endif
1405 
1406   sub_compact->builder.reset();
1407   sub_compact->current_output_file_size = 0;
1408   return s;
1409 }
1410 
InstallCompactionResults(const MutableCFOptions & mutable_cf_options)1411 Status CompactionJob::InstallCompactionResults(
1412     const MutableCFOptions& mutable_cf_options) {
1413   db_mutex_->AssertHeld();
1414 
1415   auto* compaction = compact_->compaction;
1416   // paranoia: verify that the files that we started with
1417   // still exist in the current version and in the same original level.
1418   // This ensures that a concurrent compaction did not erroneously
1419   // pick the same files to compact_.
1420   if (!versions_->VerifyCompactionFileConsistency(compaction)) {
1421     Compaction::InputLevelSummaryBuffer inputs_summary;
1422 
1423     ROCKS_LOG_ERROR(db_options_.info_log, "[%s] [JOB %d] Compaction %s aborted",
1424                     compaction->column_family_data()->GetName().c_str(),
1425                     job_id_, compaction->InputLevelSummary(&inputs_summary));
1426     return Status::Corruption("Compaction input files inconsistent");
1427   }
1428 
1429   {
1430     Compaction::InputLevelSummaryBuffer inputs_summary;
1431     ROCKS_LOG_INFO(
1432         db_options_.info_log, "[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
1433         compaction->column_family_data()->GetName().c_str(), job_id_,
1434         compaction->InputLevelSummary(&inputs_summary), compact_->total_bytes);
1435   }
1436 
1437   // Add compaction inputs
1438   compaction->AddInputDeletions(compact_->compaction->edit());
1439 
1440   for (const auto& sub_compact : compact_->sub_compact_states) {
1441     for (const auto& out : sub_compact.outputs) {
1442       compaction->edit()->AddFile(compaction->output_level(), out.meta);
1443     }
1444   }
1445   return versions_->LogAndApply(compaction->column_family_data(),
1446                                 mutable_cf_options, compaction->edit(),
1447                                 db_mutex_, db_directory_);
1448 }
1449 
RecordCompactionIOStats()1450 void CompactionJob::RecordCompactionIOStats() {
1451   RecordTick(stats_, COMPACT_READ_BYTES, IOSTATS(bytes_read));
1452   ThreadStatusUtil::IncreaseThreadOperationProperty(
1453       ThreadStatus::COMPACTION_BYTES_READ, IOSTATS(bytes_read));
1454   IOSTATS_RESET(bytes_read);
1455   RecordTick(stats_, COMPACT_WRITE_BYTES, IOSTATS(bytes_written));
1456   ThreadStatusUtil::IncreaseThreadOperationProperty(
1457       ThreadStatus::COMPACTION_BYTES_WRITTEN, IOSTATS(bytes_written));
1458   IOSTATS_RESET(bytes_written);
1459 }
1460 
OpenCompactionOutputFile(SubcompactionState * sub_compact)1461 Status CompactionJob::OpenCompactionOutputFile(
1462     SubcompactionState* sub_compact) {
1463   assert(sub_compact != nullptr);
1464   assert(sub_compact->builder == nullptr);
1465   // no need to lock because VersionSet::next_file_number_ is atomic
1466   uint64_t file_number = versions_->NewFileNumber();
1467   std::string fname =
1468       TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
1469                     file_number, sub_compact->compaction->output_path_id());
1470   // Fire events.
1471   ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
1472 #ifndef ROCKSDB_LITE
1473   EventHelpers::NotifyTableFileCreationStarted(
1474       cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname, job_id_,
1475       TableFileCreationReason::kCompaction);
1476 #endif  // !ROCKSDB_LITE
1477   // Make the output file
1478   std::unique_ptr<FSWritableFile> writable_file;
1479 #ifndef NDEBUG
1480   bool syncpoint_arg = file_options_.use_direct_writes;
1481   TEST_SYNC_POINT_CALLBACK("CompactionJob::OpenCompactionOutputFile",
1482                            &syncpoint_arg);
1483 #endif
1484   Status s = NewWritableFile(fs_, fname, &writable_file, file_options_);
1485   if (!s.ok()) {
1486     ROCKS_LOG_ERROR(
1487         db_options_.info_log,
1488         "[%s] [JOB %d] OpenCompactionOutputFiles for table #%" PRIu64
1489         " fails at NewWritableFile with status %s",
1490         sub_compact->compaction->column_family_data()->GetName().c_str(),
1491         job_id_, file_number, s.ToString().c_str());
1492     LogFlush(db_options_.info_log);
1493     EventHelpers::LogAndNotifyTableFileCreationFinished(
1494         event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(),
1495         fname, job_id_, FileDescriptor(), kInvalidBlobFileNumber,
1496         TableProperties(), TableFileCreationReason::kCompaction, s);
1497     return s;
1498   }
1499 
1500   // Try to figure out the output file's oldest ancester time.
1501   int64_t temp_current_time = 0;
1502   auto get_time_status = env_->GetCurrentTime(&temp_current_time);
1503   // Safe to proceed even if GetCurrentTime fails. So, log and proceed.
1504   if (!get_time_status.ok()) {
1505     ROCKS_LOG_WARN(db_options_.info_log,
1506                    "Failed to get current time. Status: %s",
1507                    get_time_status.ToString().c_str());
1508   }
1509   uint64_t current_time = static_cast<uint64_t>(temp_current_time);
1510   uint64_t oldest_ancester_time =
1511       sub_compact->compaction->MinInputFileOldestAncesterTime();
1512   if (oldest_ancester_time == port::kMaxUint64) {
1513     oldest_ancester_time = current_time;
1514   }
1515 
1516   // Initialize a SubcompactionState::Output and add it to sub_compact->outputs
1517   {
1518     SubcompactionState::Output out;
1519     out.meta.fd = FileDescriptor(file_number,
1520                                  sub_compact->compaction->output_path_id(), 0);
1521     out.meta.oldest_ancester_time = oldest_ancester_time;
1522     out.meta.file_creation_time = current_time;
1523     out.finished = false;
1524     sub_compact->outputs.push_back(out);
1525   }
1526 
1527   writable_file->SetIOPriority(Env::IOPriority::IO_LOW);
1528   writable_file->SetWriteLifeTimeHint(write_hint_);
1529   writable_file->SetPreallocationBlockSize(static_cast<size_t>(
1530       sub_compact->compaction->OutputFilePreallocationSize()));
1531   const auto& listeners =
1532       sub_compact->compaction->immutable_cf_options()->listeners;
1533   sub_compact->outfile.reset(
1534       new WritableFileWriter(std::move(writable_file), fname, file_options_,
1535                              env_, db_options_.statistics.get(), listeners,
1536                              db_options_.file_checksum_gen_factory.get()));
1537 
1538   // If the Column family flag is to only optimize filters for hits,
1539   // we can skip creating filters if this is the bottommost_level where
1540   // data is going to be found
1541   bool skip_filters =
1542       cfd->ioptions()->optimize_filters_for_hits && bottommost_level_;
1543 
1544   sub_compact->builder.reset(NewTableBuilder(
1545       *cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
1546       cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
1547       cfd->GetID(), cfd->GetName(), sub_compact->outfile.get(),
1548       sub_compact->compaction->output_compression(),
1549       0 /*sample_for_compression */,
1550       sub_compact->compaction->output_compression_opts(),
1551       sub_compact->compaction->output_level(), skip_filters,
1552       oldest_ancester_time, 0 /* oldest_key_time */,
1553       sub_compact->compaction->max_output_file_size(), current_time));
1554   LogFlush(db_options_.info_log);
1555   return s;
1556 }
1557 
CleanupCompaction()1558 void CompactionJob::CleanupCompaction() {
1559   for (SubcompactionState& sub_compact : compact_->sub_compact_states) {
1560     const auto& sub_status = sub_compact.status;
1561 
1562     if (sub_compact.builder != nullptr) {
1563       // May happen if we get a shutdown call in the middle of compaction
1564       sub_compact.builder->Abandon();
1565       sub_compact.builder.reset();
1566     } else {
1567       assert(!sub_status.ok() || sub_compact.outfile == nullptr);
1568     }
1569     for (const auto& out : sub_compact.outputs) {
1570       // If this file was inserted into the table cache then remove
1571       // them here because this compaction was not committed.
1572       if (!sub_status.ok()) {
1573         TableCache::Evict(table_cache_.get(), out.meta.fd.GetNumber());
1574       }
1575     }
1576   }
1577   delete compact_;
1578   compact_ = nullptr;
1579 }
1580 
1581 #ifndef ROCKSDB_LITE
1582 namespace {
CopyPrefix(const Slice & src,size_t prefix_length,std::string * dst)1583 void CopyPrefix(const Slice& src, size_t prefix_length, std::string* dst) {
1584   assert(prefix_length > 0);
1585   size_t length = src.size() > prefix_length ? prefix_length : src.size();
1586   dst->assign(src.data(), length);
1587 }
1588 }  // namespace
1589 
1590 #endif  // !ROCKSDB_LITE
1591 
UpdateCompactionStats()1592 void CompactionJob::UpdateCompactionStats() {
1593   Compaction* compaction = compact_->compaction;
1594   compaction_stats_.num_input_files_in_non_output_levels = 0;
1595   compaction_stats_.num_input_files_in_output_level = 0;
1596   for (int input_level = 0;
1597        input_level < static_cast<int>(compaction->num_input_levels());
1598        ++input_level) {
1599     if (compaction->level(input_level) != compaction->output_level()) {
1600       UpdateCompactionInputStatsHelper(
1601           &compaction_stats_.num_input_files_in_non_output_levels,
1602           &compaction_stats_.bytes_read_non_output_levels, input_level);
1603     } else {
1604       UpdateCompactionInputStatsHelper(
1605           &compaction_stats_.num_input_files_in_output_level,
1606           &compaction_stats_.bytes_read_output_level, input_level);
1607     }
1608   }
1609 
1610   uint64_t num_output_records = 0;
1611 
1612   for (const auto& sub_compact : compact_->sub_compact_states) {
1613     size_t num_output_files = sub_compact.outputs.size();
1614     if (sub_compact.builder != nullptr) {
1615       // An error occurred so ignore the last output.
1616       assert(num_output_files > 0);
1617       --num_output_files;
1618     }
1619     compaction_stats_.num_output_files += static_cast<int>(num_output_files);
1620 
1621     num_output_records += sub_compact.num_output_records;
1622 
1623     for (const auto& out : sub_compact.outputs) {
1624       compaction_stats_.bytes_written += out.meta.fd.file_size;
1625     }
1626   }
1627 
1628   if (compaction_stats_.num_input_records > num_output_records) {
1629     compaction_stats_.num_dropped_records =
1630         compaction_stats_.num_input_records - num_output_records;
1631   }
1632 }
1633 
UpdateCompactionInputStatsHelper(int * num_files,uint64_t * bytes_read,int input_level)1634 void CompactionJob::UpdateCompactionInputStatsHelper(int* num_files,
1635                                                      uint64_t* bytes_read,
1636                                                      int input_level) {
1637   const Compaction* compaction = compact_->compaction;
1638   auto num_input_files = compaction->num_input_files(input_level);
1639   *num_files += static_cast<int>(num_input_files);
1640 
1641   for (size_t i = 0; i < num_input_files; ++i) {
1642     const auto* file_meta = compaction->input(input_level, i);
1643     *bytes_read += file_meta->fd.GetFileSize();
1644     compaction_stats_.num_input_records +=
1645         static_cast<uint64_t>(file_meta->num_entries);
1646   }
1647 }
1648 
UpdateCompactionJobStats(const InternalStats::CompactionStats & stats) const1649 void CompactionJob::UpdateCompactionJobStats(
1650     const InternalStats::CompactionStats& stats) const {
1651 #ifndef ROCKSDB_LITE
1652   if (compaction_job_stats_) {
1653     compaction_job_stats_->elapsed_micros = stats.micros;
1654 
1655     // input information
1656     compaction_job_stats_->total_input_bytes =
1657         stats.bytes_read_non_output_levels + stats.bytes_read_output_level;
1658     compaction_job_stats_->num_input_records = stats.num_input_records;
1659     compaction_job_stats_->num_input_files =
1660         stats.num_input_files_in_non_output_levels +
1661         stats.num_input_files_in_output_level;
1662     compaction_job_stats_->num_input_files_at_output_level =
1663         stats.num_input_files_in_output_level;
1664 
1665     // output information
1666     compaction_job_stats_->total_output_bytes = stats.bytes_written;
1667     compaction_job_stats_->num_output_records = compact_->num_output_records;
1668     compaction_job_stats_->num_output_files = stats.num_output_files;
1669 
1670     if (compact_->NumOutputFiles() > 0U) {
1671       CopyPrefix(compact_->SmallestUserKey(),
1672                  CompactionJobStats::kMaxPrefixLength,
1673                  &compaction_job_stats_->smallest_output_key_prefix);
1674       CopyPrefix(compact_->LargestUserKey(),
1675                  CompactionJobStats::kMaxPrefixLength,
1676                  &compaction_job_stats_->largest_output_key_prefix);
1677     }
1678   }
1679 #else
1680   (void)stats;
1681 #endif  // !ROCKSDB_LITE
1682 }
1683 
LogCompaction()1684 void CompactionJob::LogCompaction() {
1685   Compaction* compaction = compact_->compaction;
1686   ColumnFamilyData* cfd = compaction->column_family_data();
1687 
1688   // Let's check if anything will get logged. Don't prepare all the info if
1689   // we're not logging
1690   if (db_options_.info_log_level <= InfoLogLevel::INFO_LEVEL) {
1691     Compaction::InputLevelSummaryBuffer inputs_summary;
1692     ROCKS_LOG_INFO(
1693         db_options_.info_log, "[%s] [JOB %d] Compacting %s, score %.2f",
1694         cfd->GetName().c_str(), job_id_,
1695         compaction->InputLevelSummary(&inputs_summary), compaction->score());
1696     char scratch[2345];
1697     compaction->Summary(scratch, sizeof(scratch));
1698     ROCKS_LOG_INFO(db_options_.info_log, "[%s] Compaction start summary: %s\n",
1699                    cfd->GetName().c_str(), scratch);
1700     // build event logger report
1701     auto stream = event_logger_->Log();
1702     stream << "job" << job_id_ << "event"
1703            << "compaction_started"
1704            << "compaction_reason"
1705            << GetCompactionReasonString(compaction->compaction_reason());
1706     for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
1707       stream << ("files_L" + ToString(compaction->level(i)));
1708       stream.StartArray();
1709       for (auto f : *compaction->inputs(i)) {
1710         stream << f->fd.GetNumber();
1711       }
1712       stream.EndArray();
1713     }
1714     stream << "score" << compaction->score() << "input_data_size"
1715            << compaction->CalculateTotalInputSize();
1716   }
1717 }
1718 
1719 }  // namespace ROCKSDB_NAMESPACE
1720