Coverage Report

Created: 2026-08-14 14:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/rowset/segment_creator.cpp
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
#include "storage/rowset/segment_creator.h"
19
20
// IWYU pragma: no_include <bthread/errno.h>
21
#include <cerrno> // IWYU pragma: keep
22
#include <chrono>
23
#include <filesystem>
24
#include <memory>
25
#include <sstream>
26
#include <thread>
27
#include <utility>
28
29
#include "common/compiler_util.h" // IWYU pragma: keep
30
#include "common/config.h"
31
#include "common/exception.h"
32
#include "common/logging.h"
33
#include "common/status.h"
34
#include "core/assert_cast.h"
35
#include "core/block/block.h"
36
#include "core/block/columns_with_type_and_name.h"
37
#include "core/column/column.h"
38
#include "core/column/column_nullable.h"
39
#include "core/column/column_string.h"
40
#include "core/column/column_variant.h"
41
#include "core/data_type/data_type.h"
42
#include "core/types.h"
43
#include "cpp/sync_point.h"
44
#include "io/fs/file_writer.h"
45
#include "storage/olap_define.h"
46
#include "storage/rowset/beta_rowset_writer.h" // SegmentStatistics
47
#include "storage/segment/row_binlog_segment_writer.h"
48
#include "storage/segment/segment_index_file_cache_loader.h"
49
#include "storage/segment/segment_writer.h"
50
#include "storage/segment/vertical_segment_writer.h"
51
#include "storage/tablet/tablet_schema.h"
52
#include "storage/transform/block_transform.h"
53
#include "storage/utils.h"
54
#include "util/debug_points.h"
55
#include "util/json/json_parser.h"
56
#include "util/pretty_printer.h"
57
#include "util/stopwatch.hpp"
58
59
namespace doris {
60
using namespace ErrorCode;
61
62
namespace {
63
64
segment_v2::TransformExecContext make_transform_exec_context(RowsetWriterContext& context,
65
2.83k
                                                             int32_t segment_id) {
66
2.83k
    return {.tablet_schema = context.tablet_schema,
67
2.83k
            .write_type = context.write_type,
68
2.83k
            .tablet = context.tablet,
69
2.83k
            .mow_context = context.mow_context,
70
2.83k
            .partial_update_info = context.partial_update_info,
71
2.83k
            .rowset_ctx = &context,
72
2.83k
            .rowset_id = context.rowset_id,
73
2.83k
            .segment_id = segment_id,
74
2.83k
            .derived_column = {},
75
2.83k
            .partial_update_stats = {}};
76
2.83k
}
77
78
} // namespace
79
80
SegmentFlusher::SegmentFlusher(RowsetWriterContext& context, SegmentFileCollection& seg_files,
81
                               InvertedIndexFileCollection& idx_files)
82
1.32k
        : _context(context), _seg_files(seg_files), _idx_files(idx_files) {}
83
84
1.32k
SegmentFlusher::~SegmentFlusher() = default;
85
86
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
87
Status SegmentFlusher::flush_single_block(const Block* block, int32_t segment_id,
88
331
                                          int64_t* flush_size) {
89
331
    if (block->rows() == 0) {
90
0
        return Status::OK();
91
0
    }
92
331
    Block flush_block(*block);
93
331
    bool no_compression = flush_block.bytes() <= config::segment_compression_threshold_kb * 1024;
94
331
    segment_v2::DerivedColumn derived_column;
95
331
    RETURN_IF_ERROR(transform_block(&flush_block, segment_id, &derived_column));
96
330
    bool use_vertical_segment_writer =
97
330
            config::enable_vertical_segment_writer && !_context.write_binlog_opt().enable;
98
330
    if (use_vertical_segment_writer) {
99
139
        std::unique_ptr<segment_v2::VerticalSegmentWriter> writer;
100
139
        RETURN_IF_ERROR(_create_segment_writer(writer, segment_id, no_compression));
101
        // the vertical writer feeds the derived column in small fixed-size batches
102
139
        writer->set_derived_column(std::move(derived_column));
103
139
        RETURN_IF_ERROR_OR_CATCH_EXCEPTION(_add_rows(writer, &flush_block, 0, flush_block.rows()));
104
139
        RETURN_IF_ERROR(_flush_segment_writer(writer, flush_size));
105
191
    } else {
106
        // the horizontal writer has no streaming feed, build it all up front
107
191
        RETURN_IF_ERROR_OR_CATCH_EXCEPTION(
108
191
                segment_v2::materialize_derived_columns(derived_column, &flush_block));
109
191
        std::unique_ptr<segment_v2::SegmentWriter> writer;
110
191
        RETURN_IF_ERROR(_create_segment_writer(writer, segment_id, no_compression));
111
191
        RETURN_IF_ERROR_OR_CATCH_EXCEPTION(_add_rows(writer, &flush_block, 0, flush_block.rows()));
112
191
        RETURN_IF_ERROR(_flush_segment_writer(writer, flush_size));
113
191
    }
114
330
    return Status::OK();
115
330
}
116
117
Status SegmentFlusher::transform_block(Block* block, int32_t segment_id,
118
2.83k
                                       segment_v2::DerivedColumn* derived_column) {
119
2.83k
    auto transform_ctx = make_transform_exec_context(_context, segment_id);
120
2.83k
    RETURN_IF_ERROR_OR_CATCH_EXCEPTION(
121
2.83k
            segment_v2::build_transform_chain(_context).apply(transform_ctx, block));
122
    // fold the fill stages' probe counters into the flusher totals; the horizontal
123
    // writer no longer sees partial-update rows
124
2.83k
    _num_rows_updated += transform_ctx.partial_update_stats.num_rows_updated;
125
2.83k
    _num_rows_deleted += transform_ctx.partial_update_stats.num_rows_deleted;
126
2.83k
    _num_rows_new_added += transform_ctx.partial_update_stats.num_rows_new_added;
127
2.83k
    _num_rows_filtered += transform_ctx.partial_update_stats.num_rows_filtered;
128
2.83k
    *derived_column = std::move(transform_ctx.derived_column);
129
2.83k
    return Status::OK();
130
2.83k
}
131
132
894
Status SegmentFlusher::close() {
133
894
    RETURN_IF_ERROR(_seg_files.close());
134
894
    RETURN_IF_ERROR(_preload_segment_indexes_to_file_cache());
135
894
    RETURN_IF_ERROR(_idx_files.finish_close());
136
894
    return Status::OK();
137
894
}
138
139
void SegmentFlusher::_record_segment_index_file_cache_preload(
140
2.30k
        uint32_t segment_id, const segment_v2::SegmentIndexFileCacheInfo& info) {
141
2.30k
    std::lock_guard lock(_segment_index_file_cache_preloads_lock);
142
2.30k
    _segment_index_file_cache_preloads.push_back({segment_id, info});
143
2.30k
}
144
145
894
Status SegmentFlusher::_preload_segment_indexes_to_file_cache() {
146
894
    std::vector<segment_v2::SegmentIndexFileCachePreloadTask> tasks;
147
894
    {
148
894
        std::lock_guard lock(_segment_index_file_cache_preloads_lock);
149
894
        tasks.swap(_segment_index_file_cache_preloads);
150
894
    }
151
894
    return segment_v2::SegmentIndexFileCacheLoader::preload_segment_indexes_to_file_cache(_context,
152
894
                                                                                          tasks);
153
894
}
154
155
Status SegmentFlusher::_add_rows(std::unique_ptr<segment_v2::SegmentWriter>& segment_writer,
156
2.69k
                                 const Block* block, size_t row_pos, size_t num_rows) {
157
2.69k
    RETURN_IF_ERROR(segment_writer->append_block(block, row_pos, num_rows));
158
2.69k
    _num_rows_written += num_rows;
159
2.69k
    return Status::OK();
160
2.69k
}
161
162
Status SegmentFlusher::_add_rows(std::unique_ptr<segment_v2::VerticalSegmentWriter>& segment_writer,
163
139
                                 const Block* block, size_t row_pos, size_t num_rows) {
164
139
    RETURN_IF_ERROR(segment_writer->batch_block(block, row_pos, num_rows));
165
139
    RETURN_IF_ERROR(segment_writer->write_batch());
166
139
    _num_rows_written += num_rows;
167
139
    return Status::OK();
168
139
}
169
170
Status SegmentFlusher::_create_segment_writer(std::unique_ptr<segment_v2::SegmentWriter>& writer,
171
2.16k
                                              int32_t segment_id, bool no_compression) {
172
2.16k
    io::FileWriterPtr segment_file_writer;
173
2.16k
    RETURN_IF_ERROR(_context.file_writer_creator->create(segment_id, segment_file_writer));
174
175
2.16k
    IndexFileWriterPtr index_file_writer;
176
2.16k
    if (_context.tablet_schema->has_inverted_index() || _context.tablet_schema->has_ann_index()) {
177
294
        RETURN_IF_ERROR(_context.file_writer_creator->create(segment_id, &index_file_writer));
178
294
    }
179
180
2.16k
    segment_v2::SegmentWriterOptions writer_options;
181
2.16k
    writer_options.enable_unique_key_merge_on_write = _context.enable_unique_key_merge_on_write;
182
2.16k
    writer_options.rowset_ctx = &_context;
183
2.16k
    writer_options.write_type = _context.write_type;
184
2.16k
    writer_options.max_rows_per_segment = _context.max_rows_per_segment;
185
2.16k
    writer_options.mow_ctx = _context.mow_context;
186
2.16k
    if (no_compression) {
187
15
        writer_options.compression_type = NO_COMPRESSION;
188
15
    }
189
190
2.16k
    if (_context.write_binlog_opt().enable) {
191
31
        writer = std::make_unique<segment_v2::RowBinlogSegmentWriter>(
192
31
                segment_file_writer.get(), segment_id, _context.tablet_schema, _context.tablet,
193
31
                _context.data_dir, writer_options,
194
31
                _context.write_binlog_opt().write_binlog_config());
195
2.13k
    } else {
196
2.13k
        writer = std::make_unique<segment_v2::SegmentWriter>(
197
2.13k
                segment_file_writer.get(), segment_id, _context.tablet_schema, _context.tablet,
198
2.13k
                _context.data_dir, writer_options, index_file_writer.get());
199
2.13k
    }
200
2.16k
    RETURN_IF_ERROR(_seg_files.add(segment_id, std::move(segment_file_writer)));
201
2.16k
    if (_context.tablet_schema->has_inverted_index() || _context.tablet_schema->has_ann_index()) {
202
294
        RETURN_IF_ERROR(_idx_files.add(segment_id, std::move(index_file_writer)));
203
294
    }
204
2.16k
    auto s = writer->init();
205
2.16k
    if (!s.ok()) {
206
0
        LOG(WARNING) << "failed to init segment writer: " << s.to_string();
207
0
        writer.reset();
208
0
        return s;
209
0
    }
210
2.16k
    return Status::OK();
211
2.16k
}
212
213
Status SegmentFlusher::_create_segment_writer(
214
        std::unique_ptr<segment_v2::VerticalSegmentWriter>& writer, int32_t segment_id,
215
139
        bool no_compression) {
216
139
    io::FileWriterPtr segment_file_writer;
217
139
    RETURN_IF_ERROR(_context.file_writer_creator->create(segment_id, segment_file_writer));
218
219
139
    IndexFileWriterPtr index_file_writer;
220
139
    if (_context.tablet_schema->has_inverted_index() || _context.tablet_schema->has_ann_index()) {
221
22
        RETURN_IF_ERROR(_context.file_writer_creator->create(segment_id, &index_file_writer));
222
22
    }
223
224
139
    segment_v2::VerticalSegmentWriterOptions writer_options;
225
139
    writer_options.enable_unique_key_merge_on_write = _context.enable_unique_key_merge_on_write;
226
139
    writer_options.rowset_ctx = &_context;
227
139
    writer_options.write_type = _context.write_type;
228
139
    writer_options.mow_ctx = _context.mow_context;
229
139
    if (no_compression) {
230
27
        writer_options.compression_type = NO_COMPRESSION;
231
27
    }
232
233
139
    writer = std::make_unique<segment_v2::VerticalSegmentWriter>(
234
139
            segment_file_writer.get(), segment_id, _context.tablet_schema, _context.tablet,
235
139
            _context.data_dir, writer_options, index_file_writer.get());
236
139
    RETURN_IF_ERROR(_seg_files.add(segment_id, std::move(segment_file_writer)));
237
139
    if (_context.tablet_schema->has_inverted_index() || _context.tablet_schema->has_ann_index()) {
238
22
        RETURN_IF_ERROR(_idx_files.add(segment_id, std::move(index_file_writer)));
239
22
    }
240
139
    auto s = writer->init();
241
139
    if (!s.ok()) {
242
0
        LOG(WARNING) << "failed to init segment writer: " << s.to_string();
243
0
        writer.reset();
244
0
        return s;
245
0
    }
246
247
139
    VLOG_DEBUG << "create new segment writer, tablet_id:" << _context.tablet_id
248
0
               << " segment id: " << segment_id << " filename: " << writer->data_dir_path()
249
0
               << " rowset_id:" << _context.rowset_id;
250
139
    return Status::OK();
251
139
}
252
253
Status SegmentFlusher::_flush_segment_writer(
254
139
        std::unique_ptr<segment_v2::VerticalSegmentWriter>& writer, int64_t* flush_size) {
255
139
    MonotonicStopWatch total_timer;
256
139
    total_timer.start();
257
258
139
    uint32_t row_num = writer->num_rows_written();
259
139
    _num_rows_updated += writer->num_rows_updated();
260
139
    _num_rows_deleted += writer->num_rows_deleted();
261
139
    _num_rows_new_added += writer->num_rows_new_added();
262
139
    _num_rows_filtered += writer->num_rows_filtered();
263
264
139
    if (row_num == 0) {
265
0
        return Status::OK();
266
0
    }
267
268
139
    MonotonicStopWatch finalize_timer;
269
139
    finalize_timer.start();
270
139
    uint64_t segment_file_size;
271
139
    uint64_t common_index_size;
272
139
    segment_v2::SegmentIndexFileCacheInfo index_file_cache_info;
273
139
    Status s = writer->finalize(&segment_file_size, &common_index_size, &index_file_cache_info);
274
139
    finalize_timer.stop();
275
276
139
    if (!s.ok()) {
277
0
        return Status::Error(s.code(), "failed to finalize segment: {}", s.to_string());
278
0
    }
279
280
139
    DBUG_EXECUTE_IF("SegmentFlusher._flush_segment_writer.after_finalize.sleep",
281
139
                    { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); });
282
283
139
    MonotonicStopWatch inverted_index_timer;
284
139
    inverted_index_timer.start();
285
139
    int64_t inverted_index_file_size = 0;
286
139
    RETURN_IF_ERROR(writer->close_inverted_index(&inverted_index_file_size));
287
139
    inverted_index_timer.stop();
288
289
139
    VLOG_DEBUG << "tablet_id:" << _context.tablet_id
290
0
               << " flushing filename: " << writer->data_dir_path()
291
0
               << " rowset_id:" << _context.rowset_id;
292
293
139
    KeyBoundsPB key_bounds;
294
139
    Slice min_key = writer->min_encoded_key();
295
139
    Slice max_key = writer->max_encoded_key();
296
139
    DCHECK_LE(min_key.compare(max_key), 0);
297
139
    key_bounds.set_min_key(min_key.to_string());
298
139
    key_bounds.set_max_key(max_key.to_string());
299
300
139
    uint32_t segment_id = writer->segment_id();
301
139
    TEST_SYNC_POINT_CALLBACK("SegmentFlusher::flush_vertical_segment_writer", &segment_id);
302
139
    SegmentStatistics segstat;
303
139
    segstat.row_num = row_num;
304
139
    segstat.data_size = segment_file_size;
305
139
    segstat.index_size = inverted_index_file_size;
306
139
    segstat.key_bounds = key_bounds;
307
308
139
    writer.reset();
309
139
    _record_segment_index_file_cache_preload(segment_id, index_file_cache_info);
310
311
139
    MonotonicStopWatch collector_timer;
312
139
    collector_timer.start();
313
139
    RETURN_IF_ERROR(_context.segment_collector->add(segment_id, segstat));
314
139
    collector_timer.stop();
315
316
139
    total_timer.stop();
317
318
139
    LOG(INFO) << "tablet_id:" << _context.tablet_id
319
139
              << ", flushing rowset_dir: " << _context.tablet_path
320
139
              << ", rowset_id:" << _context.rowset_id
321
139
              << ", data size:" << PrettyPrinter::print_bytes(segstat.data_size)
322
139
              << ", index size:" << PrettyPrinter::print_bytes(segstat.index_size)
323
139
              << ", timing breakdown: total=" << total_timer.elapsed_time_milliseconds() << "ms"
324
139
              << ", finalize=" << finalize_timer.elapsed_time_milliseconds() << "ms"
325
139
              << ", inverted_index=" << inverted_index_timer.elapsed_time_milliseconds() << "ms"
326
139
              << ", collector=" << collector_timer.elapsed_time_milliseconds() << "ms";
327
328
139
    if (flush_size) {
329
12
        *flush_size = segment_file_size;
330
12
    }
331
139
    return Status::OK();
332
139
}
333
334
Status SegmentFlusher::_flush_segment_writer(std::unique_ptr<segment_v2::SegmentWriter>& writer,
335
2.16k
                                             int64_t* flush_size) {
336
2.16k
    MonotonicStopWatch total_timer;
337
2.16k
    total_timer.start();
338
339
2.16k
    uint32_t row_num = writer->num_rows_written();
340
341
2.16k
    if (row_num == 0) {
342
0
        return Status::OK();
343
0
    }
344
345
2.16k
    MonotonicStopWatch finalize_timer;
346
2.16k
    finalize_timer.start();
347
2.16k
    uint64_t segment_file_size;
348
2.16k
    uint64_t common_index_size;
349
2.16k
    segment_v2::SegmentIndexFileCacheInfo index_file_cache_info;
350
2.16k
    Status s = writer->finalize(&segment_file_size, &common_index_size, &index_file_cache_info);
351
2.16k
    finalize_timer.stop();
352
353
2.16k
    if (!s.ok()) {
354
0
        return Status::Error(s.code(), "failed to finalize segment: {}", s.to_string());
355
0
    }
356
357
2.16k
    DBUG_EXECUTE_IF("SegmentFlusher._flush_segment_writer.after_finalize.sleep",
358
2.16k
                    { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); });
359
360
2.16k
    MonotonicStopWatch inverted_index_timer;
361
2.16k
    inverted_index_timer.start();
362
2.16k
    int64_t inverted_index_file_size = 0;
363
2.16k
    RETURN_IF_ERROR(writer->close_inverted_index(&inverted_index_file_size));
364
2.16k
    inverted_index_timer.stop();
365
366
2.16k
    VLOG_DEBUG << "tablet_id:" << _context.tablet_id
367
0
               << " flushing rowset_dir: " << _context.tablet_path
368
0
               << " rowset_id:" << _context.rowset_id;
369
370
2.16k
    KeyBoundsPB key_bounds;
371
2.16k
    Slice min_key = writer->min_encoded_key();
372
2.16k
    Slice max_key = writer->max_encoded_key();
373
2.16k
    DCHECK_LE(min_key.compare(max_key), 0);
374
2.16k
    key_bounds.set_min_key(min_key.to_string());
375
2.16k
    key_bounds.set_max_key(max_key.to_string());
376
377
2.16k
    uint32_t segment_id = writer->get_segment_id();
378
2.16k
    SegmentStatistics segstat;
379
2.16k
    segstat.row_num = row_num;
380
2.16k
    segstat.data_size = segment_file_size;
381
2.16k
    segstat.index_size = inverted_index_file_size;
382
2.16k
    segstat.key_bounds = key_bounds;
383
384
2.16k
    writer.reset();
385
2.16k
    _record_segment_index_file_cache_preload(segment_id, index_file_cache_info);
386
387
2.16k
    MonotonicStopWatch collector_timer;
388
2.16k
    collector_timer.start();
389
2.16k
    RETURN_IF_ERROR(_context.segment_collector->add(segment_id, segstat));
390
2.16k
    collector_timer.stop();
391
392
2.16k
    total_timer.stop();
393
394
2.16k
    LOG(INFO) << "tablet_id:" << _context.tablet_id
395
2.16k
              << ", flushing rowset_dir: " << _context.tablet_path
396
2.16k
              << ", rowset_id:" << _context.rowset_id
397
2.16k
              << ", data size:" << PrettyPrinter::print_bytes(segstat.data_size)
398
2.16k
              << ", index size:" << PrettyPrinter::print_bytes(segstat.index_size)
399
2.16k
              << ", timing breakdown: total=" << total_timer.elapsed_time_milliseconds() << "ms"
400
2.16k
              << ", finalize=" << finalize_timer.elapsed_time_milliseconds() << "ms"
401
2.16k
              << ", inverted_index=" << inverted_index_timer.elapsed_time_milliseconds() << "ms"
402
2.16k
              << ", collector=" << collector_timer.elapsed_time_milliseconds() << "ms";
403
404
2.16k
    if (flush_size) {
405
0
        *flush_size = segment_file_size;
406
0
    }
407
2.16k
    return Status::OK();
408
2.16k
}
409
410
Status SegmentFlusher::create_writer(std::unique_ptr<SegmentFlusher::Writer>& writer,
411
1.97k
                                     uint32_t segment_id) {
412
1.97k
    std::unique_ptr<segment_v2::SegmentWriter> segment_writer;
413
1.97k
    RETURN_IF_ERROR(_create_segment_writer(segment_writer, segment_id));
414
1.97k
    DCHECK(segment_writer != nullptr);
415
1.97k
    writer.reset(new SegmentFlusher::Writer(this, segment_writer));
416
1.97k
    return Status::OK();
417
1.97k
}
418
419
SegmentFlusher::Writer::Writer(SegmentFlusher* flusher,
420
                               std::unique_ptr<segment_v2::SegmentWriter>& segment_writer)
421
1.97k
        : _flusher(flusher), _writer(std::move(segment_writer)) {};
422
423
1.97k
SegmentFlusher::Writer::~Writer() = default;
424
425
1.97k
Status SegmentFlusher::Writer::flush() {
426
1.97k
    return _flusher->_flush_segment_writer(_writer);
427
1.97k
}
428
429
2.86k
int64_t SegmentFlusher::Writer::max_row_to_add(size_t row_avg_size_in_bytes) {
430
2.86k
    return _writer->max_row_to_add(row_avg_size_in_bytes);
431
2.86k
}
432
433
SegmentCreator::SegmentCreator(RowsetWriterContext& context, SegmentFileCollection& seg_files,
434
                               InvertedIndexFileCollection& idx_files)
435
1.16k
        : _segment_flusher(context, seg_files, idx_files) {}
436
437
2.13k
Status SegmentCreator::add_block(const Block* block) {
438
2.13k
    if (block->rows() == 0) {
439
2
        return Status::OK();
440
2
    }
441
442
2.13k
    size_t block_size_in_bytes = block->bytes();
443
2.13k
    size_t block_row_num = block->rows();
444
2.13k
    size_t row_avg_size_in_bytes = std::max((size_t)1, block_size_in_bytes / block_row_num);
445
2.13k
    size_t row_offset = 0;
446
    // This seam always feeds the horizontal writer, so the derived column is
447
    // materialized up front, like flush_single_block's horizontal branch.
448
2.13k
    Block* shared_block = const_cast<Block*>(block);
449
2.50k
    auto transform_block = [&]() -> Status {
450
2.50k
        segment_v2::DerivedColumn derived_column;
451
2.50k
        RETURN_IF_ERROR(
452
2.50k
                _segment_flusher.transform_block(shared_block, /*segment_id=*/-1, &derived_column));
453
2.50k
        RETURN_IF_ERROR_OR_CATCH_EXCEPTION(
454
2.50k
                segment_v2::materialize_derived_columns(derived_column, shared_block));
455
2.50k
        return Status::OK();
456
2.50k
    };
457
458
2.13k
    if (_flush_writer == nullptr) {
459
1.60k
        RETURN_IF_ERROR(_segment_flusher.create_writer(_flush_writer, allocate_segment_id()));
460
1.60k
    }
461
462
2.50k
    do {
463
2.50k
        auto max_row_add = _flush_writer->max_row_to_add(row_avg_size_in_bytes);
464
2.50k
        if (UNLIKELY(max_row_add < 1)) {
465
            // no space for another single row, need flush now
466
367
            RETURN_IF_ERROR(flush());
467
367
            RETURN_IF_ERROR(_segment_flusher.create_writer(_flush_writer, allocate_segment_id()));
468
367
            max_row_add = _flush_writer->max_row_to_add(row_avg_size_in_bytes);
469
367
            DCHECK(max_row_add > 0);
470
367
        }
471
2.50k
        size_t input_row_num = std::min(block_row_num - row_offset, size_t(max_row_add));
472
2.50k
        RETURN_IF_ERROR(transform_block());
473
2.50k
        RETURN_IF_ERROR(_flush_writer->add_rows(block, row_offset, input_row_num));
474
2.50k
        row_offset += input_row_num;
475
2.50k
    } while (row_offset < block_row_num);
476
477
2.13k
    return Status::OK();
478
2.13k
}
479
480
2.94k
Status SegmentCreator::flush() {
481
2.94k
    if (_flush_writer == nullptr) {
482
972
        return Status::OK();
483
972
    }
484
1.97k
    RETURN_IF_ERROR(_flush_writer->flush());
485
1.97k
    _flush_writer.reset();
486
1.97k
    return Status::OK();
487
1.97k
}
488
489
Status SegmentCreator::flush_single_block(const Block* block, int32_t segment_id,
490
23
                                          int64_t* flush_size) {
491
23
    if (block->rows() == 0) {
492
0
        return Status::OK();
493
0
    }
494
23
    RETURN_IF_ERROR(_segment_flusher.flush_single_block(block, segment_id, flush_size));
495
23
    return Status::OK();
496
23
}
497
498
735
Status SegmentCreator::close() {
499
735
    RETURN_IF_ERROR(flush());
500
735
    RETURN_IF_ERROR(_segment_flusher.close());
501
735
    return Status::OK();
502
735
}
503
504
} // namespace doris