Coverage Report

Created: 2026-08-10 05:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_tablets_channel.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_tablets_channel.h"
19
20
#include <mutex>
21
22
#include "cloud/cloud_delta_writer.h"
23
#include "cloud/cloud_meta_mgr.h"
24
#include "cloud/cloud_storage_engine.h"
25
#include "cloud/config.h"
26
#include "load/channel/tablets_channel.h"
27
#include "load/delta_writer/delta_writer.h"
28
#include "storage/tablet_info.h"
29
30
namespace doris {
31
32
CloudTabletsChannel::CloudTabletsChannel(CloudStorageEngine& engine, const TabletsChannelKey& key,
33
                                         const UniqueId& load_id, bool is_high_priority,
34
                                         RuntimeProfile* profile)
35
30.2k
        : BaseTabletsChannel(key, load_id, is_high_priority, profile), _engine(engine) {}
36
37
30.2k
CloudTabletsChannel::~CloudTabletsChannel() = default;
38
39
std::unique_ptr<BaseDeltaWriter> CloudTabletsChannel::create_delta_writer(
40
269k
        const WriteRequest& request) {
41
269k
    DCHECK(request.write_req_type == WriteRequestType::DATA);
42
269k
    DCHECK(request.table_schema_param != nullptr);
43
44
269k
    if (request.binlog_tablet_id <= 0) {
45
269k
        return std::make_unique<CloudDeltaWriter>(_engine, request, _profile, _load_id);
46
269k
    }
47
48
18.4E
    int64_t row_binlog_index_id = 0;
49
18.4E
    for (const auto* index_schema : request.table_schema_param->indexes()) {
50
0
        if (index_schema->index_id == request.index_id) {
51
0
            row_binlog_index_id = index_schema->row_binlog_id;
52
0
            break;
53
0
        }
54
0
    }
55
18.4E
    DCHECK(row_binlog_index_id > 0);
56
57
18.4E
    const auto* row_binlog_index_schema =
58
18.4E
            request.table_schema_param->row_binlog_index_schema(row_binlog_index_id);
59
18.4E
    DCHECK(row_binlog_index_schema != nullptr);
60
61
18.4E
    WriteRequest group_build_req = request;
62
18.4E
    group_build_req.write_req_type = WriteRequestType::GROUP;
63
64
18.4E
    WriteRequest sub_data_req = request;
65
18.4E
    sub_data_req.write_req_type = WriteRequestType::DATA;
66
67
18.4E
    WriteRequest sub_row_binlog_req = request;
68
18.4E
    sub_row_binlog_req.write_req_type = WriteRequestType::ROW_BINLOG;
69
18.4E
    sub_row_binlog_req.tablet_id = request.binlog_tablet_id;
70
18.4E
    sub_row_binlog_req.index_id = row_binlog_index_schema->index_id;
71
18.4E
    sub_row_binlog_req.schema_hash = row_binlog_index_schema->schema_hash;
72
73
18.4E
    return std::make_unique<CloudDeltaWriter>(_engine, group_build_req, sub_data_req,
74
18.4E
                                              sub_row_binlog_req, _profile, _load_id);
75
269k
}
76
77
Status CloudTabletsChannel::add_batch(const PTabletWriterAddBlockRequest& request,
78
32.8k
                                      PTabletWriterAddBlockResult* response) {
79
    // FIXME(plat1ko): Too many duplicate code with `TabletsChannel`
80
32.8k
    SCOPED_TIMER(_add_batch_timer);
81
32.8k
    int64_t cur_seq = 0;
82
32.8k
    if (_add_batch_number_counter != nullptr) {
83
9
        _add_batch_number_counter->update(1);
84
9
    }
85
86
32.8k
    auto status = _get_current_seq(cur_seq, request);
87
32.8k
    if (UNLIKELY(!status.ok())) {
88
0
        return status;
89
0
    }
90
91
32.8k
    if (request.packet_seq() < cur_seq) {
92
0
        LOG(INFO) << "packet has already recept before, expect_seq=" << cur_seq
93
0
                  << ", recept_seq=" << request.packet_seq();
94
0
        return Status::OK();
95
0
    }
96
97
32.8k
    if (request.is_adaptive_random_bucket()) {
98
4.30k
        std::unordered_map<int64_t, DorisVector<uint32_t>> partition_to_rowidxs;
99
4.30k
        RETURN_IF_ERROR(_build_partition_to_rowidxs_for_adaptive_random_bucket(
100
4.30k
                request, &partition_to_rowidxs));
101
4.30k
        if (!partition_to_rowidxs.empty() && !config::skip_writing_empty_rowset_metadata) {
102
0
            std::unordered_set<int64_t> partition_ids;
103
0
            partition_ids.reserve(partition_to_rowidxs.size());
104
0
            for (const auto& [partition_id, _] : partition_to_rowidxs) {
105
0
                partition_ids.insert(partition_id);
106
0
            }
107
0
            {
108
0
                std::lock_guard<std::mutex> l(_tablet_writers_lock);
109
0
                RETURN_IF_ERROR(_init_writers_by_partition_ids(partition_ids));
110
0
            }
111
0
        }
112
4.30k
        return _write_block_data_for_adaptive_random_bucket(request, cur_seq, partition_to_rowidxs,
113
4.30k
                                                            response);
114
4.30k
    }
115
116
28.5k
    std::unordered_map<int64_t, TabletAddRowsPayload> tablet_to_rows;
117
28.5k
    _build_tablet_to_rows(request, &tablet_to_rows);
118
119
28.5k
    std::unordered_set<int64_t> partition_ids;
120
28.5k
    std::vector<CloudDeltaWriter*> writers;
121
28.5k
    {
122
        // add_batch may concurrency with inc_open but not under _lock.
123
        // so need to protect it with _tablet_writers_lock.
124
28.5k
        std::lock_guard<std::mutex> l(_tablet_writers_lock);
125
111k
        for (auto& [tablet_id, _] : tablet_to_rows) {
126
111k
            auto tablet_writer_it = _tablet_writers.find(tablet_id);
127
111k
            if (tablet_writer_it == _tablet_writers.end()) {
128
0
                return Status::InternalError("unknown tablet to append data, tablet={}", tablet_id);
129
0
            }
130
111k
            partition_ids.insert(tablet_writer_it->second->partition_id());
131
111k
            writers.push_back(static_cast<CloudDeltaWriter*>(tablet_writer_it->second.get()));
132
111k
        }
133
28.5k
        if (config::skip_writing_empty_rowset_metadata && !writers.empty()) {
134
28.5k
            RETURN_IF_ERROR(CloudDeltaWriter::batch_init(writers));
135
28.5k
        } else if (!partition_ids.empty()) {
136
0
            RETURN_IF_ERROR(_init_writers_by_partition_ids(partition_ids));
137
0
        }
138
28.5k
    }
139
140
28.5k
    return _write_block_data(request, cur_seq, tablet_to_rows, response);
141
28.5k
}
142
143
4.41k
Status CloudTabletsChannel::_prepare_adaptive_random_bucket_writer(BaseDeltaWriter* writer) {
144
4.41k
    auto* cloud_writer = static_cast<CloudDeltaWriter*>(writer);
145
4.41k
    if (!cloud_writer->is_init()) {
146
2.71k
        return CloudDeltaWriter::batch_init({cloud_writer});
147
2.71k
    }
148
1.70k
    return Status::OK();
149
4.41k
}
150
151
Status CloudTabletsChannel::_init_writers_by_partition_ids(
152
0
        const std::unordered_set<int64_t>& partition_ids) {
153
0
    std::vector<CloudDeltaWriter*> writers;
154
0
    for (auto&& [tablet_id, base_writer] : _tablet_writers) {
155
0
        auto* writer = static_cast<CloudDeltaWriter*>(base_writer.get());
156
0
        if (partition_ids.contains(writer->partition_id()) && !writer->is_init()) {
157
0
            writers.push_back(writer);
158
0
        }
159
0
    }
160
0
    if (!writers.empty()) {
161
0
        RETURN_IF_ERROR(CloudDeltaWriter::batch_init(writers));
162
0
    }
163
0
    return Status::OK();
164
0
}
165
166
Status CloudTabletsChannel::close(LoadChannel* parent, const PTabletWriterAddBlockRequest& req,
167
44.2k
                                  PTabletWriterAddBlockResult* res, bool* finished) {
168
    // FIXME(plat1ko): Too many duplicate code with `TabletsChannel`
169
44.2k
    std::lock_guard l(_lock);
170
44.2k
    if (_state == kFinished) {
171
0
        return _close_status;
172
0
    }
173
174
44.2k
    auto sender_id = req.sender_id();
175
44.2k
    if (_closed_senders.Get(sender_id)) {
176
        // Double close from one sender, just return OK
177
0
        *finished = (_num_remaining_senders == 0);
178
0
        return _close_status;
179
0
    }
180
181
4.46M
    for (auto pid : req.partition_ids()) {
182
4.46M
        _partition_ids.emplace(pid);
183
4.46M
    }
184
185
44.2k
    _closed_senders.Set(sender_id, true);
186
44.2k
    _num_remaining_senders--;
187
44.2k
    *finished = (_num_remaining_senders == 0);
188
189
44.2k
    LOG(INFO) << "close tablets channel: " << _key << ", sender id: " << sender_id
190
44.2k
              << ", backend id: " << req.backend_id()
191
44.2k
              << " remaining sender: " << _num_remaining_senders;
192
193
44.2k
    if (!*finished) {
194
14.0k
        return Status::OK();
195
14.0k
    }
196
197
30.1k
    auto* tablet_errors = res->mutable_tablet_errors();
198
30.1k
    auto* tablet_vec = res->mutable_tablet_vec();
199
30.1k
    _state = kFinished;
200
201
    // All senders are closed
202
    // 1. close all delta writers. under _lock.
203
30.1k
    std::vector<CloudDeltaWriter*> writers_to_commit;
204
30.1k
    writers_to_commit.reserve(_tablet_writers.size());
205
30.1k
    bool success = true;
206
207
268k
    for (auto&& [tablet_id, base_writer] : _tablet_writers) {
208
268k
        auto* writer = static_cast<CloudDeltaWriter*>(base_writer.get());
209
        // ATTN: the strict mode means strict filtering of column type conversions during import.
210
        // Sometimes all inputs are filtered, but the partition ID is still set, and the writer is
211
        // not initialized.
212
268k
        if (_partition_ids.contains(writer->partition_id())) {
213
176k
            if (!success) { // Already failed, cancel all remain writers
214
0
                static_cast<void>(writer->cancel());
215
0
                continue;
216
0
            }
217
218
176k
            if (writer->is_init()) {
219
54.6k
                auto st = writer->close();
220
54.6k
                if (!st.ok()) {
221
0
                    LOG(WARNING) << "close tablet writer failed, tablet_id=" << tablet_id
222
0
                                 << ", txn_id=" << _txn_id << ", err=" << st;
223
0
                    PTabletError* tablet_error = tablet_errors->Add();
224
0
                    tablet_error->set_tablet_id(tablet_id);
225
0
                    tablet_error->set_msg(st.to_string());
226
0
                    success = false;
227
0
                    _close_status = std::move(st);
228
0
                    continue;
229
0
                }
230
54.6k
            }
231
232
            // to make sure tablet writer in `_broken_tablets` won't call `close_wait` method.
233
176k
            if (_is_broken_tablet(writer->tablet_id())) {
234
0
                LOG(WARNING) << "SHOULD NOT HAPPEN, tablet writer is broken but not cancelled"
235
0
                             << ", tablet_id=" << tablet_id << ", transaction_id=" << _txn_id;
236
0
                continue;
237
0
            }
238
239
176k
            writers_to_commit.push_back(writer);
240
176k
        } else {
241
91.9k
            auto st = writer->cancel();
242
91.9k
            if (!st.ok()) {
243
0
                LOG(WARNING) << "cancel tablet writer failed, tablet_id=" << tablet_id
244
0
                             << ", txn_id=" << _txn_id;
245
                // just skip this tablet(writer) and continue to close others
246
0
                continue;
247
0
            }
248
91.9k
        }
249
268k
    }
250
251
30.1k
    if (!success) {
252
0
        return _close_status;
253
0
    }
254
255
    // 2. wait delta writers
256
30.1k
    using namespace std::chrono;
257
30.1k
    auto build_start = steady_clock::now();
258
176k
    for (auto* writer : writers_to_commit) {
259
176k
        if (!writer->is_init()) {
260
121k
            continue;
261
121k
        }
262
263
54.6k
        auto st = writer->build_rowset();
264
54.6k
        if (!st.ok()) {
265
41
            LOG(WARNING) << "failed to close wait DeltaWriter. tablet_id=" << writer->tablet_id()
266
41
                         << ", err=" << st;
267
41
            PTabletError* tablet_error = tablet_errors->Add();
268
41
            tablet_error->set_tablet_id(writer->tablet_id());
269
41
            tablet_error->set_msg(st.to_string());
270
41
            _close_status = std::move(st);
271
41
            return _close_status;
272
41
        }
273
54.6k
    }
274
30.1k
    int64_t build_latency = duration_cast<milliseconds>(steady_clock::now() - build_start).count();
275
276
    // 3. commit rowsets to meta-service
277
30.1k
    auto commit_start = steady_clock::now();
278
30.1k
    std::vector<std::function<Status()>> tasks;
279
30.1k
    tasks.reserve(writers_to_commit.size());
280
175k
    for (auto* writer : writers_to_commit) {
281
175k
        tasks.emplace_back([writer] { return writer->commit_rowset(); });
282
175k
    }
283
30.1k
    _close_status = cloud::bthread_fork_join(tasks, 10);
284
30.1k
    if (!_close_status.ok()) {
285
0
        return _close_status;
286
0
    }
287
288
30.1k
    int64_t commit_latency =
289
30.1k
            duration_cast<milliseconds>(steady_clock::now() - commit_start).count();
290
291
    // 4. calculate delete bitmap for Unique Key MoW tables
292
175k
    for (auto* writer : writers_to_commit) {
293
175k
        auto st = writer->submit_calc_delete_bitmap_task();
294
175k
        if (!st.ok()) {
295
0
            LOG(WARNING) << "failed to close wait DeltaWriter. tablet_id=" << writer->tablet_id()
296
0
                         << ", err=" << st;
297
0
            _add_error_tablet(tablet_errors, writer->tablet_id(), st);
298
0
            _close_status = std::move(st);
299
0
            return _close_status;
300
0
        }
301
175k
    }
302
303
    // 5. wait for delete bitmap calculation complete if necessary
304
175k
    for (auto* writer : writers_to_commit) {
305
175k
        auto st = writer->wait_calc_delete_bitmap();
306
175k
        if (!st.ok()) {
307
0
            LOG(WARNING) << "failed to close wait DeltaWriter. tablet_id=" << writer->tablet_id()
308
0
                         << ", err=" << st;
309
0
            _add_error_tablet(tablet_errors, writer->tablet_id(), st);
310
0
            _close_status = std::move(st);
311
0
            return _close_status;
312
0
        }
313
175k
    }
314
315
    // 6. set txn related info if necessary
316
206k
    for (auto it = writers_to_commit.begin(); it != writers_to_commit.end();) {
317
175k
        auto st = (*it)->set_txn_related_info();
318
175k
        if (!st.ok()) {
319
0
            _add_error_tablet(tablet_errors, (*it)->tablet_id(), st);
320
0
            _close_status = std::move(st);
321
0
            return _close_status;
322
0
        }
323
175k
        it++;
324
175k
    }
325
326
30.1k
    tablet_vec->Reserve(static_cast<int>(writers_to_commit.size() * 2));
327
175k
    for (auto* writer : writers_to_commit) {
328
175k
        PTabletInfo* tablet_info = tablet_vec->Add();
329
175k
        tablet_info->set_tablet_id(writer->tablet_id());
330
        // unused required field.
331
175k
        tablet_info->set_schema_hash(0);
332
175k
        tablet_info->set_received_rows(writer->total_received_rows());
333
175k
        tablet_info->set_num_rows_filtered(writer->num_rows_filtered());
334
175k
        if (writer->binlog_tablet_id() > 0) {
335
0
            PTabletInfo* binlog_tablet_info = tablet_vec->Add();
336
0
            binlog_tablet_info->set_tablet_id(writer->binlog_tablet_id());
337
0
            binlog_tablet_info->set_schema_hash(0);
338
0
            binlog_tablet_info->set_received_rows(writer->total_received_rows());
339
0
            binlog_tablet_info->set_num_rows_filtered(writer->num_rows_filtered());
340
0
        }
341
        // These stats may be larger than the actual value if the txn is aborted
342
175k
        writer->update_tablet_stats();
343
175k
    }
344
30.1k
    res->set_build_rowset_latency_ms(build_latency);
345
30.1k
    res->set_commit_rowset_latency_ms(commit_latency);
346
30.1k
    return Status::OK();
347
30.1k
}
348
349
} // namespace doris