Coverage Report

Created: 2026-08-27 18:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/rowset/segment_creator.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <gen_cpp/internal_service.pb.h>
21
#include <gen_cpp/olap_file.pb.h>
22
23
#include <limits>
24
#include <memory>
25
#include <mutex>
26
#include <utility>
27
#include <vector>
28
29
#include "common/status.h"
30
#include "core/block/block.h"
31
#include "io/fs/file_reader_writer_fwd.h"
32
#include "storage/index/index_file_writer.h"
33
#include "storage/rowset/rowset_writer_context.h"
34
#include "storage/segment/segment_index_file_cache_loader.h"
35
#include "storage/tablet/tablet_fwd.h"
36
37
namespace doris {
38
class Block;
39
40
namespace segment_v2 {
41
class SegmentWriter;
42
class VerticalSegmentWriter;
43
class DerivedColumnGenerator;
44
// Matches block_transform.h: at most one derived column (the row-store column)
45
// for each flush, held as a {cid, generator} pair; null generator means none.
46
using DerivedColumn = std::pair<uint32_t, std::shared_ptr<const DerivedColumnGenerator>>;
47
} // namespace segment_v2
48
49
struct SegmentStatistics;
50
class BetaRowsetWriter;
51
class SegmentFileCollection;
52
class InvertedIndexFileCollection;
53
54
class FileWriterCreator {
55
public:
56
1.45k
    virtual ~FileWriterCreator() = default;
57
58
    virtual Status create(uint32_t segment_id, io::FileWriterPtr& file_writer,
59
                          FileType file_type = FileType::SEGMENT_FILE) = 0;
60
61
    virtual Status create(uint32_t segment_id, IndexFileWriterPtr* file_writer) = 0;
62
};
63
64
template <class T>
65
    requires std::is_base_of_v<RowsetWriter, T>
66
class FileWriterCreatorT : public FileWriterCreator {
67
public:
68
1.27k
    explicit FileWriterCreatorT(T* t) : _t(t) {}
_ZN5doris18FileWriterCreatorTINS_20BaseBetaRowsetWriterEEC2EPS1_
Line
Count
Source
68
1.27k
    explicit FileWriterCreatorT(T* t) : _t(t) {}
_ZN5doris18FileWriterCreatorTINS_18BetaRowsetWriterV2EEC2EPS1_
Line
Count
Source
68
1
    explicit FileWriterCreatorT(T* t) : _t(t) {}
69
70
    Status create(uint32_t segment_id, io::FileWriterPtr& file_writer,
71
2.05k
                  FileType file_type = FileType::SEGMENT_FILE) override {
72
2.05k
        return _t->create_file_writer(segment_id, file_writer, file_type);
73
2.05k
    }
_ZN5doris18FileWriterCreatorTINS_20BaseBetaRowsetWriterEE6createEjRSt10unique_ptrINS_2io10FileWriterESt14default_deleteIS5_EENS_8FileTypeE
Line
Count
Source
71
2.05k
                  FileType file_type = FileType::SEGMENT_FILE) override {
72
2.05k
        return _t->create_file_writer(segment_id, file_writer, file_type);
73
2.05k
    }
Unexecuted instantiation: _ZN5doris18FileWriterCreatorTINS_18BetaRowsetWriterV2EE6createEjRSt10unique_ptrINS_2io10FileWriterESt14default_deleteIS5_EENS_8FileTypeE
74
75
294
    Status create(uint32_t segment_id, IndexFileWriterPtr* file_writer) override {
76
294
        return _t->create_index_file_writer(segment_id, file_writer);
77
294
    }
_ZN5doris18FileWriterCreatorTINS_20BaseBetaRowsetWriterEE6createEjPSt10unique_ptrINS_10segment_v215IndexFileWriterESt14default_deleteIS5_EE
Line
Count
Source
75
294
    Status create(uint32_t segment_id, IndexFileWriterPtr* file_writer) override {
76
294
        return _t->create_index_file_writer(segment_id, file_writer);
77
294
    }
Unexecuted instantiation: _ZN5doris18FileWriterCreatorTINS_18BetaRowsetWriterV2EE6createEjPSt10unique_ptrINS_10segment_v215IndexFileWriterESt14default_deleteIS5_EE
78
79
private:
80
    T* _t = nullptr;
81
};
82
83
class SegmentCollector {
84
public:
85
1.45k
    virtual ~SegmentCollector() = default;
86
87
    virtual Status add(uint32_t segment_id, SegmentStatistics& segstat) = 0;
88
};
89
90
template <class T>
91
    requires std::is_base_of_v<RowsetWriter, T>
92
class SegmentCollectorT : public SegmentCollector {
93
public:
94
1.27k
    explicit SegmentCollectorT(T* t) : _t(t) {}
_ZN5doris17SegmentCollectorTINS_20BaseBetaRowsetWriterEEC2EPS1_
Line
Count
Source
94
1.27k
    explicit SegmentCollectorT(T* t) : _t(t) {}
_ZN5doris17SegmentCollectorTINS_18BetaRowsetWriterV2EEC2EPS1_
Line
Count
Source
94
1
    explicit SegmentCollectorT(T* t) : _t(t) {}
95
96
2.05k
    Status add(uint32_t segment_id, SegmentStatistics& segstat) override {
97
2.05k
        return _t->add_segment(segment_id, segstat);
98
2.05k
    }
_ZN5doris17SegmentCollectorTINS_20BaseBetaRowsetWriterEE3addEjRNS_17SegmentStatisticsE
Line
Count
Source
96
2.05k
    Status add(uint32_t segment_id, SegmentStatistics& segstat) override {
97
2.05k
        return _t->add_segment(segment_id, segstat);
98
2.05k
    }
Unexecuted instantiation: _ZN5doris17SegmentCollectorTINS_18BetaRowsetWriterV2EE3addEjRNS_17SegmentStatisticsE
99
100
private:
101
    T* _t = nullptr;
102
};
103
104
class SegmentFlusher {
105
public:
106
    SegmentFlusher(RowsetWriterContext& context, SegmentFileCollection& seg_files,
107
                   InvertedIndexFileCollection& idx_files);
108
109
    ~SegmentFlusher();
110
111
    // Runs the block transform chain on `block` and hands back the derived (row-store)
112
    // column for the caller to feed into its writer.
113
    Status transform_block(Block* block, int32_t segment_id,
114
                           segment_v2::DerivedColumn* derived_column);
115
116
    // Return the file size flushed to disk in "flush_size"
117
    // This method is thread-safe.
118
    Status flush_single_block(const Block* block, int32_t segment_id,
119
                              int64_t* flush_size = nullptr);
120
121
17
    int64_t num_rows_written() const { return _num_rows_written; }
122
123
    // for partial update
124
1
    int64_t num_rows_updated() const { return _num_rows_updated; }
125
1
    int64_t num_rows_deleted() const { return _num_rows_deleted; }
126
1
    int64_t num_rows_new_added() const { return _num_rows_new_added; }
127
1
    int64_t num_rows_filtered() const { return _num_rows_filtered; }
128
129
    Status close();
130
131
public:
132
    class Writer {
133
        friend class SegmentFlusher;
134
135
    public:
136
        ~Writer();
137
138
2.56k
        Status add_rows(const Block* block, size_t row_offset, size_t input_row_num) {
139
2.56k
            RETURN_IF_ERROR(_flusher->_add_rows(_writer, block, row_offset, input_row_num));
140
2.56k
            _flusher->_num_rows_written += input_row_num;
141
2.56k
            return Status::OK();
142
2.56k
        }
143
144
        Status flush();
145
146
        int64_t max_row_to_add(size_t row_avg_size_in_bytes);
147
148
    private:
149
        Writer(SegmentFlusher* flusher, std::unique_ptr<segment_v2::SegmentWriter>& segment_writer);
150
151
        SegmentFlusher* _flusher = nullptr;
152
        std::unique_ptr<segment_v2::SegmentWriter> _writer;
153
    };
154
155
    Status create_writer(std::unique_ptr<SegmentFlusher::Writer>& writer, uint32_t segment_id);
156
157
private:
158
    Status _add_rows(std::unique_ptr<segment_v2::SegmentWriter>& segment_writer, const Block* block,
159
                     size_t row_offset, size_t row_num);
160
    Status _add_rows(std::unique_ptr<segment_v2::VerticalSegmentWriter>& segment_writer,
161
                     const Block* block, size_t row_offset, size_t row_num);
162
    Status _create_segment_writer(std::unique_ptr<segment_v2::SegmentWriter>& writer,
163
                                  int32_t segment_id, bool no_compression = false);
164
    Status _create_segment_writer(std::unique_ptr<segment_v2::VerticalSegmentWriter>& writer,
165
                                  int32_t segment_id, bool no_compression = false);
166
    Status _flush_segment_writer(std::unique_ptr<segment_v2::SegmentWriter>& writer,
167
                                 int64_t* flush_size = nullptr);
168
    Status _flush_segment_writer(std::unique_ptr<segment_v2::VerticalSegmentWriter>& writer,
169
                                 int64_t* flush_size = nullptr);
170
    void _record_segment_index_file_cache_preload(
171
            uint32_t segment_id, const segment_v2::SegmentIndexFileCacheInfo& info);
172
    Status _preload_segment_indexes_to_file_cache();
173
174
private:
175
    RowsetWriterContext& _context;
176
    SegmentFileCollection& _seg_files;
177
    InvertedIndexFileCollection& _idx_files;
178
179
    // written rows by add_block/add_row
180
    std::atomic<int64_t> _num_rows_written = 0;
181
    std::atomic<int64_t> _num_rows_updated = 0;
182
    std::atomic<int64_t> _num_rows_new_added = 0;
183
    std::atomic<int64_t> _num_rows_deleted = 0;
184
    std::atomic<int64_t> _num_rows_filtered = 0;
185
    std::mutex _segment_index_file_cache_preloads_lock;
186
    std::vector<segment_v2::SegmentIndexFileCachePreloadTask> _segment_index_file_cache_preloads;
187
};
188
189
class SegmentCreator {
190
public:
191
    SegmentCreator(RowsetWriterContext& context, SegmentFileCollection& seg_files,
192
                   InvertedIndexFileCollection& idx_files);
193
194
1.28k
    ~SegmentCreator() = default;
195
196
    void set_segment_start_id(int32_t start_seg_id,
197
6
                              int32_t max_seg_num = std::numeric_limits<int32_t>::max()) {
198
6
        DORIS_CHECK_GE(start_seg_id, 0);
199
6
        DORIS_CHECK_GE(max_seg_num, 0);
200
6
        DORIS_CHECK_EQ(_next_segment_id.load(std::memory_order_relaxed), _segment_start_id);
201
6
        if (max_seg_num != std::numeric_limits<int32_t>::max()) {
202
3
            DORIS_CHECK_LE(static_cast<int64_t>(start_seg_id) + max_seg_num,
203
3
                           std::numeric_limits<int32_t>::max());
204
3
        }
205
6
        _segment_start_id = start_seg_id;
206
6
        _max_segment_num = max_seg_num;
207
6
        _next_segment_id.store(start_seg_id, std::memory_order_relaxed);
208
6
    }
209
210
    Status add_block(const Block* block);
211
212
    Status flush();
213
214
3.41k
    Result<int32_t> allocate_segment_id() {
215
3.41k
        if (_max_segment_num == std::numeric_limits<int32_t>::max()) {
216
3.28k
            return _next_segment_id.fetch_add(1, std::memory_order_relaxed);
217
3.28k
        }
218
219
131
        int32_t next_seg_id = _next_segment_id.load(std::memory_order_relaxed);
220
132
        while (true) {
221
132
            const int64_t allocated_segment_num =
222
132
                    static_cast<int64_t>(next_seg_id) - _segment_start_id;
223
132
            if (allocated_segment_num >= _max_segment_num) {
224
66
                return ResultError(Status::Error<ErrorCode::TOO_MANY_SEGMENTS>(
225
66
                        "too many segments, start_seg_id:{}, max_seg_num:{}", _segment_start_id,
226
66
                        _max_segment_num));
227
66
            }
228
66
            if (_next_segment_id.compare_exchange_weak(next_seg_id, next_seg_id + 1,
229
66
                                                       std::memory_order_relaxed)) {
230
66
                return next_seg_id;
231
66
            }
232
66
        }
233
131
    }
234
235
    // Return the next segment id to be allocated without advancing internal state.
236
392
    int32_t get_allocated_segment_id() const { return _next_segment_id.load(); }
237
238
31
    int32_t next_segment_id() const { return _next_segment_id.load(); }
239
240
17
    int64_t num_rows_written() const { return _segment_flusher.num_rows_written(); }
241
242
    // for partial update
243
1
    int64_t num_rows_updated() const { return _segment_flusher.num_rows_updated(); }
244
1
    int64_t num_rows_deleted() const { return _segment_flusher.num_rows_deleted(); }
245
1
    int64_t num_rows_new_added() const { return _segment_flusher.num_rows_new_added(); }
246
1
    int64_t num_rows_filtered() const { return _segment_flusher.num_rows_filtered(); }
247
248
    // Flush a block into a single segment, with pre-allocated segment_id.
249
    // Return the file size flushed to disk in "flush_size"
250
    // This method is thread-safe.
251
    Status flush_single_block(const Block* block, int32_t segment_id,
252
                              int64_t* flush_size = nullptr);
253
254
    // Flush a block into a single segment, without pre-allocated segment_id.
255
    // This method is thread-safe.
256
5
    Status flush_single_block(const Block* block) {
257
5
        auto segment_id = DORIS_TRY(allocate_segment_id());
258
5
        return flush_single_block(block, segment_id);
259
5
    }
260
261
    Status close();
262
263
private:
264
    std::atomic<int32_t> _next_segment_id = 0;
265
    int32_t _segment_start_id = 0;
266
    int32_t _max_segment_num = std::numeric_limits<int32_t>::max();
267
    SegmentFlusher _segment_flusher;
268
    std::unique_ptr<SegmentFlusher::Writer> _flush_writer;
269
};
270
271
} // namespace doris