Coverage Report

Created: 2026-11-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/db/compaction/subcompaction_state.h
Line
Count
Source
1
//  Copyright (c) Meta Platforms, Inc. and affiliates.
2
//
3
//  This source code is licensed under both the GPLv2 (found in the
4
//  COPYING file in the root directory) and Apache 2.0 License
5
//  (found in the LICENSE.Apache file in the root directory).
6
//
7
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
8
// Use of this source code is governed by a BSD-style license that can be
9
// found in the LICENSE file. See the AUTHORS file for names of contributors.
10
11
#pragma once
12
13
#include <optional>
14
15
#include "db/blob/blob_file_addition.h"
16
#include "db/blob/blob_garbage_meter.h"
17
#include "db/compaction/compaction.h"
18
#include "db/compaction/compaction_iterator.h"
19
#include "db/compaction/compaction_outputs.h"
20
#include "db/internal_stats.h"
21
#include "db/output_validator.h"
22
#include "db/range_del_aggregator.h"
23
24
namespace ROCKSDB_NAMESPACE {
25
26
// Maintains state and outputs for each sub-compaction
27
// It contains 2 `CompactionOutputs`:
28
//  1. one for the normal output files
29
//  2. another for the proximal level outputs
30
// a `current` pointer maintains the current output group, when calling
31
// `AddToOutput()`, it checks the output of the current compaction_iterator key
32
// and point `current` to the target output group. By default, it just points to
33
// normal compaction_outputs, if the compaction_iterator key should be placed on
34
// the proximal level, `current` is changed to point to
35
// `proximal_level_outputs`.
36
// The later operations uses `Current()` to get the target group.
37
//
38
// +----------+          +-----------------------------+      +---------+
39
// | *current |--------> | compaction_outputs          |----->| output  |
40
// +----------+          +-----------------------------+      +---------+
41
//       |                                                    | output  |
42
//       |                                                    +---------+
43
//       |                                                    |  ...    |
44
//       |
45
//       |               +-----------------------------+      +---------+
46
//       +-------------> | proximal_level_outputs      |----->| output  |
47
//                       +-----------------------------+      +---------+
48
//                                                            |  ...    |
49
50
class SubcompactionState {
51
 public:
52
  const Compaction* compaction;
53
54
  // The boundaries of the key-range this compaction is interested in. No two
55
  // sub-compactions may have overlapping key-ranges.
56
  // 'start' is inclusive, 'end' is exclusive, and nullptr means unbounded
57
  const std::optional<Slice> start, end;
58
59
  // The return status of this sub-compaction
60
  Status status;
61
62
  // The return IO Status of this sub-compaction
63
  IOStatus io_status;
64
65
  // Notify on sub-compaction completion only if listener was notified on
66
  // sub-compaction begin.
67
  bool notify_on_subcompaction_completion = false;
68
69
  // compaction job stats for this sub-compaction
70
  CompactionJobStats compaction_job_stats;
71
72
  // sub-compaction job id, which is used to identify different sub-compaction
73
  // within the same compaction job.
74
  const uint32_t sub_job_id;
75
76
  Slice SmallestUserKey() const;
77
78
  Slice LargestUserKey() const;
79
80
  // Get all outputs from the subcompaction. For per_key_placement compaction,
81
  // it returns both the last level outputs and proximal level outputs.
82
  OutputIterator GetOutputs() const;
83
84
  // Get mutable access to outputs for post-processing (e.g., retrieving
85
  // file open metadata).
86
0
  std::vector<CompactionOutputs::Output>& GetMutableCompactionOutputs() {
87
0
    return compaction_outputs_.GetMutableOutputs();
88
0
  }
89
0
  std::vector<CompactionOutputs::Output>& GetMutableProximalOutputs() {
90
0
    return proximal_level_outputs_.GetMutableOutputs();
91
0
  }
92
93
  // Assign range dels aggregator. The various tombstones will potentially
94
  // be filtered to different outputs.
95
  void AssignRangeDelAggregator(
96
2.77k
      std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
97
2.77k
    assert(range_del_agg_ == nullptr);
98
2.77k
    assert(range_del_agg);
99
2.77k
    range_del_agg_ = std::move(range_del_agg);
100
2.77k
  }
101
102
2.77k
  void RemoveLastEmptyOutput() {
103
2.77k
    compaction_outputs_.RemoveLastEmptyOutput();
104
2.77k
    proximal_level_outputs_.RemoveLastEmptyOutput();
105
2.77k
  }
106
107
  // Cleanup output builders for abandoning in-progress files.
108
0
  void CleanupOutputs() {
109
0
    compaction_outputs_.Cleanup();
110
0
    if (compaction->SupportsPerKeyPlacement()) {
111
0
      proximal_level_outputs_.Cleanup();
112
0
    }
113
0
  }
114
115
  void BuildSubcompactionJobInfo(
116
0
      SubcompactionJobInfo& subcompaction_job_info) const {
117
0
    const Compaction* c = compaction;
118
0
    const ColumnFamilyData* cfd = c->column_family_data();
119
120
0
    subcompaction_job_info.cf_id = cfd->GetID();
121
0
    subcompaction_job_info.cf_name = cfd->GetName();
122
0
    subcompaction_job_info.status = status;
123
0
    subcompaction_job_info.subcompaction_job_id = static_cast<int>(sub_job_id);
124
0
    subcompaction_job_info.base_input_level = c->start_level();
125
0
    subcompaction_job_info.output_level = c->output_level();
126
0
    subcompaction_job_info.compaction_reason = c->compaction_reason();
127
0
    subcompaction_job_info.compression = c->output_compression();
128
0
    subcompaction_job_info.stats = compaction_job_stats;
129
0
    subcompaction_job_info.blob_compression_type =
130
0
        c->mutable_cf_options().blob_compression_type;
131
0
  }
132
133
  SubcompactionState() = delete;
134
  SubcompactionState(const SubcompactionState&) = delete;
135
  SubcompactionState& operator=(const SubcompactionState&) = delete;
136
137
  SubcompactionState(Compaction* c, const std::optional<Slice> _start,
138
                     const std::optional<Slice> _end, uint32_t _sub_job_id)
139
2.77k
      : compaction(c),
140
2.77k
        start(_start),
141
2.77k
        end(_end),
142
2.77k
        sub_job_id(_sub_job_id),
143
2.77k
        compaction_outputs_(c, /*is_proximal_level=*/false),
144
2.77k
        proximal_level_outputs_(c, /*is_proximal_level=*/true) {
145
2.77k
    assert(compaction != nullptr);
146
    // Set output split key (used for RoundRobin feature) only for normal
147
    // compaction_outputs, output to proximal_level feature doesn't support
148
    // RoundRobin feature (and may never going to be supported, because for
149
    // RoundRobin, the data time is mostly naturally sorted, no need to have
150
    // per-key placement with output_to_proximal_level).
151
2.77k
    compaction_outputs_.SetOutputSlitKey(start, end);
152
2.77k
  }
153
154
  SubcompactionState(SubcompactionState&& state) noexcept
155
0
      : compaction(state.compaction),
156
0
        start(state.start),
157
0
        end(state.end),
158
0
        status(std::move(state.status)),
159
0
        io_status(std::move(state.io_status)),
160
        notify_on_subcompaction_completion(
161
0
            state.notify_on_subcompaction_completion),
162
0
        compaction_job_stats(std::move(state.compaction_job_stats)),
163
0
        sub_job_id(state.sub_job_id),
164
0
        compaction_outputs_(std::move(state.compaction_outputs_)),
165
0
        proximal_level_outputs_(std::move(state.proximal_level_outputs_)),
166
0
        range_del_agg_(std::move(state.range_del_agg_)),
167
0
        blob_garbage_checkpoint_(std::move(state.blob_garbage_checkpoint_)) {
168
0
    current_outputs_ = state.current_outputs_ == &state.proximal_level_outputs_
169
0
                           ? &proximal_level_outputs_
170
0
                           : &compaction_outputs_;
171
0
  }
172
  SubcompactionState& operator=(SubcompactionState&&) = delete;
173
174
2.77k
  ~SubcompactionState() = default;
175
176
  // Add all the new files from this compaction to version_edit
177
2.07k
  void AddOutputsEdit(VersionEdit* out_edit) const {
178
2.07k
    for (const auto& file : proximal_level_outputs_.outputs_) {
179
0
      out_edit->AddFile(compaction->GetProximalLevel(), file.meta);
180
0
    }
181
2.07k
    for (const auto& file : compaction_outputs_.outputs_) {
182
1.21k
      out_edit->AddFile(compaction->output_level(), file.meta);
183
1.21k
    }
184
2.07k
  }
185
186
  void Cleanup(Cache* cache, const Status& overall_status);
187
188
  void AggregateCompactionOutputStats(
189
      InternalStats::CompactionStatsFull& internal_stats) const;
190
191
8.29k
  CompactionOutputs& Current() const {
192
8.29k
    assert(current_outputs_);
193
8.29k
    return *current_outputs_;
194
8.29k
  }
195
196
  // Returns true if either output level still has an open output file.
197
0
  bool HasUnfinishedOutputFile() const {
198
0
    return compaction_outputs_.HasUnfinishedOutput() ||
199
0
           proximal_level_outputs_.HasUnfinishedOutput();
200
0
  }
201
202
0
  CompactionOutputs* Outputs(bool is_proximal_level) {
203
0
    assert(compaction);
204
0
    if (is_proximal_level) {
205
0
      assert(compaction->SupportsPerKeyPlacement());
206
0
      return &proximal_level_outputs_;
207
0
    }
208
0
    return &compaction_outputs_;
209
0
  }
210
211
  // Per-level stats for the output
212
0
  InternalStats::CompactionStats* OutputStats(bool is_proximal_level) {
213
0
    assert(compaction);
214
0
    if (is_proximal_level) {
215
0
      assert(compaction->SupportsPerKeyPlacement());
216
0
      return &proximal_level_outputs_.stats_;
217
0
    }
218
0
    return &compaction_outputs_.stats_;
219
0
  }
220
221
2.77k
  uint64_t GetWorkerCPUMicros() const {
222
2.77k
    uint64_t rv = compaction_outputs_.GetWorkerCPUMicros();
223
2.77k
    if (compaction->SupportsPerKeyPlacement()) {
224
0
      rv += proximal_level_outputs_.GetWorkerCPUMicros();
225
0
    }
226
2.77k
    return rv;
227
2.77k
  }
228
229
5.54k
  CompactionRangeDelAggregator* RangeDelAgg() const {
230
5.54k
    return range_del_agg_.get();
231
5.54k
  }
232
233
  // if the outputs have range delete, range delete is also data
234
1.21k
  bool HasRangeDel() const {
235
1.21k
    return range_del_agg_ && !range_del_agg_->IsEmpty();
236
1.21k
  }
237
238
  void SetSubcompactionProgress(
239
0
      const SubcompactionProgress& subcompaction_progress) {
240
0
    subcompaction_progress_ = subcompaction_progress;
241
0
  }
242
243
2.77k
  SubcompactionProgress& GetSubcompactionProgressRef() {
244
2.77k
    return subcompaction_progress_;
245
2.77k
  }
246
247
  // Add compaction_iterator key/value to the `Current` output group.
248
  Status AddToOutput(CompactionIterator& iter, bool use_proximal_output,
249
                     const CompactionFileOpenFunc& open_file_func,
250
                     const CompactionFileCloseFunc& close_file_func,
251
                     const ParsedInternalKey& prev_iter_output_internal_key);
252
253
  Status UpdateBlobGarbageCheckpoint(const BlobGarbageMeter* next_key_inflow);
254
0
  const BlobGarbageMeter* GetBlobGarbageCheckpoint() const {
255
0
    return blob_garbage_checkpoint_.get();
256
0
  }
257
258
  // Close all compaction output files, both output_to_proximal_level outputs
259
  // and normal outputs.
260
  Status CloseCompactionFiles(const Status& curr_status,
261
                              const CompactionFileOpenFunc& open_file_func,
262
2.77k
                              const CompactionFileCloseFunc& close_file_func) {
263
2.77k
    auto per_key = compaction->SupportsPerKeyPlacement();
264
    // Call FinishCompactionOutputFile() even if status is not ok: it needs to
265
    // close the output file.
266
    // CloseOutput() may open new compaction output files.
267
2.77k
    Status s = curr_status;
268
2.77k
    if (per_key) {
269
0
      s = proximal_level_outputs_.CloseOutput(s, range_del_agg_.get(),
270
0
                                              open_file_func, close_file_func);
271
2.77k
    } else {
272
2.77k
      assert(proximal_level_outputs_.HasBuilder() == false);
273
      assert(proximal_level_outputs_.HasOutput() == false);
274
2.77k
    }
275
2.77k
    s = compaction_outputs_.CloseOutput(s, range_del_agg_.get(), open_file_func,
276
2.77k
                                        close_file_func);
277
2.77k
    return s;
278
2.77k
  }
279
280
 private:
281
  // State kept for output being generated
282
  CompactionOutputs compaction_outputs_;
283
  CompactionOutputs proximal_level_outputs_;
284
  CompactionOutputs* current_outputs_ = &compaction_outputs_;
285
  std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
286
  std::unique_ptr<BlobGarbageMeter> blob_garbage_checkpoint_;
287
288
  SubcompactionProgress subcompaction_progress_;
289
};
290
291
}  // namespace ROCKSDB_NAMESPACE