Coverage Report

Created: 2026-09-10 16:25

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 VerticalSegmentWriter;
42
class DerivedColumnGenerator;
43
// Matches block_transform.h: at most one derived column (the row-store column)
44
// for each flush, held as a {cid, generator} pair; null generator means none.
45
using DerivedColumn = std::pair<uint32_t, std::shared_ptr<const DerivedColumnGenerator>>;
46
} // namespace segment_v2
47
48
struct SegmentStatistics;
49
class BetaRowsetWriter;
50
class SegmentFileCollection;
51
class InvertedIndexFileCollection;
52
53
class FileWriterCreator {
54
public:
55
1.46k
    virtual ~FileWriterCreator() = default;
56
57
    virtual Status create(uint32_t segment_id, io::FileWriterPtr& file_writer,
58
                          FileType file_type = FileType::SEGMENT_FILE) = 0;
59
60
    virtual Status create(uint32_t segment_id, IndexFileWriterPtr* file_writer) = 0;
61
};
62
63
template <class T>
64
    requires std::is_base_of_v<RowsetWriter, T>
65
class FileWriterCreatorT : public FileWriterCreator {
66
public:
67
1.30k
    explicit FileWriterCreatorT(T* t) : _t(t) {}
_ZN5doris18FileWriterCreatorTINS_20BaseBetaRowsetWriterEEC2EPS1_
Line
Count
Source
67
1.30k
    explicit FileWriterCreatorT(T* t) : _t(t) {}
_ZN5doris18FileWriterCreatorTINS_18BetaRowsetWriterV2EEC2EPS1_
Line
Count
Source
67
1
    explicit FileWriterCreatorT(T* t) : _t(t) {}
68
69
    Status create(uint32_t segment_id, io::FileWriterPtr& file_writer,
70
2.06k
                  FileType file_type = FileType::SEGMENT_FILE) override {
71
2.06k
        return _t->create_file_writer(segment_id, file_writer, file_type);
72
2.06k
    }
_ZN5doris18FileWriterCreatorTINS_20BaseBetaRowsetWriterEE6createEjRSt10unique_ptrINS_2io10FileWriterESt14default_deleteIS5_EENS_8FileTypeE
Line
Count
Source
70
2.06k
                  FileType file_type = FileType::SEGMENT_FILE) override {
71
2.06k
        return _t->create_file_writer(segment_id, file_writer, file_type);
72
2.06k
    }
Unexecuted instantiation: _ZN5doris18FileWriterCreatorTINS_18BetaRowsetWriterV2EE6createEjRSt10unique_ptrINS_2io10FileWriterESt14default_deleteIS5_EENS_8FileTypeE
73
74
309
    Status create(uint32_t segment_id, IndexFileWriterPtr* file_writer) override {
75
309
        return _t->create_index_file_writer(segment_id, file_writer);
76
309
    }
_ZN5doris18FileWriterCreatorTINS_20BaseBetaRowsetWriterEE6createEjPSt10unique_ptrINS_10segment_v215IndexFileWriterESt14default_deleteIS5_EE
Line
Count
Source
74
309
    Status create(uint32_t segment_id, IndexFileWriterPtr* file_writer) override {
75
309
        return _t->create_index_file_writer(segment_id, file_writer);
76
309
    }
Unexecuted instantiation: _ZN5doris18FileWriterCreatorTINS_18BetaRowsetWriterV2EE6createEjPSt10unique_ptrINS_10segment_v215IndexFileWriterESt14default_deleteIS5_EE
77
78
private:
79
    T* _t = nullptr;
80
};
81
82
class SegmentCollector {
83
public:
84
1.46k
    virtual ~SegmentCollector() = default;
85
86
    virtual Status add(uint32_t segment_id, SegmentStatistics& segstat) = 0;
87
};
88
89
template <class T>
90
    requires std::is_base_of_v<RowsetWriter, T>
91
class SegmentCollectorT : public SegmentCollector {
92
public:
93
1.30k
    explicit SegmentCollectorT(T* t) : _t(t) {}
_ZN5doris17SegmentCollectorTINS_20BaseBetaRowsetWriterEEC2EPS1_
Line
Count
Source
93
1.30k
    explicit SegmentCollectorT(T* t) : _t(t) {}
_ZN5doris17SegmentCollectorTINS_18BetaRowsetWriterV2EEC2EPS1_
Line
Count
Source
93
1
    explicit SegmentCollectorT(T* t) : _t(t) {}
94
95
2.06k
    Status add(uint32_t segment_id, SegmentStatistics& segstat) override {
96
2.06k
        return _t->add_segment(segment_id, segstat);
97
2.06k
    }
_ZN5doris17SegmentCollectorTINS_20BaseBetaRowsetWriterEE3addEjRNS_17SegmentStatisticsE
Line
Count
Source
95
2.06k
    Status add(uint32_t segment_id, SegmentStatistics& segstat) override {
96
2.06k
        return _t->add_segment(segment_id, segstat);
97
2.06k
    }
Unexecuted instantiation: _ZN5doris17SegmentCollectorTINS_18BetaRowsetWriterV2EE3addEjRNS_17SegmentStatisticsE
98
99
private:
100
    T* _t = nullptr;
101
};
102
103
class SegmentFlusher {
104
public:
105
    SegmentFlusher(RowsetWriterContext& context, SegmentFileCollection& seg_files,
106
                   InvertedIndexFileCollection& idx_files);
107
108
    ~SegmentFlusher();
109
110
    // Runs the block transform chain on `block` and hands back the derived (row-store)
111
    // column for the caller to feed into its writer.
112
    Status transform_block(Block* block, int32_t segment_id,
113
                           segment_v2::DerivedColumn* derived_column);
114
115
    // Return the file size flushed to disk in "flush_size"
116
    // This method is thread-safe.
117
    Status flush_single_block(const Block* block, int32_t segment_id,
118
                              int64_t* flush_size = nullptr);
119
120
17
    int64_t num_rows_written() const { return _num_rows_written; }
121
122
    // for partial update
123
1
    int64_t num_rows_updated() const { return _num_rows_updated; }
124
1
    int64_t num_rows_deleted() const { return _num_rows_deleted; }
125
1
    int64_t num_rows_new_added() const { return _num_rows_new_added; }
126
1
    int64_t num_rows_filtered() const { return _num_rows_filtered; }
127
128
    Status close();
129
130
public:
131
    class Writer {
132
        friend class SegmentFlusher;
133
134
    public:
135
        ~Writer();
136
137
        Status add_rows(const Block* block, size_t row_offset, size_t input_row_num);
138
139
        Status flush();
140
141
        int64_t max_row_to_add(size_t row_avg_size_in_bytes);
142
143
    private:
144
        Writer(SegmentFlusher* flusher,
145
               std::unique_ptr<segment_v2::VerticalSegmentWriter>& segment_writer);
146
147
        SegmentFlusher* _flusher = nullptr;
148
        std::unique_ptr<segment_v2::VerticalSegmentWriter> _writer;
149
    };
150
151
    Status create_writer(std::unique_ptr<SegmentFlusher::Writer>& writer, uint32_t segment_id);
152
153
private:
154
    Status _create_segment_writer(std::unique_ptr<segment_v2::VerticalSegmentWriter>& writer,
155
                                  int32_t segment_id, bool no_compression = false);
156
    Status _flush_segment_writer(std::unique_ptr<segment_v2::VerticalSegmentWriter>& writer,
157
                                 int64_t* flush_size = nullptr);
158
    void _record_segment_index_file_cache_preload(
159
            uint32_t segment_id, const segment_v2::SegmentIndexFileCacheInfo& info);
160
    Status _preload_segment_indexes_to_file_cache();
161
162
private:
163
    RowsetWriterContext& _context;
164
    SegmentFileCollection& _seg_files;
165
    InvertedIndexFileCollection& _idx_files;
166
167
    // written rows by add_block/add_row
168
    std::atomic<int64_t> _num_rows_written = 0;
169
    std::atomic<int64_t> _num_rows_updated = 0;
170
    std::atomic<int64_t> _num_rows_new_added = 0;
171
    std::atomic<int64_t> _num_rows_deleted = 0;
172
    std::atomic<int64_t> _num_rows_filtered = 0;
173
    std::mutex _segment_index_file_cache_preloads_lock;
174
    std::vector<segment_v2::SegmentIndexFileCachePreloadTask> _segment_index_file_cache_preloads;
175
};
176
177
class SegmentCreator {
178
public:
179
    SegmentCreator(RowsetWriterContext& context, SegmentFileCollection& seg_files,
180
                   InvertedIndexFileCollection& idx_files);
181
182
1.39k
    ~SegmentCreator() = default;
183
184
    void set_segment_start_id(int32_t start_seg_id,
185
6
                              int32_t max_seg_num = std::numeric_limits<int32_t>::max()) {
186
6
        DORIS_CHECK_GE(start_seg_id, 0);
187
6
        DORIS_CHECK_GE(max_seg_num, 0);
188
6
        DORIS_CHECK_EQ(_next_segment_id.load(std::memory_order_relaxed), _segment_start_id);
189
6
        if (max_seg_num != std::numeric_limits<int32_t>::max()) {
190
3
            DORIS_CHECK_LE(static_cast<int64_t>(start_seg_id) + max_seg_num,
191
3
                           std::numeric_limits<int32_t>::max());
192
3
        }
193
6
        _segment_start_id = start_seg_id;
194
6
        _max_segment_num = max_seg_num;
195
6
        _next_segment_id.store(start_seg_id, std::memory_order_relaxed);
196
6
    }
197
198
    Status add_block(const Block* block);
199
200
    Status flush();
201
202
3.58k
    Result<int32_t> allocate_segment_id() {
203
3.58k
        if (_max_segment_num == std::numeric_limits<int32_t>::max()) {
204
3.45k
            return _next_segment_id.fetch_add(1, std::memory_order_relaxed);
205
3.45k
        }
206
207
132
        int32_t next_seg_id = _next_segment_id.load(std::memory_order_relaxed);
208
132
        while (true) {
209
132
            const int64_t allocated_segment_num =
210
132
                    static_cast<int64_t>(next_seg_id) - _segment_start_id;
211
132
            if (allocated_segment_num >= _max_segment_num) {
212
66
                return ResultError(Status::Error<ErrorCode::TOO_MANY_SEGMENTS>(
213
66
                        "too many segments, start_seg_id:{}, max_seg_num:{}", _segment_start_id,
214
66
                        _max_segment_num));
215
66
            }
216
66
            if (_next_segment_id.compare_exchange_weak(next_seg_id, next_seg_id + 1,
217
66
                                                       std::memory_order_relaxed)) {
218
66
                return next_seg_id;
219
66
            }
220
66
        }
221
132
    }
222
223
    // Return the next segment id to be allocated without advancing internal state.
224
193
    int32_t get_allocated_segment_id() const { return _next_segment_id.load(); }
225
226
36
    int32_t next_segment_id() const { return _next_segment_id.load(); }
227
228
17
    int64_t num_rows_written() const { return _segment_flusher.num_rows_written(); }
229
230
    // for partial update
231
1
    int64_t num_rows_updated() const { return _segment_flusher.num_rows_updated(); }
232
1
    int64_t num_rows_deleted() const { return _segment_flusher.num_rows_deleted(); }
233
1
    int64_t num_rows_new_added() const { return _segment_flusher.num_rows_new_added(); }
234
1
    int64_t num_rows_filtered() const { return _segment_flusher.num_rows_filtered(); }
235
236
    // Flush a block into a single segment, with pre-allocated segment_id.
237
    // Return the file size flushed to disk in "flush_size"
238
    // This method is thread-safe.
239
    Status flush_single_block(const Block* block, int32_t segment_id,
240
                              int64_t* flush_size = nullptr);
241
242
    // Flush a block into a single segment, without pre-allocated segment_id.
243
    // This method is thread-safe.
244
5
    Status flush_single_block(const Block* block) {
245
5
        auto segment_id = DORIS_TRY(allocate_segment_id());
246
5
        return flush_single_block(block, segment_id);
247
5
    }
248
249
    Status close();
250
251
private:
252
    std::atomic<int32_t> _next_segment_id = 0;
253
    int32_t _segment_start_id = 0;
254
    int32_t _max_segment_num = std::numeric_limits<int32_t>::max();
255
    SegmentFlusher _segment_flusher;
256
    std::unique_ptr<SegmentFlusher::Writer> _flush_writer;
257
};
258
259
} // namespace doris