Coverage Report

Created: 2026-08-14 17:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_rowset_builder.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 "cloud/cloud_rowset_builder.h"
19
20
#include <algorithm>
21
22
#include "cloud/cloud_meta_mgr.h"
23
#include "cloud/cloud_storage_engine.h"
24
#include "cloud/cloud_tablet.h"
25
#include "cloud/cloud_tablet_mgr.h"
26
#include "storage/rowset/group_rowset_writer.h"
27
#include "storage/rowset/rowset_factory.h"
28
#include "storage/rowset/rowset_writer_context.h"
29
#include "storage/storage_policy.h"
30
#include "storage/tablet_info.h"
31
32
namespace doris {
33
using namespace ErrorCode;
34
35
CloudRowsetBuilder::CloudRowsetBuilder(CloudStorageEngine& engine, const WriteRequest& req,
36
                                       RuntimeProfile* profile)
37
266k
        : BaseRowsetBuilder(req, profile), _engine(engine) {}
38
39
CloudGroupRowsetBuilder::CloudGroupRowsetBuilder(CloudStorageEngine& engine,
40
                                                 const WriteRequest& group_build_req,
41
                                                 const WriteRequest& sub_data_req,
42
                                                 const WriteRequest& sub_row_binlog_req,
43
                                                 RuntimeProfile* profile)
44
1
        : CloudRowsetBuilder(engine, group_build_req, profile) {
45
1
    DCHECK(group_build_req.write_req_type == WriteRequestType::GROUP &&
46
1
           sub_data_req.write_req_type == WriteRequestType::DATA &&
47
1
           sub_row_binlog_req.write_req_type == WriteRequestType::ROW_BINLOG);
48
1
    _data_builder = std::make_shared<CloudRowsetBuilder>(engine, sub_data_req, profile);
49
1
    _row_binlog_builder = std::make_shared<CloudRowsetBuilder>(engine, sub_row_binlog_req, profile);
50
1
}
51
52
266k
CloudRowsetBuilder::~CloudRowsetBuilder() {
53
    // Clear file cache immediately when load fails
54
266k
    if (_is_init && _rowset != nullptr && _rowset->rowset_meta()->rowset_state() == PREPARED) {
55
0
        _rowset->clear_cache();
56
0
    }
57
266k
}
58
59
172k
Status CloudRowsetBuilder::init() {
60
172k
    _tablet = DORIS_TRY(_engine.get_tablet(_req.tablet_id));
61
62
172k
    std::shared_ptr<MowContext> mow_context;
63
172k
    if (_tablet->enable_unique_key_merge_on_write() && is_data_builder()) {
64
51.7k
        if (config::cloud_mow_sync_rowsets_when_load_txn_begin) {
65
0
            auto st = std::static_pointer_cast<CloudTablet>(_tablet)->sync_rowsets();
66
            // sync_rowsets will return INVALID_TABLET_STATE when tablet is under alter
67
0
            if (!st.ok() && !st.is<ErrorCode::INVALID_TABLET_STATE>()) {
68
0
                return st;
69
0
            }
70
0
        }
71
51.7k
        RETURN_IF_ERROR(init_mow_context(mow_context));
72
120k
    } else if (_req.write_req_type == WriteRequestType::ROW_BINLOG) {
73
        // Row binlog tablets use txn_delete_bitmap_cache for local make-visible.
74
        // The real binlog delete bitmap is derived when the base tablet calculates delete bitmap.
75
1
        _delete_bitmap = std::make_shared<DeleteBitmap>(_req.tablet_id);
76
1
    }
77
172k
    RETURN_IF_ERROR(check_tablet_version_count());
78
79
172k
    using namespace std::chrono;
80
172k
    std::static_pointer_cast<CloudTablet>(_tablet)->last_load_time_ms =
81
172k
            duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
82
83
    // build tablet schema in request level
84
172k
    RETURN_IF_ERROR(_build_current_tablet_schema(_req.index_id, _req.table_schema_param.get(),
85
172k
                                                 *_tablet->tablet_schema()));
86
87
172k
    RowsetWriterContext context;
88
172k
    context.txn_id = _req.txn_id;
89
172k
    context.txn_expiration = _req.txn_expiration;
90
172k
    context.load_id = _req.load_id;
91
172k
    context.db_id = _req.table_schema_param->db_id();
92
172k
    context.table_id = _req.table_schema_param->table_id();
93
172k
    context.rowset_state = PREPARED;
94
172k
    context.segments_overlap = OVERLAPPING;
95
172k
    context.tablet_schema = _tablet_schema;
96
172k
    context.newest_write_timestamp = UnixSeconds();
97
172k
    context.tablet_id = _req.tablet_id;
98
172k
    context.tablet_schema_hash = _req.schema_hash;
99
172k
    context.index_id = _req.index_id;
100
172k
    context.tablet = _tablet;
101
173k
    if (_req.write_req_type == WriteRequestType::ROW_BINLOG || !_attach_rowset_ids.empty()) {
102
2
        context.enable_segcompaction = false;
103
2
    }
104
172k
    context.write_type = DataWriteType::TYPE_DIRECT;
105
172k
    context.mow_context = mow_context;
106
172k
    context.write_file_cache = _req.write_file_cache;
107
172k
    context.partial_update_info = _partial_update_info;
108
172k
    context.write_binlog_opt().enable = _req.write_req_type == WriteRequestType::ROW_BINLOG;
109
172k
    context.file_cache_ttl_sec = _tablet->ttl_seconds();
110
172k
    context.storage_resource = _engine.get_storage_resource(_req.storage_vault_id);
111
172k
    if (!context.storage_resource) {
112
0
        return Status::InternalError("vault id not found, maybe not sync, vault id {}",
113
0
                                     _req.storage_vault_id);
114
0
    }
115
116
172k
    _rowset_writer = DORIS_TRY(_tablet->create_rowset_writer(context, false));
117
172k
    _rowset_id = context.rowset_id;
118
119
172k
    _calc_delete_bitmap_token = _engine.calc_delete_bitmap_executor()->create_token();
120
121
173k
    if (!_skip_writing_rowset_metadata) {
122
173k
        RETURN_IF_ERROR(_engine.meta_mgr().prepare_rowset(*_rowset_writer->rowset_meta(), "",
123
173k
                                                          _tablet->table_id()));
124
173k
    }
125
126
172k
    _is_init = true;
127
172k
    return Status::OK();
128
172k
}
129
130
1
Status CloudGroupRowsetBuilder::init() {
131
1
    RETURN_IF_ERROR(_row_binlog_builder->init());
132
1
    RETURN_IF_ERROR(
133
1
            _data_builder->attach_pending_rs_guard_to_txn(_row_binlog_builder->rowset_id()));
134
1
    RETURN_IF_ERROR(_data_builder->init());
135
1
    _tablet = _data_builder->tablet_sptr();
136
137
1
    std::unique_ptr<GroupRowsetWriter> group_writer;
138
1
    RETURN_IF_ERROR(RowsetFactory::create_empty_group_rowset_writer(&group_writer));
139
1
    group_writer->set_data_writer(_data_builder->rowset_writer());
140
1
    group_writer->set_row_binlog_writer(_row_binlog_builder->rowset_writer());
141
142
1
    {
143
1
        const auto& data_ctx = _data_builder->rowset_writer()->context();
144
1
        auto& binlog_ctx =
145
1
                const_cast<RowsetWriterContext&>(_row_binlog_builder->rowset_writer()->context());
146
1
        auto& cfg = binlog_ctx.write_binlog_opt().write_binlog_config();
147
1
        cfg.source.tablet_schema = data_ctx.tablet_schema;
148
1
        cfg.source.partial_update_info = data_ctx.partial_update_info;
149
1
        cfg.source.mow_context = data_ctx.mow_context;
150
1
        cfg.source.is_transient_rowset_writer = data_ctx.is_transient_rowset_writer;
151
1
        cfg.source.source_write_type = data_ctx.write_type;
152
1
        cfg.source.base_tablet = _data_builder->tablet_sptr();
153
1
    }
154
155
1
    _rowset_writer = std::move(group_writer);
156
1
    _is_init = true;
157
1
    return Status::OK();
158
1
}
159
160
1
Status CloudGroupRowsetBuilder::build_rowset() {
161
1
    RETURN_IF_ERROR(_row_binlog_builder->build_rowset());
162
1
    return _data_builder->build_rowset();
163
1
}
164
165
0
Status CloudGroupRowsetBuilder::submit_calc_delete_bitmap_task() {
166
0
    return _data_builder->submit_calc_delete_bitmap_task();
167
0
}
168
169
0
Status CloudGroupRowsetBuilder::wait_calc_delete_bitmap() {
170
0
    return _data_builder->wait_calc_delete_bitmap();
171
0
}
172
173
0
void CloudGroupRowsetBuilder::update_tablet_stats() {
174
0
    _data_builder->update_tablet_stats();
175
0
    _row_binlog_builder->update_tablet_stats();
176
0
}
177
178
0
Status CloudGroupRowsetBuilder::commit_rowset(const std::string& job_id, int64_t table_id) {
179
0
    return _engine.meta_mgr().commit_rowset(*_data_builder->rowset_meta(), job_id, table_id,
180
0
                                            nullptr, _row_binlog_builder->rowset_meta().get());
181
0
}
182
183
0
Status CloudGroupRowsetBuilder::set_txn_related_info() {
184
0
    RowBinlogTxnInfo attach_row_binlog;
185
0
    attach_row_binlog.rowset = _row_binlog_builder->rowset();
186
0
    attach_row_binlog.tablet = _row_binlog_builder->tablet_sptr();
187
0
    if (_data_builder->tablet()->enable_unique_key_merge_on_write()) {
188
0
        attach_row_binlog.delete_bitmap =
189
0
                std::make_shared<DeleteBitmap>(_row_binlog_builder->tablet()->tablet_id());
190
0
    }
191
0
    RETURN_IF_ERROR(_data_builder->attach_row_binlog_to_txn(attach_row_binlog));
192
0
    RETURN_IF_ERROR(_data_builder->set_txn_related_info());
193
0
    return _row_binlog_builder->set_txn_related_info();
194
0
}
195
196
1
void CloudGroupRowsetBuilder::set_skip_writing_rowset_metadata(bool skip) {
197
1
    _data_builder->set_skip_writing_rowset_metadata(skip);
198
1
    _row_binlog_builder->set_skip_writing_rowset_metadata(skip);
199
1
}
200
201
171k
Status CloudRowsetBuilder::check_tablet_version_count() {
202
171k
    int64_t version_count = cloud_tablet()->fetch_add_approximate_num_rowsets(0);
203
171k
    DBUG_EXECUTE_IF("RowsetBuilder.check_tablet_version_count.too_many_version",
204
171k
                    { version_count = INT_MAX; });
205
    // TODO(plat1ko): load backoff algorithm
206
171k
    int32_t max_version_config = cloud_tablet()->max_version_config();
207
171k
    if (version_count > max_version_config) {
208
0
        return Status::Error<TOO_MANY_VERSION>(
209
0
                "failed to init rowset builder. version count: {}, exceed limit: {}, "
210
0
                "tablet: {}. Please reduce the frequency of loading data or adjust the "
211
0
                "max_tablet_version_num or time_series_max_tablet_version_numin be.conf to a "
212
0
                "larger value.",
213
0
                version_count, max_version_config, _tablet->tablet_id());
214
0
    }
215
171k
    return Status::OK();
216
171k
}
217
218
173k
void CloudRowsetBuilder::update_tablet_stats() {
219
173k
    auto* tablet = cloud_tablet();
220
173k
    DCHECK(tablet);
221
173k
    DCHECK(_rowset);
222
173k
    tablet->fetch_add_approximate_num_rowsets(1);
223
173k
    tablet->fetch_add_approximate_num_segments(_rowset->num_segments());
224
173k
    tablet->fetch_add_approximate_num_rows(_rowset->num_rows());
225
173k
    tablet->fetch_add_approximate_data_size(_rowset->total_disk_size());
226
173k
    tablet->fetch_add_approximate_cumu_num_rowsets(1);
227
173k
    tablet->fetch_add_approximate_cumu_num_deltas(std::max<int64_t>(_rowset->num_segments(), 1));
228
173k
    tablet->write_count.fetch_add(1, std::memory_order_relaxed);
229
173k
}
230
231
518k
CloudTablet* CloudRowsetBuilder::cloud_tablet() {
232
518k
    return static_cast<CloudTablet*>(_tablet.get());
233
518k
}
234
235
294k
const RowsetMetaSharedPtr& CloudRowsetBuilder::rowset_meta() {
236
294k
    return _rowset_writer->rowset_meta();
237
294k
}
238
239
173k
Status CloudRowsetBuilder::commit_rowset(const std::string& job_id, int64_t table_id) {
240
173k
    return _engine.meta_mgr().commit_rowset(*rowset_meta(), job_id, table_id);
241
173k
}
242
243
173k
Status CloudRowsetBuilder::set_txn_related_info() {
244
173k
    if (_tablet->enable_unique_key_merge_on_write() || _tablet->is_row_binlog_tablet()) {
245
        // For empty rowsets when skip_writing_empty_rowset_metadata=true,
246
        // store only a lightweight marker instead of full rowset info.
247
        // This allows CalcDeleteBitmapTask to detect and skip gracefully,
248
        // while using minimal memory (~16 bytes per entry).
249
52.0k
        if (_skip_writing_rowset_metadata) {
250
0
            _engine.txn_delete_bitmap_cache().mark_empty_rowset(_req.txn_id, _tablet->tablet_id(),
251
0
                                                                _req.txn_expiration);
252
0
            return Status::OK();
253
0
        }
254
52.0k
        if (config::enable_merge_on_write_correctness_check &&
255
52.0k
            _tablet->enable_unique_key_merge_on_write() && _rowset->num_rows() != 0) {
256
21.0k
            auto st = _tablet->check_delete_bitmap_correctness(
257
21.0k
                    _delete_bitmap, _rowset->end_version() - 1, _req.txn_id, *_rowset_ids);
258
21.0k
            if (!st.ok()) {
259
0
                LOG(WARNING) << fmt::format(
260
0
                        "[tablet_id:{}][txn_id:{}][load_id:{}][partition_id:{}] "
261
0
                        "delete bitmap correctness check failed in commit phase!",
262
0
                        _req.tablet_id, _req.txn_id, UniqueId(_req.load_id).to_string(),
263
0
                        _req.partition_id);
264
0
                return st;
265
0
            }
266
21.0k
        }
267
52.0k
        _engine.txn_delete_bitmap_cache().set_tablet_txn_info(
268
52.0k
                _req.txn_id, _tablet->tablet_id(), _delete_bitmap, *_rowset_ids, _rowset,
269
52.0k
                _req.txn_expiration, _partial_update_info, _attach_row_binlog);
270
121k
    } else {
271
        // TSO-enabled rowsets must become visible from MS rowset meta.
272
121k
        if (config::enable_cloud_make_rs_visible_on_be && !_tablet_schema->is_tso_enabled()) {
273
121k
            if (_skip_writing_rowset_metadata) {
274
0
                _engine.committed_rs_mgr().mark_empty_rowset(_req.txn_id, _tablet->tablet_id(),
275
0
                                                             _req.txn_expiration);
276
121k
            } else {
277
121k
                _engine.meta_mgr().cache_committed_rowset(rowset_meta(), _req.txn_expiration);
278
121k
            }
279
121k
        }
280
121k
    }
281
173k
    return Status::OK();
282
173k
}
283
} // namespace doris