Coverage Report

Created: 2026-03-12 17:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/sink/writer/vtablet_writer.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 "exec/sink/writer/vtablet_writer.h"
19
20
#include <brpc/http_method.h>
21
#include <bthread/bthread.h>
22
#include <fmt/format.h>
23
#include <gen_cpp/DataSinks_types.h>
24
#include <gen_cpp/Descriptors_types.h>
25
#include <gen_cpp/Exprs_types.h>
26
#include <gen_cpp/FrontendService.h>
27
#include <gen_cpp/FrontendService_types.h>
28
#include <gen_cpp/HeartbeatService_types.h>
29
#include <gen_cpp/Metrics_types.h>
30
#include <gen_cpp/Types_types.h>
31
#include <gen_cpp/data.pb.h>
32
#include <gen_cpp/internal_service.pb.h>
33
#include <glog/logging.h>
34
#include <google/protobuf/stubs/common.h>
35
#include <sys/param.h>
36
37
#include <algorithm>
38
#include <initializer_list>
39
#include <memory>
40
#include <mutex>
41
#include <sstream>
42
#include <string>
43
#include <unordered_map>
44
#include <utility>
45
#include <vector>
46
47
#include "cloud/config.h"
48
#include "common/config.h"
49
#include "core/data_type/data_type.h"
50
#include "cpp/sync_point.h"
51
#include "exec/sink/vrow_distribution.h"
52
#include "exprs/vexpr_fwd.h"
53
#include "runtime/runtime_profile.h"
54
55
#ifdef DEBUG
56
#include <unordered_set>
57
#endif
58
59
#include "common/compiler_util.h" // IWYU pragma: keep
60
#include "common/logging.h"
61
#include "common/metrics/doris_metrics.h"
62
#include "common/object_pool.h"
63
#include "common/signal_handler.h"
64
#include "common/status.h"
65
#include "core/block/block.h"
66
#include "core/column/column.h"
67
#include "core/column/column_const.h"
68
#include "core/data_type/data_type_nullable.h"
69
#include "exec/sink/vtablet_block_convertor.h"
70
#include "exec/sink/vtablet_finder.h"
71
#include "exprs/vexpr.h"
72
#include "runtime/descriptors.h"
73
#include "runtime/exec_env.h"
74
#include "runtime/memory/memory_reclamation.h"
75
#include "runtime/query_context.h"
76
#include "runtime/runtime_state.h"
77
#include "runtime/thread_context.h"
78
#include "service/backend_options.h"
79
#include "storage/tablet_info.h"
80
#include "util/brpc_closure.h"
81
#include "util/debug_points.h"
82
#include "util/defer_op.h"
83
#include "util/mem_info.h"
84
#include "util/network_util.h"
85
#include "util/proto_util.h"
86
#include "util/threadpool.h"
87
#include "util/thrift_rpc_helper.h"
88
#include "util/thrift_util.h"
89
#include "util/time.h"
90
#include "util/uid_util.h"
91
92
namespace doris {
93
class TExpr;
94
95
#include "common/compile_check_begin.h"
96
97
bvar::Adder<int64_t> g_sink_write_bytes;
98
bvar::PerSecond<bvar::Adder<int64_t>> g_sink_write_bytes_per_second("sink_throughput_byte",
99
                                                                    &g_sink_write_bytes, 60);
100
bvar::Adder<int64_t> g_sink_write_rows;
101
bvar::PerSecond<bvar::Adder<int64_t>> g_sink_write_rows_per_second("sink_throughput_row",
102
                                                                   &g_sink_write_rows, 60);
103
bvar::Adder<int64_t> g_sink_load_back_pressure_version_time_ms(
104
        "load_back_pressure_version_time_ms");
105
106
Status IndexChannel::init(RuntimeState* state, const std::vector<TTabletWithPartition>& tablets,
107
67.4k
                          bool incremental) {
108
67.4k
    SCOPED_CONSUME_MEM_TRACKER(_index_channel_tracker.get());
109
403k
    for (const auto& tablet : tablets) {
110
        // First find the location BEs of this tablet
111
403k
        auto* tablet_locations = _parent->_location->find_tablet(tablet.tablet_id);
112
403k
        if (tablet_locations == nullptr) {
113
0
            return Status::InternalError("unknown tablet, tablet_id={}", tablet.tablet_id);
114
0
        }
115
403k
        std::vector<std::shared_ptr<VNodeChannel>> channels;
116
        // For tablet, deal with its' all replica (in some node).
117
403k
        for (auto& replica_node_id : tablet_locations->node_ids) {
118
403k
            std::shared_ptr<VNodeChannel> channel;
119
403k
            auto it = _node_channels.find(replica_node_id);
120
            // when we prepare for TableSink or incremental open tablet, we need init
121
403k
            if (it == _node_channels.end()) {
122
                // NodeChannel is not added to the _parent->_pool.
123
                // Because the deconstruction of NodeChannel may take a long time to wait rpc finish.
124
                // but the ObjectPool will hold a spin lock to delete objects.
125
68.0k
                channel =
126
68.0k
                        std::make_shared<VNodeChannel>(_parent, this, replica_node_id, incremental);
127
68.0k
                _node_channels.emplace(replica_node_id, channel);
128
                // incremental opened new node. when close we have use two-stage close.
129
68.0k
                if (incremental) {
130
0
                    _has_inc_node = true;
131
0
                }
132
68.0k
                VLOG_CRITICAL << "init new node for instance " << _parent->_sender_id
133
429
                              << ", node id:" << replica_node_id << ", incremantal:" << incremental;
134
335k
            } else {
135
335k
                channel = it->second;
136
335k
            }
137
403k
            channel->add_tablet(tablet);
138
403k
            if (_parent->_write_single_replica) {
139
0
                auto* slave_location = _parent->_slave_location->find_tablet(tablet.tablet_id);
140
0
                if (slave_location != nullptr) {
141
0
                    channel->add_slave_tablet_nodes(tablet.tablet_id, slave_location->node_ids);
142
0
                }
143
0
            }
144
403k
            channels.push_back(channel);
145
403k
            _tablets_by_channel[replica_node_id].insert(tablet.tablet_id);
146
403k
        }
147
403k
        _channels_by_tablet.emplace(tablet.tablet_id, std::move(channels));
148
403k
    }
149
68.1k
    for (auto& it : _node_channels) {
150
68.1k
        RETURN_IF_ERROR(it.second->init(state));
151
68.1k
    }
152
67.4k
    if (_where_clause != nullptr) {
153
33
        RETURN_IF_ERROR(_where_clause->prepare(state, *_parent->_output_row_desc));
154
33
        RETURN_IF_ERROR(_where_clause->open(state));
155
33
    }
156
157
67.4k
    return Status::OK();
158
67.4k
}
159
160
void IndexChannel::mark_as_failed(const VNodeChannel* node_channel, const std::string& err,
161
48
                                  int64_t tablet_id) {
162
48
    DCHECK(node_channel != nullptr);
163
48
    LOG(INFO) << "mark node_id:" << node_channel->channel_info() << " tablet_id: " << tablet_id
164
48
              << " as failed, err: " << err;
165
48
    auto node_id = node_channel->node_id();
166
48
    const auto& it = _tablets_by_channel.find(node_id);
167
48
    if (it == _tablets_by_channel.end()) {
168
0
        return;
169
0
    }
170
171
48
    {
172
48
        std::lock_guard<std::mutex> l(_fail_lock);
173
48
        if (tablet_id == -1) {
174
141
            for (const auto the_tablet_id : it->second) {
175
141
                _failed_channels[the_tablet_id].insert(node_id);
176
141
                _failed_channels_msgs.emplace(the_tablet_id,
177
141
                                              err + ", host: " + node_channel->host());
178
141
                if (_failed_channels[the_tablet_id].size() > _max_failed_replicas(the_tablet_id)) {
179
141
                    _intolerable_failure_status = Status::Error<ErrorCode::INTERNAL_ERROR, false>(
180
141
                            _failed_channels_msgs[the_tablet_id]);
181
141
                }
182
141
            }
183
48
        } else {
184
0
            _failed_channels[tablet_id].insert(node_id);
185
0
            _failed_channels_msgs.emplace(tablet_id, err + ", host: " + node_channel->host());
186
0
            if (_failed_channels[tablet_id].size() > _max_failed_replicas(tablet_id)) {
187
0
                _intolerable_failure_status = Status::Error<ErrorCode::INTERNAL_ERROR, false>(
188
0
                        _failed_channels_msgs[tablet_id]);
189
0
            }
190
0
        }
191
48
    }
192
48
}
193
194
141
int IndexChannel::_max_failed_replicas(int64_t tablet_id) {
195
141
    auto [total_replicas_num, load_required_replicas_num] =
196
141
            _parent->_tablet_replica_info[tablet_id];
197
141
    int max_failed_replicas = total_replicas_num == 0
198
141
                                      ? (_parent->_num_replicas - 1) / 2
199
141
                                      : total_replicas_num - load_required_replicas_num;
200
141
    return max_failed_replicas;
201
141
}
202
203
1.58M
int IndexChannel::_load_required_replicas_num(int64_t tablet_id) {
204
1.58M
    auto [total_replicas_num, load_required_replicas_num] =
205
1.58M
            _parent->_tablet_replica_info[tablet_id];
206
1.58M
    if (total_replicas_num == 0) {
207
0
        return (_parent->_num_replicas + 1) / 2;
208
0
    }
209
1.58M
    return load_required_replicas_num;
210
1.58M
}
211
212
176k
Status IndexChannel::check_intolerable_failure() {
213
176k
    std::lock_guard<std::mutex> l(_fail_lock);
214
176k
    return _intolerable_failure_status;
215
176k
}
216
217
68.0k
void IndexChannel::set_error_tablet_in_state(RuntimeState* state) {
218
68.0k
    std::vector<TErrorTabletInfo> error_tablet_infos;
219
220
68.0k
    {
221
68.0k
        std::lock_guard<std::mutex> l(_fail_lock);
222
68.0k
        for (const auto& it : _failed_channels_msgs) {
223
0
            TErrorTabletInfo error_info;
224
0
            error_info.__set_tabletId(it.first);
225
0
            error_info.__set_msg(it.second);
226
0
            error_tablet_infos.emplace_back(error_info);
227
0
        }
228
68.0k
    }
229
68.0k
    state->add_error_tablet_infos(error_tablet_infos);
230
68.0k
}
231
232
void IndexChannel::set_tablets_received_rows(
233
68.0k
        const std::vector<std::pair<int64_t, int64_t>>& tablets_received_rows, int64_t node_id) {
234
181k
    for (const auto& [tablet_id, rows_num] : tablets_received_rows) {
235
181k
        _tablets_received_rows[tablet_id].emplace_back(node_id, rows_num);
236
181k
    }
237
68.0k
}
238
239
void IndexChannel::set_tablets_filtered_rows(
240
68.0k
        const std::vector<std::pair<int64_t, int64_t>>& tablets_filtered_rows, int64_t node_id) {
241
181k
    for (const auto& [tablet_id, rows_num] : tablets_filtered_rows) {
242
181k
        _tablets_filtered_rows[tablet_id].emplace_back(node_id, rows_num);
243
181k
    }
244
68.0k
}
245
246
0
Status IndexChannel::check_tablet_received_rows_consistency() {
247
0
    for (auto& tablet : _tablets_received_rows) {
248
0
        for (size_t i = 0; i < tablet.second.size(); i++) {
249
0
            VLOG_NOTICE << "check_tablet_received_rows_consistency, load_id: " << _parent->_load_id
250
0
                        << ", txn_id: " << std::to_string(_parent->_txn_id)
251
0
                        << ", tablet_id: " << tablet.first
252
0
                        << ", node_id: " << tablet.second[i].first
253
0
                        << ", rows_num: " << tablet.second[i].second;
254
0
            if (i == 0) {
255
0
                continue;
256
0
            }
257
0
            if (tablet.second[i].second != tablet.second[0].second) {
258
0
                return Status::InternalError(
259
0
                        "rows num written by multi replicas doest't match, load_id={}, txn_id={}, "
260
0
                        "tablt_id={}, node_id={}, rows_num={}, node_id={}, rows_num={}",
261
0
                        print_id(_parent->_load_id), _parent->_txn_id, tablet.first,
262
0
                        tablet.second[i].first, tablet.second[i].second, tablet.second[0].first,
263
0
                        tablet.second[0].second);
264
0
            }
265
0
        }
266
0
    }
267
0
    return Status::OK();
268
0
}
269
270
59
Status IndexChannel::check_tablet_filtered_rows_consistency() {
271
95
    for (auto& tablet : _tablets_filtered_rows) {
272
190
        for (size_t i = 0; i < tablet.second.size(); i++) {
273
95
            VLOG_NOTICE << "check_tablet_filtered_rows_consistency, load_id: " << _parent->_load_id
274
0
                        << ", txn_id: " << std::to_string(_parent->_txn_id)
275
0
                        << ", tablet_id: " << tablet.first
276
0
                        << ", node_id: " << tablet.second[i].first
277
0
                        << ", rows_num: " << tablet.second[i].second;
278
95
            if (i == 0) {
279
95
                continue;
280
95
            }
281
0
            if (tablet.second[i].second != tablet.second[0].second) {
282
0
                return Status::InternalError(
283
0
                        "rows num filtered by multi replicas doest't match, load_id={}, txn_id={}, "
284
0
                        "tablt_id={}, node_id={}, rows_num={}, node_id={}, rows_num={}",
285
0
                        print_id(_parent->_load_id), _parent->_txn_id, tablet.first,
286
0
                        tablet.second[i].first, tablet.second[i].second, tablet.second[0].first,
287
0
                        tablet.second[0].second);
288
0
            }
289
0
        }
290
95
    }
291
59
    return Status::OK();
292
59
}
293
294
static Status cancel_channel_and_check_intolerable_failure(Status status,
295
                                                           const std::string& err_msg,
296
48
                                                           IndexChannel& ich, VNodeChannel& nch) {
297
48
    LOG(WARNING) << nch.channel_info() << ", close channel failed, err: " << err_msg;
298
48
    ich.mark_as_failed(&nch, err_msg, -1);
299
    // cancel the node channel in best effort
300
48
    nch.cancel(err_msg);
301
302
    // check if index has intolerable failure
303
48
    if (Status index_st = ich.check_intolerable_failure(); !index_st.ok()) {
304
48
        status = std::move(index_st);
305
48
    } else if (Status receive_st = ich.check_tablet_received_rows_consistency(); !receive_st.ok()) {
306
0
        status = std::move(receive_st);
307
0
    } else if (Status filter_st = ich.check_tablet_filtered_rows_consistency(); !filter_st.ok()) {
308
0
        status = std::move(filter_st);
309
0
    }
310
48
    return status;
311
48
}
312
313
Status IndexChannel::close_wait(
314
        RuntimeState* state, WriterStats* writer_stats,
315
        std::unordered_map<int64_t, AddBatchCounter>* node_add_batch_counter_map,
316
        std::unordered_set<int64_t> unfinished_node_channel_ids,
317
68.1k
        bool need_wait_after_quorum_success) {
318
68.1k
    DBUG_EXECUTE_IF("IndexChannel.close_wait.timeout",
319
68.1k
                    { return Status::TimedOut("injected timeout"); });
320
68.1k
    Status status = Status::OK();
321
    // 1. wait quorum success
322
68.1k
    std::unordered_set<int64_t> need_finish_tablets;
323
68.1k
    auto partition_ids = _parent->_tablet_finder->partition_ids();
324
81.5k
    for (const auto& part : _parent->_vpartition->get_partitions()) {
325
81.5k
        if (partition_ids.contains(part->id)) {
326
49.5k
            for (const auto& index : part->indexes) {
327
262k
                for (const auto& tablet_id : index.tablets) {
328
262k
                    need_finish_tablets.insert(tablet_id);
329
262k
                }
330
49.5k
            }
331
32.9k
        }
332
81.5k
    }
333
1.51M
    while (true) {
334
1.51M
        RETURN_IF_ERROR(check_each_node_channel_close(
335
1.51M
                &unfinished_node_channel_ids, node_add_batch_counter_map, writer_stats, status));
336
1.51M
        bool quorum_success = _quorum_success(unfinished_node_channel_ids, need_finish_tablets);
337
1.51M
        if (unfinished_node_channel_ids.empty() || quorum_success) {
338
68.0k
            LOG(INFO) << "quorum_success: " << quorum_success
339
68.0k
                      << ", is all finished: " << unfinished_node_channel_ids.empty()
340
68.0k
                      << ", txn_id: " << _parent->_txn_id
341
68.0k
                      << ", load_id: " << print_id(_parent->_load_id);
342
68.0k
            break;
343
68.0k
        }
344
1.44M
        bthread_usleep(1000 * 10);
345
1.44M
    }
346
347
    // 2. wait for all node channel to complete as much as possible
348
68.1k
    if (!unfinished_node_channel_ids.empty() && need_wait_after_quorum_success) {
349
0
        int64_t arrival_quorum_success_time = UnixMillis();
350
0
        int64_t max_wait_time_ms = _calc_max_wait_time_ms(unfinished_node_channel_ids);
351
0
        while (true) {
352
0
            RETURN_IF_ERROR(check_each_node_channel_close(&unfinished_node_channel_ids,
353
0
                                                          node_add_batch_counter_map, writer_stats,
354
0
                                                          status));
355
0
            if (unfinished_node_channel_ids.empty()) {
356
0
                break;
357
0
            }
358
0
            int64_t elapsed_ms = UnixMillis() - arrival_quorum_success_time;
359
0
            if (elapsed_ms > max_wait_time_ms ||
360
0
                _parent->_load_channel_timeout_s - elapsed_ms / 1000 <
361
0
                        config::quorum_success_remaining_timeout_seconds) {
362
                // cancel unfinished node channel
363
0
                std::stringstream unfinished_node_channel_host_str;
364
0
                for (auto& it : unfinished_node_channel_ids) {
365
0
                    unfinished_node_channel_host_str << _node_channels[it]->host() << ",";
366
0
                    _node_channels[it]->cancel("timeout");
367
0
                }
368
0
                LOG(WARNING) << "reach max wait time, max_wait_time_ms: " << max_wait_time_ms
369
0
                             << ", cancel unfinished node channel and finish close"
370
0
                             << ", load id: " << print_id(_parent->_load_id)
371
0
                             << ", txn_id: " << _parent->_txn_id << ", unfinished node channel: "
372
0
                             << unfinished_node_channel_host_str.str();
373
0
                break;
374
0
            }
375
0
            bthread_usleep(1000 * 10);
376
0
        }
377
0
    }
378
68.1k
    return status;
379
68.1k
}
380
381
Status IndexChannel::check_each_node_channel_close(
382
        std::unordered_set<int64_t>* unfinished_node_channel_ids,
383
        std::unordered_map<int64_t, AddBatchCounter>* node_add_batch_counter_map,
384
1.51M
        WriterStats* writer_stats, Status status) {
385
1.51M
    Status final_status = Status::OK();
386
1.51M
    for (auto& it : _node_channels) {
387
1.51M
        std::shared_ptr<VNodeChannel> node_channel = it.second;
388
        // If the node channel is not in the unfinished_node_channel_ids,
389
        // it means the node channel is already closed.
390
1.51M
        if (!unfinished_node_channel_ids->contains(it.first)) {
391
0
            continue;
392
0
        }
393
1.51M
        bool node_channel_closed = false;
394
1.51M
        auto close_status = it.second->close_wait(_parent->_state, &node_channel_closed);
395
1.51M
        if (node_channel_closed) {
396
68.1k
            close_status = it.second->after_close_handle(_parent->_state, writer_stats,
397
68.1k
                                                         node_add_batch_counter_map);
398
68.1k
            unfinished_node_channel_ids->erase(it.first);
399
68.1k
        }
400
1.51M
        DBUG_EXECUTE_IF("IndexChannel.check_each_node_channel_close.close_status_not_ok",
401
1.51M
                        { close_status = Status::InternalError("injected close status not ok"); });
402
1.51M
        if (!close_status.ok()) {
403
48
            final_status = cancel_channel_and_check_intolerable_failure(
404
48
                    std::move(final_status), close_status.to_string(), *this, *it.second);
405
48
        }
406
1.51M
    }
407
408
1.51M
    return final_status;
409
1.51M
}
410
411
bool IndexChannel::_quorum_success(const std::unordered_set<int64_t>& unfinished_node_channel_ids,
412
1.51M
                                   const std::unordered_set<int64_t>& need_finish_tablets) {
413
1.51M
    if (!config::enable_quorum_success_write) {
414
0
        return false;
415
0
    }
416
1.51M
    if (need_finish_tablets.empty()) [[unlikely]] {
417
86.5k
        return false;
418
86.5k
    }
419
420
    // 1. collect all write tablets and finished tablets
421
1.42M
    std::unordered_map<int64_t, int64_t> finished_tablets_replica;
422
1.42M
    for (const auto& [node_id, node_channel] : _node_channels) {
423
1.42M
        if (unfinished_node_channel_ids.contains(node_id) || !node_channel->check_status().ok()) {
424
1.39M
            continue;
425
1.39M
        }
426
272k
        for (const auto& tablet_id : _tablets_by_channel[node_id]) {
427
272k
            finished_tablets_replica[tablet_id]++;
428
272k
        }
429
29.6k
    }
430
431
    // 2. check if quorum success
432
1.58M
    for (const auto& tablet_id : need_finish_tablets) {
433
1.58M
        if (finished_tablets_replica[tablet_id] < _load_required_replicas_num(tablet_id)) {
434
1.39M
            return false;
435
1.39M
        }
436
1.58M
    }
437
438
26.9k
    return true;
439
1.42M
}
440
441
int64_t IndexChannel::_calc_max_wait_time_ms(
442
0
        const std::unordered_set<int64_t>& unfinished_node_channel_ids) {
443
    // 1. calculate avg speed of all unfinished node channel
444
0
    int64_t elapsed_ms = UnixMillis() - _start_time;
445
0
    int64_t total_bytes = 0;
446
0
    int finished_count = 0;
447
0
    for (const auto& [node_id, node_channel] : _node_channels) {
448
0
        if (unfinished_node_channel_ids.contains(node_id)) {
449
0
            continue;
450
0
        }
451
0
        total_bytes += node_channel->write_bytes();
452
0
        finished_count++;
453
0
    }
454
    // no data loaded in index channel, return 0
455
0
    if (total_bytes == 0 || finished_count == 0) {
456
0
        return 0;
457
0
    }
458
    // if elapsed_ms is equal to 0, explain the loaded data is too small
459
0
    if (elapsed_ms <= 0) {
460
0
        return config::quorum_success_min_wait_seconds * 1000;
461
0
    }
462
0
    double avg_speed =
463
0
            static_cast<double>(total_bytes) / (static_cast<double>(elapsed_ms) * finished_count);
464
465
    // 2. calculate max wait time of each unfinished node channel and return the max value
466
0
    int64_t max_wait_time_ms = 0;
467
0
    for (int64_t id : unfinished_node_channel_ids) {
468
0
        int64_t bytes = _node_channels[id]->write_bytes();
469
0
        int64_t wait =
470
0
                avg_speed > 0 ? static_cast<int64_t>(static_cast<double>(bytes) / avg_speed) : 0;
471
0
        max_wait_time_ms = std::max(max_wait_time_ms, wait);
472
0
    }
473
474
    // 3. calculate max wait time
475
    // introduce quorum_success_min_wait_seconds to avoid jitter of small load
476
0
    max_wait_time_ms -= UnixMillis() - _start_time;
477
0
    max_wait_time_ms =
478
0
            std::max(static_cast<int64_t>(static_cast<double>(max_wait_time_ms) *
479
0
                                          (1.0 + config::quorum_success_max_wait_multiplier)),
480
0
                     config::quorum_success_min_wait_seconds * 1000);
481
482
0
    return max_wait_time_ms;
483
0
}
484
485
1.64M
static Status none_of(std::initializer_list<bool> vars) {
486
3.29M
    bool none = std::none_of(vars.begin(), vars.end(), [](bool var) { return var; });
487
1.64M
    Status st = Status::OK();
488
1.64M
    if (!none) {
489
48
        std::string vars_str;
490
48
        std::for_each(vars.begin(), vars.end(),
491
96
                      [&vars_str](bool var) -> void { vars_str += (var ? "1/" : "0/"); });
492
48
        if (!vars_str.empty()) {
493
48
            vars_str.pop_back(); // 0/1/0/ -> 0/1/0
494
48
        }
495
48
        st = Status::Uninitialized(vars_str);
496
48
    }
497
498
1.64M
    return st;
499
1.64M
}
500
501
VNodeChannel::VNodeChannel(VTabletWriter* parent, IndexChannel* index_channel, int64_t node_id,
502
                           bool is_incremental)
503
67.9k
        : _parent(parent),
504
67.9k
          _index_channel(index_channel),
505
67.9k
          _node_id(node_id),
506
67.9k
          _is_incremental(is_incremental) {
507
67.9k
    _cur_add_block_request = std::make_shared<PTabletWriterAddBlockRequest>();
508
67.9k
    _node_channel_tracker = std::make_shared<MemTracker>(
509
67.9k
            fmt::format("NodeChannel:indexID={}:threadId={}",
510
67.9k
                        std::to_string(_index_channel->_index_id), ThreadContext::get_thread_id()));
511
67.9k
    _load_mem_limit = MemInfo::mem_limit() * config::load_process_max_memory_limit_percent / 100;
512
67.9k
}
513
514
68.2k
VNodeChannel::~VNodeChannel() = default;
515
516
68.2k
void VNodeChannel::clear_all_blocks() {
517
68.2k
    std::lock_guard<std::mutex> lg(_pending_batches_lock);
518
68.2k
    std::queue<AddBlockReq> empty;
519
68.2k
    std::swap(_pending_blocks, empty);
520
68.2k
    _cur_mutable_block.reset();
521
68.2k
}
522
523
// we don't need to send tablet_writer_cancel rpc request when
524
// init failed, so set _is_closed to true.
525
// if "_cancelled" is set to true,
526
// no need to set _cancel_msg because the error will be
527
// returned directly via "TabletSink::prepare()" method.
528
68.1k
Status VNodeChannel::init(RuntimeState* state) {
529
68.1k
    if (_inited) {
530
179
        return Status::OK();
531
179
    }
532
533
67.9k
    SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker.get());
534
67.9k
    _task_exec_ctx = state->get_task_execution_context();
535
67.9k
    _tuple_desc = _parent->_output_tuple_desc;
536
67.9k
    _state = state;
537
    // get corresponding BE node.
538
67.9k
    const auto* node = _parent->_nodes_info->find_node(_node_id);
539
67.9k
    if (node == nullptr) {
540
0
        _cancelled = true;
541
0
        _is_closed = true;
542
0
        return Status::InternalError("unknown node id, id={}", _node_id);
543
0
    }
544
67.9k
    _node_info = *node;
545
546
67.9k
    _load_info = "load_id=" + print_id(_parent->_load_id) +
547
67.9k
                 ", txn_id=" + std::to_string(_parent->_txn_id);
548
549
67.9k
    _row_desc = std::make_unique<RowDescriptor>(_tuple_desc);
550
67.9k
    _batch_size = state->batch_size();
551
552
67.9k
    _stub = state->exec_env()->brpc_internal_client_cache()->get_client(_node_info.host,
553
67.9k
                                                                        _node_info.brpc_port);
554
67.9k
    if (_stub == nullptr) {
555
0
        _cancelled = true;
556
0
        _is_closed = true;
557
0
        return Status::InternalError("Get rpc stub failed, host={}, port={}, info={}",
558
0
                                     _node_info.host, _node_info.brpc_port, channel_info());
559
0
    }
560
561
67.9k
    _rpc_timeout_ms = state->execution_timeout() * 1000;
562
67.9k
    _timeout_watch.start();
563
564
    // Initialize _cur_add_block_request
565
68.2k
    if (!_cur_add_block_request->has_id()) {
566
68.2k
        *(_cur_add_block_request->mutable_id()) = _parent->_load_id;
567
68.2k
    }
568
67.9k
    _cur_add_block_request->set_index_id(_index_channel->_index_id);
569
67.9k
    _cur_add_block_request->set_sender_id(_parent->_sender_id);
570
67.9k
    _cur_add_block_request->set_backend_id(_node_id);
571
67.9k
    _cur_add_block_request->set_eos(false);
572
573
    // add block closure
574
    // Has to using value to capture _task_exec_ctx because tablet writer may destroyed during callback.
575
67.9k
    _send_block_callback = WriteBlockCallback<PTabletWriterAddBlockResult>::create_shared();
576
67.9k
    _send_block_callback->addFailedHandler(
577
67.9k
            [&, task_exec_ctx = _task_exec_ctx](const WriteBlockCallbackContext& ctx) {
578
0
                std::shared_ptr<TaskExecutionContext> ctx_lock = task_exec_ctx.lock();
579
0
                if (ctx_lock == nullptr) {
580
0
                    return;
581
0
                }
582
0
                _add_block_failed_callback(ctx);
583
0
            });
584
585
67.9k
    _send_block_callback->addSuccessHandler(
586
67.9k
            [&, task_exec_ctx = _task_exec_ctx](const PTabletWriterAddBlockResult& result,
587
70.4k
                                                const WriteBlockCallbackContext& ctx) {
588
70.4k
                std::shared_ptr<TaskExecutionContext> ctx_lock = task_exec_ctx.lock();
589
70.4k
                if (ctx_lock == nullptr) {
590
0
                    return;
591
0
                }
592
70.4k
                _add_block_success_callback(result, ctx);
593
70.4k
            });
594
595
67.9k
    _name = fmt::format("VNodeChannel[{}-{}]", _index_channel->_index_id, _node_id);
596
    // The node channel will send _batch_size rows of data each rpc. When the
597
    // number of tablets is large, the number of data rows received by each
598
    // tablet is small, TabletsChannel need to traverse each tablet for import.
599
    // so the import performance is poor. Therefore, we set _batch_size to
600
    // a relatively large value to improve the import performance.
601
67.9k
    _batch_size = std::max(_batch_size, 8192);
602
603
67.9k
    if (_state) {
604
67.9k
        QueryContext* query_ctx = _state->get_query_ctx();
605
67.9k
        if (query_ctx) {
606
67.9k
            auto wg_ptr = query_ctx->workload_group();
607
68.1k
            if (wg_ptr) {
608
68.1k
                _wg_id = wg_ptr->id();
609
68.1k
            }
610
67.9k
        }
611
67.9k
    }
612
613
67.9k
    _inited = true;
614
67.9k
    return Status::OK();
615
67.9k
}
616
617
68.2k
void VNodeChannel::_open_internal(bool is_incremental) {
618
68.2k
    if (_tablets_wait_open.empty()) {
619
0
        return;
620
0
    }
621
68.2k
    SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker.get());
622
68.2k
    auto request = std::make_shared<PTabletWriterOpenRequest>();
623
68.2k
    request->set_allocated_id(&_parent->_load_id);
624
68.2k
    request->set_index_id(_index_channel->_index_id);
625
68.2k
    request->set_txn_id(_parent->_txn_id);
626
68.2k
    request->set_sender_id(_parent->_sender_id);
627
68.2k
    request->set_allocated_schema(_parent->_schema->to_protobuf());
628
68.2k
    if (_parent->_t_sink.olap_table_sink.__isset.storage_vault_id) {
629
0
        request->set_storage_vault_id(_parent->_t_sink.olap_table_sink.storage_vault_id);
630
0
    }
631
68.2k
    std::set<int64_t> deduper;
632
403k
    for (auto& tablet : _tablets_wait_open) {
633
403k
        if (deduper.contains(tablet.tablet_id)) {
634
0
            continue;
635
0
        }
636
403k
        auto* ptablet = request->add_tablets();
637
403k
        ptablet->set_partition_id(tablet.partition_id);
638
403k
        ptablet->set_tablet_id(tablet.tablet_id);
639
403k
        deduper.insert(tablet.tablet_id);
640
403k
        _all_tablets.push_back(std::move(tablet));
641
403k
    }
642
68.2k
    _tablets_wait_open.clear();
643
644
68.2k
    request->set_num_senders(_parent->_num_senders);
645
68.2k
    request->set_need_gen_rollup(false); // Useless but it is a required field in pb
646
68.2k
    request->set_load_channel_timeout_s(_parent->_load_channel_timeout_s);
647
68.2k
    request->set_is_high_priority(_parent->_is_high_priority);
648
68.2k
    request->set_sender_ip(BackendOptions::get_localhost());
649
68.2k
    request->set_is_vectorized(true);
650
68.2k
    request->set_backend_id(_node_id);
651
68.2k
    request->set_enable_profile(_state->enable_profile());
652
68.2k
    request->set_is_incremental(is_incremental);
653
68.2k
    request->set_txn_expiration(_parent->_txn_expiration);
654
68.2k
    request->set_write_file_cache(_parent->_write_file_cache);
655
656
68.2k
    if (_wg_id > 0) {
657
68.0k
        request->set_workload_group_id(_wg_id);
658
68.0k
    }
659
660
68.2k
    auto open_callback = DummyBrpcCallback<PTabletWriterOpenResult>::create_shared();
661
68.2k
    auto open_closure = AutoReleaseClosure<
662
68.2k
            PTabletWriterOpenRequest,
663
68.2k
            DummyBrpcCallback<PTabletWriterOpenResult>>::create_unique(request, open_callback);
664
68.2k
    open_callback->cntl_->set_timeout_ms(config::tablet_writer_open_rpc_timeout_sec * 1000);
665
68.2k
    if (config::tablet_writer_ignore_eovercrowded) {
666
67.9k
        open_callback->cntl_->ignore_eovercrowded();
667
67.9k
    }
668
68.2k
    VLOG_DEBUG << fmt::format("txn {}: open NodeChannel to {}, incremental: {}, senders: {}",
669
321
                              _parent->_txn_id, _node_id, is_incremental, _parent->_num_senders);
670
    // the real transmission here. the corresponding BE's load mgr will open load channel for it.
671
68.2k
    _stub->tablet_writer_open(open_closure->cntl_.get(), open_closure->request_.get(),
672
68.2k
                              open_closure->response_.get(), open_closure.get());
673
68.2k
    open_closure.release();
674
68.2k
    _open_callbacks.push_back(open_callback);
675
676
68.2k
    static_cast<void>(request->release_id());
677
68.2k
    static_cast<void>(request->release_schema());
678
68.2k
}
679
680
68.1k
void VNodeChannel::open() {
681
68.1k
    _open_internal(false);
682
68.1k
}
683
684
179
void VNodeChannel::incremental_open() {
685
179
    VLOG_DEBUG << "incremental opening node channel" << _node_id;
686
179
    _open_internal(true);
687
179
}
688
689
68.3k
Status VNodeChannel::open_wait() {
690
68.3k
    Status status;
691
68.4k
    for (auto& open_callback : _open_callbacks) {
692
        // because of incremental open, we will wait multi times. so skip the closures which have been checked and set to nullptr in previous rounds
693
68.4k
        if (open_callback == nullptr) {
694
0
            continue;
695
0
        }
696
697
68.4k
        open_callback->join();
698
68.4k
        SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker.get());
699
68.4k
        if (open_callback->cntl_->Failed()) {
700
0
            if (!ExecEnv::GetInstance()->brpc_internal_client_cache()->available(
701
0
                        _stub, _node_info.host, _node_info.brpc_port)) {
702
0
                ExecEnv::GetInstance()->brpc_internal_client_cache()->erase(
703
0
                        open_callback->cntl_->remote_side());
704
0
            }
705
0
            _cancelled = true;
706
0
            auto error_code = open_callback->cntl_->ErrorCode();
707
0
            auto error_text = open_callback->cntl_->ErrorText();
708
0
            if (error_text.find("Reached timeout") != std::string::npos) {
709
0
                LOG(WARNING) << "failed to open tablet writer may caused by timeout. increase BE "
710
0
                                "config `tablet_writer_open_rpc_timeout_sec` if you are sure that "
711
0
                                "your table building and data are reasonable.";
712
0
            }
713
0
            return Status::Error<ErrorCode::INTERNAL_ERROR, false>(
714
0
                    "failed to open tablet writer, error={}, error_text={}, info={}",
715
0
                    berror(error_code), error_text, channel_info());
716
0
        }
717
68.4k
        status = Status::create(open_callback->response_->status());
718
719
68.4k
        if (!status.ok()) {
720
0
            _cancelled = true;
721
0
            return status;
722
0
        }
723
68.4k
    }
724
725
68.3k
    return status;
726
68.3k
}
727
728
37.0k
Status VNodeChannel::add_block(Block* block, const Payload* payload) {
729
37.0k
    SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker.get());
730
37.0k
    if (payload->second.empty()) {
731
0
        return Status::OK();
732
0
    }
733
    // If add_block() when _eos_is_produced==true, there must be sth wrong, we can only mark this channel as failed.
734
37.0k
    auto st = none_of({_cancelled, _eos_is_produced});
735
37.0k
    if (!st.ok()) {
736
0
        if (_cancelled) {
737
0
            std::lock_guard<std::mutex> l(_cancel_msg_lock);
738
0
            return Status::Error<ErrorCode::INTERNAL_ERROR, false>("add row failed. {}",
739
0
                                                                   _cancel_msg);
740
0
        } else {
741
0
            return std::move(st.prepend("already stopped, can't add row. cancelled/eos: "));
742
0
        }
743
0
    }
744
745
    // We use OlapTableSink mem_tracker which has the same ancestor of _plan node,
746
    // so in the ideal case, mem limit is a matter for _plan node.
747
    // But there is still some unfinished things, we do mem limit here temporarily.
748
    // _cancelled may be set by rpc callback, and it's possible that _cancelled might be set in any of the steps below.
749
    // It's fine to do a fake add_block() and return OK, because we will check _cancelled in next add_block() or mark_close().
750
37.0k
    constexpr int64_t kBackPressureSleepMs = 10;
751
37.0k
    auto* memtable_limiter = ExecEnv::GetInstance()->memtable_memory_limiter();
752
37.0k
    while (true) {
753
37.0k
        bool is_exceed_soft_mem_limit = GlobalMemoryArbitrator::is_exceed_soft_mem_limit();
754
37.0k
        int64_t memtable_mem =
755
37.0k
                (memtable_limiter != nullptr && memtable_limiter->mem_tracker() != nullptr)
756
37.0k
                        ? memtable_limiter->mem_tracker()->consumption()
757
37.0k
                        : 0;
758
        // Note: Memtable memory is not included in load memory statistics (MemoryProfile::load_current_usage())
759
        // for performance and memory control complexity reasons. Therefore, we explicitly add memtable memory
760
        // consumption here to ensure accurate back pressure decisions and prevent OOM during heavy loads.
761
37.0k
        auto current_load_mem_value = MemoryProfile::load_current_usage() + memtable_mem;
762
37.0k
        bool mem_limit_exceeded = is_exceed_soft_mem_limit ||
763
37.0k
                                  current_load_mem_value > _load_mem_limit ||
764
37.0k
                                  _pending_batches_bytes > _max_pending_batches_bytes;
765
37.0k
        bool need_back_pressure = !_cancelled && !_state->is_cancelled() &&
766
37.0k
                                  _pending_batches_num > 0 && mem_limit_exceeded;
767
37.0k
        if (!need_back_pressure) {
768
37.0k
            break;
769
37.0k
        }
770
0
        SCOPED_RAW_TIMER(&_stat.mem_exceeded_block_ns);
771
0
        std::this_thread::sleep_for(std::chrono::milliseconds(kBackPressureSleepMs));
772
0
    }
773
774
37.0k
    if (UNLIKELY(!_cur_mutable_block)) {
775
30.2k
        _cur_mutable_block = MutableBlock::create_unique(block->clone_empty());
776
30.2k
    }
777
778
37.0k
    SCOPED_RAW_TIMER(&_stat.append_node_channel_ns);
779
37.0k
    st = block->append_to_block_by_selector(_cur_mutable_block.get(), *(payload->first));
780
37.0k
    if (!st.ok()) {
781
0
        _cancel_with_msg(fmt::format("{}, err: {}", channel_info(), st.to_string()));
782
0
        return st;
783
0
    }
784
35.7M
    for (auto tablet_id : payload->second) {
785
35.7M
        _cur_add_block_request->add_tablet_ids(tablet_id);
786
35.7M
    }
787
37.0k
    _write_bytes.fetch_add(_cur_mutable_block->bytes());
788
789
37.0k
    if (_cur_mutable_block->rows() >= _batch_size ||
790
37.0k
        _cur_mutable_block->bytes() > config::doris_scanner_row_bytes) {
791
2.35k
        {
792
2.35k
            SCOPED_ATOMIC_TIMER(&_queue_push_lock_ns);
793
2.35k
            std::lock_guard<std::mutex> l(_pending_batches_lock);
794
            // To simplify the add_row logic, postpone adding block into req until the time of sending req
795
2.35k
            _pending_batches_bytes += _cur_mutable_block->allocated_bytes();
796
2.35k
            _cur_add_block_request->set_eos(
797
2.35k
                    false); // for multi-add, only when marking close we set it eos.
798
            // Copy the request to tmp request to add to pend block queue
799
2.35k
            auto tmp_add_block_request = std::make_shared<PTabletWriterAddBlockRequest>();
800
2.35k
            *tmp_add_block_request = *_cur_add_block_request;
801
2.35k
            _pending_blocks.emplace(std::move(_cur_mutable_block), tmp_add_block_request);
802
2.35k
            _pending_batches_num++;
803
2.35k
            VLOG_DEBUG << "VTabletWriter:" << _parent << " VNodeChannel:" << this
804
0
                       << " pending_batches_bytes:" << _pending_batches_bytes
805
0
                       << " jobid:" << std::to_string(_state->load_job_id())
806
0
                       << " loadinfo:" << _load_info;
807
2.35k
        }
808
2.35k
        _cur_mutable_block = MutableBlock::create_unique(block->clone_empty());
809
2.35k
        _cur_add_block_request->clear_tablet_ids();
810
2.35k
    }
811
812
37.0k
    return Status::OK();
813
37.0k
}
814
815
0
static void injection_full_gc_fn() {
816
0
    MemoryReclamation::revoke_process_memory("injection_full_gc_fn");
817
0
}
818
819
int VNodeChannel::try_send_and_fetch_status(RuntimeState* state,
820
23.7M
                                            std::unique_ptr<ThreadPoolToken>& thread_pool_token) {
821
23.7M
    DBUG_EXECUTE_IF("VNodeChannel.try_send_and_fetch_status_full_gc", {
822
23.7M
        std::thread t(injection_full_gc_fn);
823
23.7M
        t.join();
824
23.7M
    });
825
826
24.1M
    if (_cancelled || _send_finished) { // not run
827
965k
        return 0;
828
965k
    }
829
830
22.7M
    auto load_back_pressure_version_wait_time_ms = _load_back_pressure_version_wait_time_ms.load();
831
22.7M
    if (UNLIKELY(load_back_pressure_version_wait_time_ms > 0)) {
832
0
        std::this_thread::sleep_for(
833
0
                std::chrono::milliseconds(load_back_pressure_version_wait_time_ms));
834
0
        _load_back_pressure_version_block_ms.fetch_add(
835
0
                load_back_pressure_version_wait_time_ms); // already in milliseconds
836
0
        _load_back_pressure_version_wait_time_ms = 0;
837
0
    }
838
839
    // set closure for sending block.
840
22.7M
    if (!_send_block_callback->try_set_in_flight()) {
841
        // There is packet in flight, skip.
842
1.18M
        return _send_finished ? 0 : 1;
843
1.18M
    }
844
845
    // We are sure that try_send_batch is not running
846
21.5M
    if (_pending_batches_num > 0) {
847
70.5k
        auto s = thread_pool_token->submit_func([this, state] { try_send_pending_block(state); });
848
70.5k
        if (!s.ok()) {
849
0
            _cancel_with_msg("submit send_batch task to send_batch_thread_pool failed");
850
            // sending finished. clear in flight
851
0
            _send_block_callback->clear_in_flight();
852
0
        }
853
        // in_flight is cleared in closure::Run
854
21.5M
    } else {
855
        // sending finished. clear in flight
856
21.5M
        _send_block_callback->clear_in_flight();
857
21.5M
    }
858
21.5M
    return _send_finished ? 0 : 1;
859
22.7M
}
860
861
158
void VNodeChannel::_cancel_with_msg(const std::string& msg) {
862
158
    LOG(WARNING) << "cancel node channel " << channel_info() << ", error message: " << msg;
863
158
    {
864
158
        std::lock_guard<std::mutex> l(_cancel_msg_lock);
865
158
        if (_cancel_msg.empty()) {
866
110
            _cancel_msg = msg;
867
110
        }
868
158
    }
869
158
    _cancelled = true;
870
158
}
871
872
void VNodeChannel::_refresh_back_pressure_version_wait_time(
873
        const ::google::protobuf::RepeatedPtrField<::doris::PTabletLoadRowsetInfo>&
874
70.2k
                tablet_load_infos) {
875
70.2k
    int64_t max_rowset_num_gap = 0;
876
    // if any one tablet is under high load pressure, we would make the whole procedure
877
    // sleep to prevent the corresponding BE return -235
878
70.2k
    std::for_each(
879
70.2k
            tablet_load_infos.begin(), tablet_load_infos.end(),
880
70.2k
            [&max_rowset_num_gap](auto& load_info) {
881
0
                int64_t cur_rowset_num = load_info.current_rowset_nums();
882
0
                int64_t high_load_point = load_info.max_config_rowset_nums() *
883
0
                                          (config::load_back_pressure_version_threshold / 100);
884
0
                DCHECK(cur_rowset_num > high_load_point);
885
0
                max_rowset_num_gap = std::max(max_rowset_num_gap, cur_rowset_num - high_load_point);
886
0
            });
887
    // to slow down the high load pressure
888
    // we would use the rowset num gap to calculate one sleep time
889
    // for example:
890
    // if the max tablet version is 2000, there are 3 BE
891
    // A: ====================  1800
892
    // B: ===================   1700
893
    // C: ==================    1600
894
    //    ==================    1600
895
    //                      ^
896
    //                      the high load point
897
    // then then max gap is 1800 - (max tablet version * config::load_back_pressure_version_threshold / 100) = 200,
898
    // we would make the whole send procesure sleep
899
    // 1200ms for compaction to be done toe reduce the high pressure
900
70.2k
    auto max_time = config::max_load_back_pressure_version_wait_time_ms;
901
70.2k
    if (UNLIKELY(max_rowset_num_gap > 0)) {
902
0
        _load_back_pressure_version_wait_time_ms.store(
903
0
                std::min(max_rowset_num_gap + 1000, max_time));
904
0
        LOG(INFO) << "try to back pressure version, wait time(ms): "
905
0
                  << _load_back_pressure_version_wait_time_ms
906
0
                  << ", load id: " << print_id(_parent->_load_id)
907
0
                  << ", max_rowset_num_gap: " << max_rowset_num_gap;
908
0
    }
909
70.2k
}
910
911
70.5k
void VNodeChannel::try_send_pending_block(RuntimeState* state) {
912
70.5k
    SCOPED_ATTACH_TASK(state);
913
70.5k
    SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker);
914
70.5k
    SCOPED_ATOMIC_TIMER(&_actual_consume_ns);
915
70.5k
    signal::set_signal_task_id(_parent->_load_id);
916
70.5k
    AddBlockReq send_block;
917
70.5k
    {
918
70.5k
        std::lock_guard<std::mutex> l(_pending_batches_lock);
919
70.5k
        DCHECK(!_pending_blocks.empty());
920
70.5k
        send_block = std::move(_pending_blocks.front());
921
70.5k
        _pending_blocks.pop();
922
70.5k
        _pending_batches_num--;
923
70.5k
        _pending_batches_bytes -= send_block.first->allocated_bytes();
924
70.5k
    }
925
926
70.5k
    auto mutable_block = std::move(send_block.first);
927
70.5k
    auto request = std::move(send_block.second); // doesn't need to be saved in heap
928
929
    // tablet_ids has already set when add row
930
70.5k
    request->set_packet_seq(_next_packet_seq);
931
70.5k
    auto block = mutable_block->to_block();
932
70.5k
    CHECK(block.rows() == request->tablet_ids_size())
933
17
            << "block rows: " << block.rows()
934
17
            << ", tablet_ids_size: " << request->tablet_ids_size();
935
70.5k
    if (block.rows() > 0) {
936
32.3k
        SCOPED_ATOMIC_TIMER(&_serialize_batch_ns);
937
32.3k
        size_t uncompressed_bytes = 0, compressed_bytes = 0;
938
32.3k
        int64_t compressed_time = 0;
939
32.3k
        Status st = block.serialize(state->be_exec_version(), request->mutable_block(),
940
32.3k
                                    &uncompressed_bytes, &compressed_bytes, &compressed_time,
941
32.3k
                                    state->fragement_transmission_compression_type(),
942
32.3k
                                    _parent->_transfer_large_data_by_brpc);
943
32.3k
        TEST_INJECTION_POINT_CALLBACK("VNodeChannel::try_send_block", &st);
944
32.3k
        if (!st.ok()) {
945
0
            cancel(fmt::format("{}, err: {}", channel_info(), st.to_string()));
946
0
            _send_block_callback->clear_in_flight();
947
0
            return;
948
0
        }
949
32.3k
        if (double(compressed_bytes) >= double(config::brpc_max_body_size) * 0.95F) {
950
0
            LOG(WARNING) << "send block too large, this rpc may failed. send size: "
951
0
                         << compressed_bytes << ", threshold: " << config::brpc_max_body_size
952
0
                         << ", " << channel_info();
953
0
        }
954
32.3k
    }
955
956
70.5k
    auto remain_ms = _rpc_timeout_ms - _timeout_watch.elapsed_time() / NANOS_PER_MILLIS;
957
70.5k
    if (UNLIKELY(remain_ms < config::min_load_rpc_timeout_ms)) {
958
4
        if (remain_ms <= 0 && !request->eos()) {
959
0
            cancel(fmt::format("{}, err: load timeout after {} ms", channel_info(),
960
0
                               _rpc_timeout_ms));
961
0
            _send_block_callback->clear_in_flight();
962
0
            return;
963
4
        } else {
964
4
            remain_ms = config::min_load_rpc_timeout_ms;
965
4
        }
966
4
    }
967
968
70.5k
    _send_block_callback->reset();
969
70.5k
    _send_block_callback->cntl_->set_timeout_ms(remain_ms);
970
70.5k
    if (config::tablet_writer_ignore_eovercrowded) {
971
70.5k
        _send_block_callback->cntl_->ignore_eovercrowded();
972
70.5k
    }
973
974
70.5k
    if (request->eos()) {
975
68.1k
        for (auto pid : _parent->_tablet_finder->partition_ids()) {
976
32.9k
            request->add_partition_ids(pid);
977
32.9k
        }
978
979
68.1k
        request->set_write_single_replica(_parent->_write_single_replica);
980
68.1k
        if (_parent->_write_single_replica) {
981
0
            for (auto& _slave_tablet_node : _slave_tablet_nodes) {
982
0
                PSlaveTabletNodes slave_tablet_nodes;
983
0
                for (auto node_id : _slave_tablet_node.second) {
984
0
                    const auto* node = _parent->_nodes_info->find_node(node_id);
985
0
                    DBUG_EXECUTE_IF("VNodeChannel.try_send_pending_block.slave_node_not_found", {
986
0
                        LOG(WARNING) << "trigger "
987
0
                                        "VNodeChannel.try_send_pending_block.slave_node_not_found "
988
0
                                        "debug point will set node to nullptr";
989
0
                        node = nullptr;
990
0
                    });
991
0
                    if (node == nullptr) {
992
0
                        LOG(WARNING) << "slave node not found, node_id=" << node_id;
993
0
                        cancel(fmt::format("slave node not found, node_id={}", node_id));
994
0
                        _send_block_callback->clear_in_flight();
995
0
                        return;
996
0
                    }
997
0
                    PNodeInfo* pnode = slave_tablet_nodes.add_slave_nodes();
998
0
                    pnode->set_id(node->id);
999
0
                    pnode->set_option(node->option);
1000
0
                    pnode->set_host(node->host);
1001
0
                    pnode->set_async_internal_port(node->brpc_port);
1002
0
                }
1003
0
                request->mutable_slave_tablet_nodes()->insert(
1004
0
                        {_slave_tablet_node.first, slave_tablet_nodes});
1005
0
            }
1006
0
        }
1007
1008
        // eos request must be the last request-> it's a signal makeing callback function to set _add_batch_finished true.
1009
        // end_mark makes is_last_rpc true when rpc finished and call callbacks.
1010
68.1k
        _send_block_callback->end_mark();
1011
68.1k
        _send_finished = true;
1012
18.4E
        CHECK(_pending_batches_num == 0) << _pending_batches_num;
1013
68.1k
    }
1014
1015
70.5k
    auto send_block_closure = AutoReleaseClosure<
1016
70.5k
            PTabletWriterAddBlockRequest,
1017
70.5k
            WriteBlockCallback<PTabletWriterAddBlockResult>>::create_unique(request,
1018
70.5k
                                                                            _send_block_callback);
1019
70.5k
    if (_parent->_transfer_large_data_by_brpc && request->has_block() &&
1020
70.5k
        request->block().has_column_values() && request->ByteSizeLong() > MIN_HTTP_BRPC_SIZE) {
1021
0
        Status st = request_embed_attachment_contain_blockv2(send_block_closure->request_.get(),
1022
0
                                                             send_block_closure);
1023
0
        if (!st.ok()) {
1024
0
            cancel(fmt::format("{}, err: {}", channel_info(), st.to_string()));
1025
0
            _send_block_callback->clear_in_flight();
1026
0
            return;
1027
0
        }
1028
1029
0
        std::string host = _node_info.host;
1030
0
        auto dns_cache = ExecEnv::GetInstance()->dns_cache();
1031
0
        if (dns_cache == nullptr) {
1032
0
            LOG(WARNING) << "DNS cache is not initialized, skipping hostname resolve";
1033
0
        } else if (!is_valid_ip(_node_info.host)) {
1034
0
            Status status = dns_cache->get(_node_info.host, &host);
1035
0
            if (!status.ok()) {
1036
0
                LOG(WARNING) << "failed to get ip from host " << _node_info.host << ": "
1037
0
                             << status.to_string();
1038
0
                cancel(fmt::format("failed to get ip from host {}", _node_info.host));
1039
0
                _send_block_callback->clear_in_flight();
1040
0
                return;
1041
0
            }
1042
0
        }
1043
        //format an ipv6 address
1044
0
        std::string brpc_url = get_brpc_http_url(host, _node_info.brpc_port);
1045
0
        std::shared_ptr<PBackendService_Stub> _brpc_http_stub =
1046
0
                _state->exec_env()->brpc_internal_client_cache()->get_new_client_no_cache(brpc_url,
1047
0
                                                                                          "http");
1048
0
        if (_brpc_http_stub == nullptr) {
1049
0
            cancel(fmt::format("{}, failed to open brpc http client to {}", channel_info(),
1050
0
                               brpc_url));
1051
0
            _send_block_callback->clear_in_flight();
1052
0
            return;
1053
0
        }
1054
0
        _send_block_callback->cntl_->http_request().uri() =
1055
0
                brpc_url + "/PInternalServiceImpl/tablet_writer_add_block_by_http";
1056
0
        _send_block_callback->cntl_->http_request().set_method(brpc::HTTP_METHOD_POST);
1057
0
        _send_block_callback->cntl_->http_request().set_content_type("application/json");
1058
1059
0
        {
1060
0
            _brpc_http_stub->tablet_writer_add_block_by_http(
1061
0
                    send_block_closure->cntl_.get(), nullptr, send_block_closure->response_.get(),
1062
0
                    send_block_closure.get());
1063
0
            send_block_closure.release();
1064
0
        }
1065
70.5k
    } else {
1066
70.5k
        _send_block_callback->cntl_->http_request().Clear();
1067
70.5k
        {
1068
70.5k
            _stub->tablet_writer_add_block(
1069
70.5k
                    send_block_closure->cntl_.get(), send_block_closure->request_.get(),
1070
70.5k
                    send_block_closure->response_.get(), send_block_closure.get());
1071
70.5k
            send_block_closure.release();
1072
70.5k
        }
1073
70.5k
    }
1074
1075
70.5k
    _next_packet_seq++;
1076
70.5k
}
1077
1078
void VNodeChannel::_add_block_success_callback(const PTabletWriterAddBlockResult& result,
1079
70.4k
                                               const WriteBlockCallbackContext& ctx) {
1080
70.4k
    std::lock_guard<std::mutex> l(this->_closed_lock);
1081
70.4k
    if (this->_is_closed) {
1082
        // if the node channel is closed, no need to call the following logic,
1083
        // and notice that _index_channel may already be destroyed.
1084
0
        return;
1085
0
    }
1086
70.4k
    SCOPED_ATTACH_TASK(_state);
1087
70.4k
    Status status(Status::create(result.status()));
1088
70.4k
    if (status.ok()) {
1089
70.3k
        _refresh_back_pressure_version_wait_time(result.tablet_load_rowset_num_infos());
1090
        // if has error tablet, handle them first
1091
70.3k
        for (const auto& error : result.tablet_errors()) {
1092
0
            _index_channel->mark_as_failed(this, "tablet error: " + error.msg(), error.tablet_id());
1093
0
        }
1094
1095
70.3k
        Status st = _index_channel->check_intolerable_failure();
1096
70.3k
        if (!st.ok()) {
1097
0
            _cancel_with_msg(st.to_string());
1098
70.3k
        } else if (ctx._is_last_rpc) {
1099
68.0k
            bool skip_tablet_info = false;
1100
68.0k
            DBUG_EXECUTE_IF("VNodeChannel.add_block_success_callback.incomplete_commit_info",
1101
68.0k
                            { skip_tablet_info = true; });
1102
180k
            for (const auto& tablet : result.tablet_vec()) {
1103
180k
                DBUG_EXECUTE_IF("VNodeChannel.add_block_success_callback.incomplete_commit_info", {
1104
180k
                    if (skip_tablet_info) {
1105
180k
                        LOG(INFO) << "skip tablet info: " << tablet.tablet_id();
1106
180k
                        skip_tablet_info = false;
1107
180k
                        continue;
1108
180k
                    }
1109
180k
                });
1110
180k
                TTabletCommitInfo commit_info;
1111
180k
                commit_info.tabletId = tablet.tablet_id();
1112
180k
                commit_info.backendId = _node_id;
1113
180k
                _tablet_commit_infos.emplace_back(std::move(commit_info));
1114
181k
                if (tablet.has_received_rows()) {
1115
181k
                    _tablets_received_rows.emplace_back(tablet.tablet_id(), tablet.received_rows());
1116
181k
                }
1117
181k
                if (tablet.has_num_rows_filtered()) {
1118
181k
                    _tablets_filtered_rows.emplace_back(tablet.tablet_id(),
1119
181k
                                                        tablet.num_rows_filtered());
1120
181k
                }
1121
18.4E
                VLOG_CRITICAL << "master replica commit info: tabletId=" << tablet.tablet_id()
1122
18.4E
                              << ", backendId=" << _node_id
1123
18.4E
                              << ", master node id: " << this->node_id()
1124
18.4E
                              << ", host: " << this->host() << ", txn_id=" << _parent->_txn_id;
1125
180k
            }
1126
68.0k
            if (_parent->_write_single_replica) {
1127
0
                for (const auto& tablet_slave_node_ids : result.success_slave_tablet_node_ids()) {
1128
0
                    for (auto slave_node_id : tablet_slave_node_ids.second.slave_node_ids()) {
1129
0
                        TTabletCommitInfo commit_info;
1130
0
                        commit_info.tabletId = tablet_slave_node_ids.first;
1131
0
                        commit_info.backendId = slave_node_id;
1132
0
                        _tablet_commit_infos.emplace_back(std::move(commit_info));
1133
0
                        VLOG_CRITICAL
1134
0
                                << "slave replica commit info: tabletId="
1135
0
                                << tablet_slave_node_ids.first << ", backendId=" << slave_node_id
1136
0
                                << ", master node id: " << this->node_id()
1137
0
                                << ", host: " << this->host() << ", txn_id=" << _parent->_txn_id;
1138
0
                    }
1139
0
                }
1140
0
            }
1141
68.0k
            _add_batches_finished = true;
1142
68.0k
        }
1143
70.3k
    } else {
1144
111
        _cancel_with_msg(fmt::format("{}, add batch req success but status isn't ok, err: {}",
1145
111
                                     channel_info(), status.to_string()));
1146
111
    }
1147
1148
70.4k
    if (result.has_execution_time_us()) {
1149
70.4k
        _add_batch_counter.add_batch_execution_time_us += result.execution_time_us();
1150
70.4k
        _add_batch_counter.add_batch_wait_execution_time_us += result.wait_execution_time_us();
1151
70.4k
        _add_batch_counter.add_batch_num++;
1152
70.4k
    }
1153
70.4k
    if (result.has_load_channel_profile()) {
1154
31
        TRuntimeProfileTree tprofile;
1155
31
        const auto* buf = (const uint8_t*)result.load_channel_profile().data();
1156
31
        auto len = cast_set<uint32_t>(result.load_channel_profile().size());
1157
31
        auto st = deserialize_thrift_msg(buf, &len, false, &tprofile);
1158
31
        if (st.ok()) {
1159
31
            _state->load_channel_profile()->update(tprofile);
1160
31
        } else {
1161
0
            LOG(WARNING) << "load channel TRuntimeProfileTree deserialize failed, errmsg=" << st;
1162
0
        }
1163
31
    }
1164
70.4k
}
1165
1166
0
void VNodeChannel::_add_block_failed_callback(const WriteBlockCallbackContext& ctx) {
1167
0
    std::lock_guard<std::mutex> l(this->_closed_lock);
1168
0
    if (this->_is_closed) {
1169
        // if the node channel is closed, no need to call `mark_as_failed`,
1170
        // and notice that _index_channel may already be destroyed.
1171
0
        return;
1172
0
    }
1173
0
    SCOPED_ATTACH_TASK(_state);
1174
    // If rpc failed, mark all tablets on this node channel as failed
1175
0
    _index_channel->mark_as_failed(this,
1176
0
                                   fmt::format("rpc failed, error code:{}, error text:{}",
1177
0
                                               _send_block_callback->cntl_->ErrorCode(),
1178
0
                                               _send_block_callback->cntl_->ErrorText()),
1179
0
                                   -1);
1180
0
    if (_send_block_callback->cntl_->ErrorText().find("Reached timeout") != std::string::npos) {
1181
0
        LOG(WARNING) << "rpc failed may caused by timeout. increase BE config "
1182
0
                        "`min_load_rpc_timeout_ms` of to avoid this if you are sure that your "
1183
0
                        "table building and data are reasonable.";
1184
0
    }
1185
0
    Status st = _index_channel->check_intolerable_failure();
1186
0
    if (!st.ok()) {
1187
0
        _cancel_with_msg(fmt::format("{}, err: {}", channel_info(), st.to_string()));
1188
0
    } else if (ctx._is_last_rpc) {
1189
        // if this is last rpc, will must set _add_batches_finished. otherwise, node channel's close_wait
1190
        // will be blocked.
1191
0
        _add_batches_finished = true;
1192
0
    }
1193
0
}
1194
1195
// When _cancelled is true, we still need to send a tablet_writer_cancel
1196
// rpc request to truly release the load channel
1197
157
void VNodeChannel::cancel(const std::string& cancel_msg) {
1198
157
    if (_is_closed) {
1199
        // skip the channels that have been canceled or close_wait.
1200
48
        return;
1201
48
    }
1202
109
    SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker.get());
1203
    // set _is_closed to true finally
1204
110
    Defer set_closed {[&]() {
1205
110
        std::lock_guard<std::mutex> l(_closed_lock);
1206
110
        _is_closed = true;
1207
110
    }};
1208
    // we don't need to wait last rpc finished, cause closure's release/reset will join.
1209
    // But do we need brpc::StartCancel(call_id)?
1210
109
    _cancel_with_msg(cancel_msg);
1211
    // if not inited, _stub will be nullptr, skip sending cancel rpc
1212
109
    if (!_inited) {
1213
0
        return;
1214
0
    }
1215
1216
109
    auto request = std::make_shared<PTabletWriterCancelRequest>();
1217
109
    request->set_allocated_id(&_parent->_load_id);
1218
109
    request->set_index_id(_index_channel->_index_id);
1219
109
    request->set_sender_id(_parent->_sender_id);
1220
109
    request->set_cancel_reason(cancel_msg);
1221
1222
109
    auto cancel_callback = DummyBrpcCallback<PTabletWriterCancelResult>::create_shared();
1223
109
    auto closure = AutoReleaseClosure<
1224
109
            PTabletWriterCancelRequest,
1225
109
            DummyBrpcCallback<PTabletWriterCancelResult>>::create_unique(request, cancel_callback);
1226
1227
109
    auto remain_ms = _rpc_timeout_ms - _timeout_watch.elapsed_time() / NANOS_PER_MILLIS;
1228
109
    if (UNLIKELY(remain_ms < config::min_load_rpc_timeout_ms)) {
1229
0
        remain_ms = config::min_load_rpc_timeout_ms;
1230
0
    }
1231
109
    cancel_callback->cntl_->set_timeout_ms(remain_ms);
1232
110
    if (config::tablet_writer_ignore_eovercrowded) {
1233
110
        closure->cntl_->ignore_eovercrowded();
1234
110
    }
1235
109
    _stub->tablet_writer_cancel(closure->cntl_.get(), closure->request_.get(),
1236
109
                                closure->response_.get(), closure.get());
1237
109
    closure.release();
1238
109
    static_cast<void>(request->release_id());
1239
109
}
1240
1241
1.51M
Status VNodeChannel::close_wait(RuntimeState* state, bool* is_closed) {
1242
1.51M
    DBUG_EXECUTE_IF("VNodeChannel.close_wait_full_gc", {
1243
1.51M
        std::thread t(injection_full_gc_fn);
1244
1.51M
        t.join();
1245
1.51M
    });
1246
1.51M
    SCOPED_CONSUME_MEM_TRACKER(_node_channel_tracker.get());
1247
1248
1.51M
    *is_closed = true;
1249
1250
1.51M
    auto st = none_of({_cancelled, !_eos_is_produced});
1251
1.51M
    if (!st.ok()) {
1252
48
        if (_cancelled) {
1253
48
            std::lock_guard<std::mutex> l(_cancel_msg_lock);
1254
48
            return Status::Error<ErrorCode::INTERNAL_ERROR, false>("wait close failed. {}",
1255
48
                                                                   _cancel_msg);
1256
48
        } else {
1257
0
            return std::move(
1258
0
                    st.prepend("already stopped, skip waiting for close. cancelled/!eos: "));
1259
0
        }
1260
48
    }
1261
1262
1.51M
    DBUG_EXECUTE_IF("VNodeChannel.close_wait.cancelled", {
1263
1.51M
        _cancelled = true;
1264
1.51M
        _cancel_msg = "injected cancel";
1265
1.51M
    });
1266
1267
1.51M
    if (state->is_cancelled()) {
1268
0
        _cancel_with_msg(state->cancel_reason().to_string());
1269
0
    }
1270
1271
    // Waiting for finished until _add_batches_finished changed by rpc's finished callback.
1272
    // it may take a long time, so we couldn't set a timeout
1273
    // For pipeline engine, the close is called in async writer's process block method,
1274
    // so that it will not block pipeline thread.
1275
1.51M
    if (!_add_batches_finished && !_cancelled && !state->is_cancelled()) {
1276
1.44M
        *is_closed = false;
1277
1.44M
        return Status::OK();
1278
1.44M
    }
1279
18.4E
    VLOG_CRITICAL << _parent->_sender_id << " close wait finished";
1280
67.7k
    return Status::OK();
1281
1.51M
}
1282
1283
Status VNodeChannel::after_close_handle(
1284
        RuntimeState* state, WriterStats* writer_stats,
1285
68.1k
        std::unordered_map<int64_t, AddBatchCounter>* node_add_batch_counter_map) {
1286
68.1k
    Status st = Status::Error<ErrorCode::INTERNAL_ERROR, false>(get_cancel_msg());
1287
68.1k
    _close_time_ms = UnixMillis() - _close_time_ms;
1288
1289
68.1k
    if (_add_batches_finished) {
1290
68.0k
        _close_check();
1291
68.0k
        _state->add_tablet_commit_infos(_tablet_commit_infos);
1292
1293
68.0k
        _index_channel->set_error_tablet_in_state(state);
1294
68.0k
        _index_channel->set_tablets_received_rows(_tablets_received_rows, _node_id);
1295
68.0k
        _index_channel->set_tablets_filtered_rows(_tablets_filtered_rows, _node_id);
1296
1297
68.0k
        std::lock_guard<std::mutex> l(_closed_lock);
1298
        // only when normal close, we set _is_closed to true.
1299
        // otherwise, we will set it to true in cancel().
1300
68.0k
        _is_closed = true;
1301
68.0k
        st = Status::OK();
1302
68.0k
    }
1303
1304
68.1k
    time_report(node_add_batch_counter_map, writer_stats);
1305
68.1k
    return st;
1306
68.1k
}
1307
1308
30.1k
Status VNodeChannel::check_status() {
1309
30.1k
    return none_of({_cancelled, !_eos_is_produced});
1310
30.1k
}
1311
1312
67.9k
void VNodeChannel::_close_check() {
1313
67.9k
    std::lock_guard<std::mutex> lg(_pending_batches_lock);
1314
18.4E
    CHECK(_pending_blocks.empty()) << name();
1315
18.4E
    CHECK(_cur_mutable_block == nullptr) << name();
1316
67.9k
}
1317
1318
68.1k
void VNodeChannel::mark_close(bool hang_wait) {
1319
68.1k
    auto st = none_of({_cancelled, _eos_is_produced});
1320
68.1k
    if (!st.ok()) {
1321
0
        return;
1322
0
    }
1323
1324
68.1k
    _cur_add_block_request->set_eos(true);
1325
68.1k
    _cur_add_block_request->set_hang_wait(hang_wait);
1326
68.1k
    {
1327
68.1k
        std::lock_guard<std::mutex> l(_pending_batches_lock);
1328
68.1k
        if (!_cur_mutable_block) [[unlikely]] {
1329
            // never had a block arrived. add a dummy block
1330
37.9k
            _cur_mutable_block = MutableBlock::create_unique();
1331
37.9k
        }
1332
68.1k
        auto tmp_add_block_request =
1333
68.1k
                std::make_shared<PTabletWriterAddBlockRequest>(*_cur_add_block_request);
1334
        // when prepare to close, add block to queue so that try_send_pending_block thread will send it.
1335
68.1k
        _pending_blocks.emplace(std::move(_cur_mutable_block), tmp_add_block_request);
1336
68.1k
        _pending_batches_num++;
1337
68.1k
        DCHECK(_pending_blocks.back().second->eos());
1338
68.1k
        _close_time_ms = UnixMillis();
1339
68.1k
        LOG(INFO) << channel_info()
1340
68.1k
                  << " mark closed, left pending batch size: " << _pending_blocks.size()
1341
68.1k
                  << " hang_wait: " << hang_wait;
1342
68.1k
    }
1343
1344
68.1k
    _eos_is_produced = true;
1345
68.1k
}
1346
1347
VTabletWriter::VTabletWriter(const TDataSink& t_sink, const VExprContextSPtrs& output_exprs,
1348
                             std::shared_ptr<Dependency> dep, std::shared_ptr<Dependency> fin_dep)
1349
66.2k
        : AsyncResultWriter(output_exprs, dep, fin_dep), _t_sink(t_sink) {
1350
66.2k
    _transfer_large_data_by_brpc = config::transfer_large_data_by_brpc;
1351
66.2k
}
1352
1353
66.6k
void VTabletWriter::_send_batch_process() {
1354
66.6k
    SCOPED_TIMER(_non_blocking_send_timer);
1355
66.6k
    SCOPED_ATTACH_TASK(_state);
1356
66.6k
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker);
1357
1358
66.6k
    int sleep_time = int(config::olap_table_sink_send_interval_microseconds *
1359
66.6k
                         (_vpartition->is_auto_partition()
1360
66.6k
                                  ? config::olap_table_sink_send_interval_auto_partition_factor
1361
66.6k
                                  : 1));
1362
1363
18.7M
    while (true) {
1364
        // incremental open will temporarily make channels into abnormal state. stop checking when this.
1365
18.7M
        std::unique_lock<bthread::Mutex> l(_stop_check_channel);
1366
1367
18.7M
        int running_channels_num = 0;
1368
18.7M
        int opened_nodes = 0;
1369
24.0M
        for (const auto& index_channel : _channels) {
1370
24.0M
            index_channel->for_each_node_channel([&running_channels_num,
1371
24.0M
                                                  this](const std::shared_ptr<VNodeChannel>& ch) {
1372
                // if this channel all completed(cancelled), got 0. else 1.
1373
24.0M
                running_channels_num +=
1374
24.0M
                        ch->try_send_and_fetch_status(_state, this->_send_batch_thread_pool_token);
1375
24.0M
            });
1376
24.0M
            opened_nodes += index_channel->num_node_channels();
1377
24.0M
        }
1378
1379
        // auto partition table may have no node channel temporarily. wait to open.
1380
18.7M
        if (opened_nodes != 0 && running_channels_num == 0) {
1381
66.6k
            LOG(INFO) << "All node channels are stopped(maybe finished/offending/cancelled), "
1382
66.6k
                         "sender thread exit. "
1383
66.6k
                      << print_id(_load_id);
1384
66.6k
            return;
1385
66.6k
        }
1386
1387
        // for auto partition tables, there's a situation: we haven't open any node channel but decide to cancel the task.
1388
        // then the judge in front will never be true because opened_nodes won't increase. so we have to specially check wether we called close.
1389
        // we must RECHECK opened_nodes below, after got closed signal, because it may changed. Think of this:
1390
        //      checked opened_nodes = 0 ---> new block arrived ---> task finished, close() was called ---> we got _try_close here
1391
        // if we don't check again, we may lose the last package.
1392
18.6M
        if (_try_close) {
1393
171k
            opened_nodes = 0;
1394
171k
            std::ranges::for_each(_channels,
1395
1.98M
                                  [&opened_nodes](const std::shared_ptr<IndexChannel>& ich) {
1396
1.98M
                                      opened_nodes += ich->num_node_channels();
1397
1.98M
                                  });
1398
171k
            if (opened_nodes == 0) {
1399
0
                LOG(INFO) << "No node channel have ever opened but now we have to close. sender "
1400
0
                             "thread exit. "
1401
0
                          << print_id(_load_id);
1402
0
                return;
1403
0
            }
1404
171k
        }
1405
18.6M
        bthread_usleep(sleep_time);
1406
18.6M
    }
1407
66.6k
}
1408
1409
66.5k
static void* periodic_send_batch(void* writer) {
1410
66.5k
    auto* tablet_writer = (VTabletWriter*)(writer);
1411
66.5k
    tablet_writer->_send_batch_process();
1412
66.5k
    return nullptr;
1413
66.5k
}
1414
1415
66.6k
Status VTabletWriter::open(doris::RuntimeState* state, doris::RuntimeProfile* profile) {
1416
66.6k
    RETURN_IF_ERROR(_init(state, profile));
1417
66.6k
    signal::set_signal_task_id(_load_id);
1418
66.6k
    SCOPED_TIMER(profile->total_time_counter());
1419
66.6k
    SCOPED_TIMER(_open_timer);
1420
66.6k
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker.get());
1421
1422
66.6k
    fmt::memory_buffer buf;
1423
68.1k
    for (const auto& index_channel : _channels) {
1424
68.1k
        fmt::format_to(buf, "index id:{}", index_channel->_index_id);
1425
68.1k
        index_channel->for_each_node_channel(
1426
68.1k
                [](const std::shared_ptr<VNodeChannel>& ch) { ch->open(); });
1427
68.1k
    }
1428
66.6k
    VLOG_DEBUG << "list of open index id = " << fmt::to_string(buf);
1429
1430
68.1k
    for (const auto& index_channel : _channels) {
1431
68.1k
        index_channel->set_start_time(UnixMillis());
1432
68.1k
        index_channel->for_each_node_channel([&index_channel](
1433
68.1k
                                                     const std::shared_ptr<VNodeChannel>& ch) {
1434
68.1k
            auto st = ch->open_wait();
1435
68.1k
            if (!st.ok()) {
1436
                // The open() phase is mainly to generate DeltaWriter instances on the nodes corresponding to each node channel.
1437
                // This phase will not fail due to a single tablet.
1438
                // Therefore, if the open() phase fails, all tablets corresponding to the node need to be marked as failed.
1439
0
                index_channel->mark_as_failed(
1440
0
                        ch.get(),
1441
0
                        fmt::format("{}, open failed, err: {}", ch->channel_info(), st.to_string()),
1442
0
                        -1);
1443
0
            }
1444
68.1k
        });
1445
1446
68.1k
        RETURN_IF_ERROR(index_channel->check_intolerable_failure());
1447
68.1k
    }
1448
66.6k
    _send_batch_thread_pool_token = state->exec_env()->send_batch_thread_pool()->new_token(
1449
66.6k
            ThreadPool::ExecutionMode::CONCURRENT, _send_batch_parallelism);
1450
1451
    // start to send batch continually. this must be called after _init
1452
66.6k
    if (bthread_start_background(&_sender_thread, nullptr, periodic_send_batch, (void*)this) != 0) {
1453
0
        return Status::Error<ErrorCode::INTERNAL_ERROR>("bthread_start_backgroud failed");
1454
0
    }
1455
66.6k
    return Status::OK();
1456
66.6k
}
1457
1458
179
Status VTabletWriter::on_partitions_created(TCreatePartitionResult* result) {
1459
    // add new tablet locations. it will use by address. so add to pool
1460
179
    auto* new_locations = _pool->add(new std::vector<TTabletLocation>(result->tablets));
1461
179
    _location->add_locations(*new_locations);
1462
179
    if (_write_single_replica) {
1463
0
        auto* slave_locations = _pool->add(new std::vector<TTabletLocation>(result->slave_tablets));
1464
0
        _slave_location->add_locations(*slave_locations);
1465
0
    }
1466
1467
    // update new node info
1468
179
    _nodes_info->add_nodes(result->nodes);
1469
1470
    // incremental open node channel
1471
179
    RETURN_IF_ERROR(_incremental_open_node_channel(result->partitions));
1472
1473
179
    return Status::OK();
1474
179
}
1475
1476
179
static Status on_partitions_created(void* writer, TCreatePartitionResult* result) {
1477
179
    return static_cast<VTabletWriter*>(writer)->on_partitions_created(result);
1478
179
}
1479
1480
66.4k
Status VTabletWriter::_init_row_distribution() {
1481
66.4k
    _row_distribution.init({.state = _state,
1482
66.4k
                            .block_convertor = _block_convertor.get(),
1483
66.4k
                            .tablet_finder = _tablet_finder.get(),
1484
66.4k
                            .vpartition = _vpartition,
1485
66.4k
                            .add_partition_request_timer = _add_partition_request_timer,
1486
66.4k
                            .txn_id = _txn_id,
1487
66.4k
                            .pool = _pool,
1488
66.4k
                            .location = _location,
1489
66.4k
                            .vec_output_expr_ctxs = &_vec_output_expr_ctxs,
1490
66.4k
                            .schema = _schema,
1491
66.4k
                            .caller = this,
1492
66.4k
                            .write_single_replica = _write_single_replica,
1493
66.4k
                            .create_partition_callback = &::doris::on_partitions_created});
1494
1495
66.4k
    return _row_distribution.open(_output_row_desc);
1496
66.4k
}
1497
1498
66.6k
Status VTabletWriter::_init(RuntimeState* state, RuntimeProfile* profile) {
1499
66.6k
    DCHECK(_t_sink.__isset.olap_table_sink);
1500
66.6k
    _pool = state->obj_pool();
1501
66.6k
    auto& table_sink = _t_sink.olap_table_sink;
1502
66.6k
    _load_id.set_hi(table_sink.load_id.hi);
1503
66.6k
    _load_id.set_lo(table_sink.load_id.lo);
1504
66.6k
    _txn_id = table_sink.txn_id;
1505
66.6k
    _num_replicas = table_sink.num_replicas;
1506
66.6k
    _tuple_desc_id = table_sink.tuple_id;
1507
66.6k
    _write_file_cache = table_sink.write_file_cache;
1508
66.6k
    _schema.reset(new OlapTableSchemaParam());
1509
66.6k
    RETURN_IF_ERROR(_schema->init(table_sink.schema));
1510
66.6k
    _schema->set_timestamp_ms(state->timestamp_ms());
1511
66.6k
    _schema->set_nano_seconds(state->nano_seconds());
1512
66.6k
    _schema->set_timezone(state->timezone());
1513
66.6k
    _location = _pool->add(new OlapTableLocationParam(table_sink.location));
1514
66.6k
    _nodes_info = _pool->add(new DorisNodesInfo(table_sink.nodes_info));
1515
66.6k
    if (table_sink.__isset.write_single_replica && table_sink.write_single_replica) {
1516
0
        _write_single_replica = true;
1517
0
        _slave_location = _pool->add(new OlapTableLocationParam(table_sink.slave_location));
1518
0
        if (!config::enable_single_replica_load) {
1519
0
            return Status::InternalError("single replica load is disabled on BE.");
1520
0
        }
1521
0
    }
1522
1523
66.6k
    if (config::is_cloud_mode() &&
1524
66.6k
        (!table_sink.__isset.txn_timeout_s || table_sink.txn_timeout_s <= 0)) {
1525
0
        return Status::InternalError("The txn_timeout_s of TDataSink is invalid");
1526
0
    }
1527
66.6k
    _txn_expiration = ::time(nullptr) + table_sink.txn_timeout_s;
1528
1529
66.6k
    if (table_sink.__isset.load_channel_timeout_s) {
1530
66.3k
        _load_channel_timeout_s = table_sink.load_channel_timeout_s;
1531
66.3k
    } else {
1532
283
        _load_channel_timeout_s = config::streaming_load_rpc_max_alive_time_sec;
1533
283
    }
1534
66.6k
    if (table_sink.__isset.send_batch_parallelism && table_sink.send_batch_parallelism > 1) {
1535
1
        _send_batch_parallelism = table_sink.send_batch_parallelism;
1536
1
    }
1537
    // if distributed column list is empty, we can ensure that tablet is with random distribution info
1538
    // and if load_to_single_tablet is set and set to true, we should find only one tablet in one partition
1539
    // for the whole olap table sink
1540
66.6k
    auto find_tablet_mode = OlapTabletFinder::FindTabletMode::FIND_TABLET_EVERY_ROW;
1541
66.6k
    if (table_sink.partition.distributed_columns.empty()) {
1542
35.3k
        if (table_sink.__isset.load_to_single_tablet && table_sink.load_to_single_tablet) {
1543
8
            find_tablet_mode = OlapTabletFinder::FindTabletMode::FIND_TABLET_EVERY_SINK;
1544
35.3k
        } else {
1545
35.3k
            find_tablet_mode = OlapTabletFinder::FindTabletMode::FIND_TABLET_EVERY_BATCH;
1546
35.3k
        }
1547
35.3k
    }
1548
66.6k
    _vpartition = _pool->add(new doris::VOlapTablePartitionParam(_schema, table_sink.partition));
1549
66.6k
    _tablet_finder = std::make_unique<OlapTabletFinder>(_vpartition, find_tablet_mode);
1550
66.6k
    RETURN_IF_ERROR(_vpartition->init());
1551
1552
66.6k
    _state = state;
1553
66.6k
    _operator_profile = profile;
1554
1555
66.6k
    _sender_id = state->per_fragment_instance_idx();
1556
66.6k
    _num_senders = state->num_per_fragment_instances();
1557
66.6k
    _is_high_priority =
1558
66.6k
            (state->execution_timeout() <= config::load_task_high_priority_threshold_second);
1559
66.6k
    DBUG_EXECUTE_IF("VTabletWriter._init.is_high_priority", { _is_high_priority = true; });
1560
    // profile must add to state's object pool
1561
66.6k
    _mem_tracker =
1562
66.6k
            std::make_shared<MemTracker>("OlapTableSink:" + std::to_string(state->load_job_id()));
1563
66.6k
    SCOPED_TIMER(profile->total_time_counter());
1564
66.6k
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker.get());
1565
1566
    // get table's tuple descriptor
1567
66.6k
    _output_tuple_desc = state->desc_tbl().get_tuple_descriptor(_tuple_desc_id);
1568
66.6k
    if (_output_tuple_desc == nullptr) {
1569
0
        LOG(WARNING) << "unknown destination tuple descriptor, id=" << _tuple_desc_id;
1570
0
        return Status::InternalError("unknown destination tuple descriptor");
1571
0
    }
1572
1573
66.6k
    if (!_vec_output_expr_ctxs.empty() &&
1574
66.6k
        _output_tuple_desc->slots().size() != _vec_output_expr_ctxs.size()) {
1575
0
        LOG(WARNING) << "output tuple slot num should be equal to num of output exprs, "
1576
0
                     << "output_tuple_slot_num " << _output_tuple_desc->slots().size()
1577
0
                     << " output_expr_num " << _vec_output_expr_ctxs.size();
1578
0
        return Status::InvalidArgument(
1579
0
                "output_tuple_slot_num {} should be equal to output_expr_num {}",
1580
0
                _output_tuple_desc->slots().size(), _vec_output_expr_ctxs.size());
1581
0
    }
1582
1583
66.6k
    _block_convertor = std::make_unique<OlapTableBlockConvertor>(_output_tuple_desc);
1584
    // if partition_type is OLAP_TABLE_SINK_HASH_PARTITIONED, we handle the processing of auto_increment column
1585
    // on exchange node rather than on TabletWriter
1586
66.6k
    _block_convertor->init_autoinc_info(
1587
66.6k
            _schema->db_id(), _schema->table_id(), _state->batch_size(),
1588
66.6k
            _schema->is_fixed_partial_update() && !_schema->auto_increment_coulumn().empty(),
1589
66.6k
            _schema->auto_increment_column_unique_id());
1590
66.6k
    _output_row_desc = _pool->add(new RowDescriptor(_output_tuple_desc));
1591
1592
    // add all counter
1593
66.6k
    _input_rows_counter = ADD_COUNTER(profile, "RowsRead", TUnit::UNIT);
1594
66.6k
    _output_rows_counter = ADD_COUNTER(profile, "RowsProduced", TUnit::UNIT);
1595
66.6k
    _filtered_rows_counter = ADD_COUNTER(profile, "RowsFiltered", TUnit::UNIT);
1596
66.6k
    _send_data_timer = ADD_TIMER(profile, "SendDataTime");
1597
66.6k
    _wait_mem_limit_timer = ADD_CHILD_TIMER(profile, "WaitMemLimitTime", "SendDataTime");
1598
66.6k
    _row_distribution_timer = ADD_CHILD_TIMER(profile, "RowDistributionTime", "SendDataTime");
1599
66.6k
    _filter_timer = ADD_CHILD_TIMER(profile, "FilterTime", "SendDataTime");
1600
66.6k
    _where_clause_timer = ADD_CHILD_TIMER(profile, "WhereClauseTime", "SendDataTime");
1601
66.6k
    _append_node_channel_timer = ADD_CHILD_TIMER(profile, "AppendNodeChannelTime", "SendDataTime");
1602
66.6k
    _add_partition_request_timer =
1603
66.6k
            ADD_CHILD_TIMER(profile, "AddPartitionRequestTime", "SendDataTime");
1604
66.6k
    _validate_data_timer = ADD_TIMER(profile, "ValidateDataTime");
1605
66.6k
    _open_timer = ADD_TIMER(profile, "OpenTime");
1606
66.6k
    _close_timer = ADD_TIMER(profile, "CloseWaitTime");
1607
66.6k
    _non_blocking_send_timer = ADD_TIMER(profile, "NonBlockingSendTime");
1608
66.6k
    _non_blocking_send_work_timer =
1609
66.6k
            ADD_CHILD_TIMER(profile, "NonBlockingSendWorkTime", "NonBlockingSendTime");
1610
66.6k
    _serialize_batch_timer =
1611
66.6k
            ADD_CHILD_TIMER(profile, "SerializeBatchTime", "NonBlockingSendWorkTime");
1612
66.6k
    _total_add_batch_exec_timer = ADD_TIMER(profile, "TotalAddBatchExecTime");
1613
66.6k
    _max_add_batch_exec_timer = ADD_TIMER(profile, "MaxAddBatchExecTime");
1614
66.6k
    _total_wait_exec_timer = ADD_TIMER(profile, "TotalWaitExecTime");
1615
66.6k
    _max_wait_exec_timer = ADD_TIMER(profile, "MaxWaitExecTime");
1616
66.6k
    _add_batch_number = ADD_COUNTER(profile, "NumberBatchAdded", TUnit::UNIT);
1617
66.6k
    _num_node_channels = ADD_COUNTER(profile, "NumberNodeChannels", TUnit::UNIT);
1618
66.6k
    _load_back_pressure_version_time_ms = ADD_TIMER(profile, "LoadBackPressureVersionTimeMs");
1619
1620
#ifdef DEBUG
1621
    // check: tablet ids should be unique
1622
    {
1623
        std::unordered_set<int64_t> tablet_ids;
1624
        const auto& partitions = _vpartition->get_partitions();
1625
        for (int i = 0; i < _schema->indexes().size(); ++i) {
1626
            for (const auto& partition : partitions) {
1627
                for (const auto& tablet : partition->indexes[i].tablets) {
1628
                    CHECK(tablet_ids.count(tablet) == 0) << "found duplicate tablet id: " << tablet;
1629
                    tablet_ids.insert(tablet);
1630
                }
1631
            }
1632
        }
1633
    }
1634
#endif
1635
1636
    // open all channels
1637
66.6k
    const auto& partitions = _vpartition->get_partitions();
1638
134k
    for (int i = 0; i < _schema->indexes().size(); ++i) {
1639
        // collect all tablets belong to this rollup
1640
67.5k
        std::vector<TTabletWithPartition> tablets;
1641
67.5k
        auto* index = _schema->indexes()[i];
1642
81.5k
        for (const auto& part : partitions) {
1643
393k
            for (const auto& tablet : part->indexes[i].tablets) {
1644
393k
                TTabletWithPartition tablet_with_partition;
1645
393k
                tablet_with_partition.partition_id = part->id;
1646
393k
                tablet_with_partition.tablet_id = tablet;
1647
393k
                tablets.emplace_back(std::move(tablet_with_partition));
1648
393k
                _build_tablet_replica_info(tablet, part);
1649
393k
            }
1650
81.5k
        }
1651
67.5k
        if (tablets.empty() && !_vpartition->is_auto_partition()) {
1652
0
            LOG(WARNING) << "load job:" << state->load_job_id() << " index: " << index->index_id
1653
0
                         << " would open 0 tablet";
1654
0
        }
1655
67.5k
        _channels.emplace_back(new IndexChannel(this, index->index_id, index->where_clause));
1656
67.5k
        _index_id_to_channel[index->index_id] = _channels.back();
1657
67.5k
        RETURN_IF_ERROR(_channels.back()->init(state, tablets));
1658
67.5k
    }
1659
1660
66.6k
    RETURN_IF_ERROR(_init_row_distribution());
1661
1662
66.6k
    _inited = true;
1663
66.6k
    return Status::OK();
1664
66.6k
}
1665
1666
Status VTabletWriter::_incremental_open_node_channel(
1667
179
        const std::vector<TOlapTablePartition>& partitions) {
1668
    // do what we did in prepare() for partitions. indexes which don't change when we create new partition is orthogonal to partitions.
1669
179
    std::unique_lock<bthread::Mutex> _l(_stop_check_channel);
1670
358
    for (int i = 0; i < _schema->indexes().size(); ++i) {
1671
179
        const OlapTableIndexSchema* index = _schema->indexes()[i];
1672
179
        std::vector<TTabletWithPartition> tablets;
1673
357
        for (const auto& t_part : partitions) {
1674
357
            VOlapTablePartition* part = nullptr;
1675
357
            RETURN_IF_ERROR(_vpartition->generate_partition_from(t_part, part));
1676
8.82k
            for (const auto& tablet : part->indexes[i].tablets) {
1677
8.82k
                TTabletWithPartition tablet_with_partition;
1678
8.82k
                tablet_with_partition.partition_id = part->id;
1679
8.82k
                tablet_with_partition.tablet_id = tablet;
1680
8.82k
                tablets.emplace_back(std::move(tablet_with_partition));
1681
8.82k
                _build_tablet_replica_info(tablet, part);
1682
8.82k
            }
1683
357
            DCHECK(!tablets.empty()) << "incremental open got nothing!";
1684
357
        }
1685
        // update and reinit for existing channels.
1686
179
        std::shared_ptr<IndexChannel> channel = _index_id_to_channel[index->index_id];
1687
179
        DCHECK(channel != nullptr);
1688
179
        RETURN_IF_ERROR(channel->init(_state, tablets, true)); // add tablets into it
1689
179
    }
1690
1691
179
    fmt::memory_buffer buf;
1692
179
    for (auto& channel : _channels) {
1693
        // incremental open new partition's tablet on storage side
1694
179
        channel->for_each_node_channel(
1695
179
                [](const std::shared_ptr<VNodeChannel>& ch) { ch->incremental_open(); });
1696
179
        fmt::format_to(buf, "index id:{}", channel->_index_id);
1697
179
        VLOG_DEBUG << "list of open index id = " << fmt::to_string(buf);
1698
1699
179
        channel->for_each_node_channel([&channel](const std::shared_ptr<VNodeChannel>& ch) {
1700
179
            auto st = ch->open_wait();
1701
179
            if (!st.ok()) {
1702
                // The open() phase is mainly to generate DeltaWriter instances on the nodes corresponding to each node channel.
1703
                // This phase will not fail due to a single tablet.
1704
                // Therefore, if the open() phase fails, all tablets corresponding to the node need to be marked as failed.
1705
0
                channel->mark_as_failed(
1706
0
                        ch.get(),
1707
0
                        fmt::format("{}, open failed, err: {}", ch->channel_info(), st.to_string()),
1708
0
                        -1);
1709
0
            }
1710
179
        });
1711
1712
179
        RETURN_IF_ERROR(channel->check_intolerable_failure());
1713
179
    }
1714
1715
179
    return Status::OK();
1716
179
}
1717
1718
void VTabletWriter::_build_tablet_replica_info(const int64_t tablet_id,
1719
401k
                                               VOlapTablePartition* partition) {
1720
402k
    if (partition != nullptr) {
1721
402k
        int total_replicas_num =
1722
402k
                partition->total_replica_num == 0 ? _num_replicas : partition->total_replica_num;
1723
402k
        int load_required_replicas_num = partition->load_required_replica_num == 0
1724
402k
                                                 ? (_num_replicas + 1) / 2
1725
402k
                                                 : partition->load_required_replica_num;
1726
402k
        _tablet_replica_info.emplace(
1727
402k
                tablet_id, std::make_pair(total_replicas_num, load_required_replicas_num));
1728
18.4E
    } else {
1729
18.4E
        _tablet_replica_info.emplace(tablet_id,
1730
18.4E
                                     std::make_pair(_num_replicas, (_num_replicas + 1) / 2));
1731
18.4E
    }
1732
401k
}
1733
1734
110
void VTabletWriter::_cancel_all_channel(Status status) {
1735
110
    for (const auto& index_channel : _channels) {
1736
110
        index_channel->for_each_node_channel([&status](const std::shared_ptr<VNodeChannel>& ch) {
1737
110
            ch->cancel(status.to_string());
1738
110
        });
1739
110
    }
1740
110
    LOG(INFO) << fmt::format(
1741
110
            "close olap table sink. load_id={}, txn_id={}, canceled all node channels due to "
1742
110
            "error: {}",
1743
110
            print_id(_load_id), _txn_id, status);
1744
110
}
1745
1746
102k
Status VTabletWriter::_send_new_partition_batch() {
1747
102k
    if (_row_distribution.need_deal_batching()) { // maybe try_close more than 1 time
1748
160
        RETURN_IF_ERROR(_row_distribution.automatic_create_partition());
1749
1750
160
        Block tmp_block = _row_distribution._batching_block->to_block(); // Borrow out, for lval ref
1751
1752
        // these order is unique.
1753
        //  1. clear batching stats(and flag goes true) so that we won't make a new batching process in dealing batched block.
1754
        //  2. deal batched block
1755
        //  3. now reuse the column of lval block. cuz write doesn't real adjust it. it generate a new block from that.
1756
160
        _row_distribution.clear_batching_stats();
1757
160
        RETURN_IF_ERROR(this->write(_state, tmp_block));
1758
160
        _row_distribution._batching_block->set_mutable_columns(
1759
160
                tmp_block.mutate_columns()); // Recovery back
1760
160
        _row_distribution._batching_block->clear_column_data();
1761
160
        _row_distribution._deal_batched = false;
1762
160
    }
1763
102k
    return Status::OK();
1764
102k
}
1765
1766
66.5k
void VTabletWriter::_do_try_close(RuntimeState* state, const Status& exec_status) {
1767
66.5k
    SCOPED_TIMER(_close_timer);
1768
66.5k
    Status status = exec_status;
1769
1770
    // must before set _try_close
1771
66.5k
    if (status.ok()) {
1772
66.5k
        SCOPED_TIMER(_operator_profile->total_time_counter());
1773
66.5k
        _row_distribution._deal_batched = true;
1774
66.5k
        status = _send_new_partition_batch();
1775
66.5k
    }
1776
1777
66.5k
    _try_close = true; // will stop periodic thread
1778
66.5k
    if (status.ok()) {
1779
        // BE id -> add_batch method counter
1780
66.5k
        std::unordered_map<int64_t, AddBatchCounter> node_add_batch_counter_map;
1781
1782
        // only if status is ok can we call this _profile->total_time_counter().
1783
        // if status is not ok, this sink may not be prepared, so that _profile is null
1784
66.5k
        SCOPED_TIMER(_operator_profile->total_time_counter());
1785
68.1k
        for (const auto& index_channel : _channels) {
1786
            // two-step mark close. first we send close_origin to recievers to close all originly exist TabletsChannel.
1787
            // when they all closed, we are sure all Writer of instances called _do_try_close. that means no new channel
1788
            // will be opened. the refcount of recievers will be monotonically decreasing. then we are safe to close all
1789
            // our channels.
1790
68.1k
            if (index_channel->has_incremental_node_channel()) {
1791
0
                if (!status.ok()) {
1792
0
                    break;
1793
0
                }
1794
0
                VLOG_TRACE << _sender_id << " first stage close start " << _txn_id;
1795
0
                index_channel->for_init_node_channel(
1796
0
                        [&index_channel, &status, this](const std::shared_ptr<VNodeChannel>& ch) {
1797
0
                            if (!status.ok() || ch->is_closed()) {
1798
0
                                return;
1799
0
                            }
1800
0
                            VLOG_DEBUG << index_channel->_parent->_sender_id << "'s " << ch->host()
1801
0
                                       << "mark close1 for inits " << _txn_id;
1802
0
                            ch->mark_close(true);
1803
0
                            if (ch->is_cancelled()) {
1804
0
                                status = cancel_channel_and_check_intolerable_failure(
1805
0
                                        std::move(status), ch->get_cancel_msg(), *index_channel,
1806
0
                                        *ch);
1807
0
                            }
1808
0
                        });
1809
0
                if (!status.ok()) {
1810
0
                    break;
1811
0
                }
1812
                // Do not need to wait after quorum success,
1813
                // for first-stage close_wait only ensure incremental node channels load has been completed,
1814
                // unified waiting in the second-stage close_wait.
1815
0
                status = index_channel->close_wait(_state, nullptr, nullptr,
1816
0
                                                   index_channel->init_node_channel_ids(), false);
1817
0
                if (!status.ok()) {
1818
0
                    break;
1819
0
                }
1820
0
                VLOG_DEBUG << _sender_id << " first stage finished. closeing inc nodes " << _txn_id;
1821
0
                index_channel->for_inc_node_channel(
1822
0
                        [&index_channel, &status, this](const std::shared_ptr<VNodeChannel>& ch) {
1823
0
                            if (!status.ok() || ch->is_closed()) {
1824
0
                                return;
1825
0
                            }
1826
                            // only first try close, all node channels will mark_close()
1827
0
                            VLOG_DEBUG << index_channel->_parent->_sender_id << "'s " << ch->host()
1828
0
                                       << "mark close2 for inc " << _txn_id;
1829
0
                            ch->mark_close();
1830
0
                            if (ch->is_cancelled()) {
1831
0
                                status = cancel_channel_and_check_intolerable_failure(
1832
0
                                        std::move(status), ch->get_cancel_msg(), *index_channel,
1833
0
                                        *ch);
1834
0
                            }
1835
0
                        });
1836
68.1k
            } else { // not has_incremental_node_channel
1837
68.1k
                VLOG_TRACE << _sender_id << " has no incremental channels " << _txn_id;
1838
68.1k
                index_channel->for_each_node_channel(
1839
68.1k
                        [&index_channel, &status](const std::shared_ptr<VNodeChannel>& ch) {
1840
68.1k
                            if (!status.ok() || ch->is_closed()) {
1841
0
                                return;
1842
0
                            }
1843
                            // only first try close, all node channels will mark_close()
1844
68.1k
                            ch->mark_close();
1845
68.1k
                            if (ch->is_cancelled()) {
1846
0
                                status = cancel_channel_and_check_intolerable_failure(
1847
0
                                        std::move(status), ch->get_cancel_msg(), *index_channel,
1848
0
                                        *ch);
1849
0
                            }
1850
68.1k
                        });
1851
68.1k
            }
1852
68.1k
        } // end for index channels
1853
66.5k
    }
1854
1855
66.5k
    if (!status.ok()) {
1856
62
        _cancel_all_channel(status);
1857
62
        _close_status = status;
1858
62
        _close_wait = true;
1859
62
    }
1860
66.5k
}
1861
1862
66.6k
Status VTabletWriter::close(Status exec_status) {
1863
66.6k
    if (!_inited) {
1864
0
        DCHECK(!exec_status.ok());
1865
0
        _cancel_all_channel(exec_status);
1866
0
        _close_status = exec_status;
1867
0
        return _close_status;
1868
0
    }
1869
1870
66.6k
    SCOPED_TIMER(_close_timer);
1871
66.6k
    SCOPED_TIMER(_operator_profile->total_time_counter());
1872
1873
    // will make the last batch of request-> close_wait will wait this finished.
1874
66.6k
    _do_try_close(_state, exec_status);
1875
66.6k
    TEST_INJECTION_POINT("VOlapTableSink::close");
1876
1877
66.6k
    DBUG_EXECUTE_IF("VTabletWriter.close.sleep", {
1878
66.6k
        auto sleep_sec = DebugPoints::instance()->get_debug_param_or_default<int32_t>(
1879
66.6k
                "VTabletWriter.close.sleep", "sleep_sec", 1);
1880
66.6k
        std::this_thread::sleep_for(std::chrono::seconds(sleep_sec));
1881
66.6k
    });
1882
66.6k
    DBUG_EXECUTE_IF("VTabletWriter.close.close_status_not_ok",
1883
66.6k
                    { _close_status = Status::InternalError("injected close status not ok"); });
1884
1885
    // If _close_status is not ok, all nodes have been canceled in try_close.
1886
66.6k
    if (_close_status.ok()) {
1887
66.5k
        auto status = Status::OK();
1888
        // BE id -> add_batch method counter
1889
66.5k
        std::unordered_map<int64_t, AddBatchCounter> node_add_batch_counter_map;
1890
66.5k
        WriterStats writer_stats;
1891
1892
68.1k
        for (const auto& index_channel : _channels) {
1893
68.1k
            if (!status.ok()) {
1894
0
                break;
1895
0
            }
1896
68.1k
            int64_t add_batch_exec_time = 0;
1897
68.1k
            int64_t wait_exec_time = 0;
1898
68.1k
            status = index_channel->close_wait(_state, &writer_stats, &node_add_batch_counter_map,
1899
68.1k
                                               index_channel->each_node_channel_ids(), true);
1900
1901
            // Due to the non-determinism of compaction, the rowsets of each replica may be different from each other on different
1902
            // BE nodes. The number of rows filtered in SegmentWriter depends on the historical rowsets located in the correspoding
1903
            // BE node. So we check the number of rows filtered on each succeccful BE to ensure the consistency of the current load
1904
68.1k
            if (status.ok() && !_write_single_replica && _schema->is_strict_mode() &&
1905
68.1k
                _schema->is_partial_update()) {
1906
59
                if (Status st = index_channel->check_tablet_filtered_rows_consistency(); !st.ok()) {
1907
0
                    status = st;
1908
59
                } else {
1909
59
                    _state->set_num_rows_filtered_in_strict_mode_partial_update(
1910
59
                            index_channel->num_rows_filtered());
1911
59
                }
1912
59
            }
1913
1914
68.1k
            writer_stats.num_node_channels += index_channel->num_node_channels();
1915
68.1k
            writer_stats.max_add_batch_exec_time_ns =
1916
68.1k
                    std::max(add_batch_exec_time, writer_stats.max_add_batch_exec_time_ns);
1917
68.1k
            writer_stats.max_wait_exec_time_ns =
1918
68.1k
                    std::max(wait_exec_time, writer_stats.max_wait_exec_time_ns);
1919
68.1k
        } // end for index channels
1920
1921
66.5k
        if (status.ok()) {
1922
            // TODO need to be improved
1923
66.4k
            LOG(INFO) << "total mem_exceeded_block_ns="
1924
66.4k
                      << writer_stats.channel_stat.mem_exceeded_block_ns
1925
66.4k
                      << ", total queue_push_lock_ns=" << writer_stats.queue_push_lock_ns
1926
66.4k
                      << ", total actual_consume_ns=" << writer_stats.actual_consume_ns
1927
66.4k
                      << ", load id=" << print_id(_load_id) << ", txn_id=" << _txn_id;
1928
1929
66.4k
            COUNTER_SET(_input_rows_counter, _number_input_rows);
1930
66.4k
            COUNTER_SET(_output_rows_counter, _number_output_rows);
1931
66.4k
            COUNTER_SET(_filtered_rows_counter,
1932
66.4k
                        _block_convertor->num_filtered_rows() +
1933
66.4k
                                _tablet_finder->num_filtered_rows() +
1934
66.4k
                                _state->num_rows_filtered_in_strict_mode_partial_update());
1935
66.4k
            COUNTER_SET(_send_data_timer, _send_data_ns);
1936
66.4k
            COUNTER_SET(_row_distribution_timer, (int64_t)_row_distribution_watch.elapsed_time());
1937
66.4k
            COUNTER_SET(_filter_timer, _filter_ns);
1938
66.4k
            COUNTER_SET(_append_node_channel_timer,
1939
66.4k
                        writer_stats.channel_stat.append_node_channel_ns);
1940
66.4k
            COUNTER_SET(_where_clause_timer, writer_stats.channel_stat.where_clause_ns);
1941
66.4k
            COUNTER_SET(_wait_mem_limit_timer, writer_stats.channel_stat.mem_exceeded_block_ns);
1942
66.4k
            COUNTER_SET(_validate_data_timer, _block_convertor->validate_data_ns());
1943
66.4k
            COUNTER_SET(_serialize_batch_timer, writer_stats.serialize_batch_ns);
1944
66.4k
            COUNTER_SET(_non_blocking_send_work_timer, writer_stats.actual_consume_ns);
1945
66.4k
            COUNTER_SET(_total_add_batch_exec_timer, writer_stats.total_add_batch_exec_time_ns);
1946
66.4k
            COUNTER_SET(_max_add_batch_exec_timer, writer_stats.max_add_batch_exec_time_ns);
1947
66.4k
            COUNTER_SET(_total_wait_exec_timer, writer_stats.total_wait_exec_time_ns);
1948
66.4k
            COUNTER_SET(_max_wait_exec_timer, writer_stats.max_wait_exec_time_ns);
1949
66.4k
            COUNTER_SET(_add_batch_number, writer_stats.total_add_batch_num);
1950
66.4k
            COUNTER_SET(_num_node_channels, writer_stats.num_node_channels);
1951
66.4k
            COUNTER_SET(_load_back_pressure_version_time_ms,
1952
66.4k
                        writer_stats.load_back_pressure_version_time_ms);
1953
66.4k
            g_sink_load_back_pressure_version_time_ms
1954
66.4k
                    << writer_stats.load_back_pressure_version_time_ms;
1955
1956
            // _number_input_rows don't contain num_rows_load_filtered and num_rows_load_unselected in scan node
1957
66.4k
            int64_t num_rows_load_total = _number_input_rows + _state->num_rows_load_filtered() +
1958
66.4k
                                          _state->num_rows_load_unselected();
1959
66.4k
            _state->set_num_rows_load_total(num_rows_load_total);
1960
66.4k
            _state->update_num_rows_load_filtered(
1961
66.4k
                    _block_convertor->num_filtered_rows() + _tablet_finder->num_filtered_rows() +
1962
66.4k
                    _state->num_rows_filtered_in_strict_mode_partial_update());
1963
66.4k
            _state->update_num_rows_load_unselected(
1964
66.4k
                    _tablet_finder->num_immutable_partition_filtered_rows());
1965
1966
66.4k
            if (_state->enable_profile() && _state->profile_level() >= 2) {
1967
                // Output detailed profiling info for auto-partition requests
1968
7
                _row_distribution.output_profile_info(_operator_profile);
1969
7
            }
1970
1971
            // print log of add batch time of all node, for tracing load performance easily
1972
66.4k
            std::stringstream ss;
1973
66.4k
            ss << "finished to close olap table sink. load_id=" << print_id(_load_id)
1974
66.4k
               << ", txn_id=" << _txn_id
1975
66.4k
               << ", node add batch time(ms)/wait execution time(ms)/close time(ms)/num: ";
1976
66.4k
            for (auto const& pair : node_add_batch_counter_map) {
1977
66.4k
                ss << "{" << pair.first << ":(" << (pair.second.add_batch_execution_time_us / 1000)
1978
66.4k
                   << ")(" << (pair.second.add_batch_wait_execution_time_us / 1000) << ")("
1979
66.4k
                   << pair.second.close_wait_time_ms << ")(" << pair.second.add_batch_num << ")} ";
1980
66.4k
            }
1981
66.4k
            LOG(INFO) << ss.str();
1982
66.4k
        } else {
1983
49
            _cancel_all_channel(status);
1984
49
        }
1985
66.5k
        _close_status = status;
1986
66.5k
    }
1987
1988
    // Sender join() must put after node channels mark_close/cancel.
1989
    // But there is no specific sequence required between sender join() & close_wait().
1990
66.6k
    if (_sender_thread) {
1991
66.5k
        bthread_join(_sender_thread, nullptr);
1992
        // We have to wait all task in _send_batch_thread_pool_token finished,
1993
        // because it is difficult to handle concurrent problem if we just
1994
        // shutdown it.
1995
66.5k
        _send_batch_thread_pool_token->wait();
1996
66.5k
    }
1997
1998
    // We clear NodeChannels' batches here, cuz NodeChannels' batches destruction will use
1999
    // OlapTableSink::_mem_tracker and its parents.
2000
    // But their destructions are after OlapTableSink's.
2001
68.2k
    for (const auto& index_channel : _channels) {
2002
68.2k
        index_channel->for_each_node_channel(
2003
68.2k
                [](const std::shared_ptr<VNodeChannel>& ch) { ch->clear_all_blocks(); });
2004
68.2k
    }
2005
66.6k
    return _close_status;
2006
66.6k
}
2007
2008
void VTabletWriter::_generate_one_index_channel_payload(
2009
        RowPartTabletIds& row_part_tablet_id, int32_t index_idx,
2010
37.3k
        ChannelDistributionPayload& channel_payload) {
2011
37.3k
    auto& row_ids = row_part_tablet_id.row_ids;
2012
37.3k
    auto& tablet_ids = row_part_tablet_id.tablet_ids;
2013
2014
37.3k
    size_t row_cnt = row_ids.size();
2015
2016
35.6M
    for (size_t i = 0; i < row_ids.size(); i++) {
2017
        // (tablet_id, VNodeChannel) where this tablet locate
2018
35.6M
        auto it = _channels[index_idx]->_channels_by_tablet.find(tablet_ids[i]);
2019
18.4E
        DCHECK(it != _channels[index_idx]->_channels_by_tablet.end())
2020
18.4E
                << "unknown tablet, tablet_id=" << tablet_ids[i];
2021
2022
35.6M
        std::vector<std::shared_ptr<VNodeChannel>>& tablet_locations = it->second;
2023
35.7M
        for (const auto& locate_node : tablet_locations) {
2024
35.7M
            auto payload_it = channel_payload.find(locate_node.get()); // <VNodeChannel*, Payload>
2025
35.7M
            if (payload_it == channel_payload.end()) {
2026
37.0k
                auto [tmp_it, _] = channel_payload.emplace(
2027
37.0k
                        locate_node.get(),
2028
37.0k
                        Payload {std::make_unique<IColumn::Selector>(), std::vector<int64_t>()});
2029
37.0k
                payload_it = tmp_it;
2030
37.0k
                payload_it->second.first->reserve(row_cnt);
2031
37.0k
                payload_it->second.second.reserve(row_cnt);
2032
37.0k
            }
2033
35.7M
            payload_it->second.first->push_back(row_ids[i]);
2034
35.7M
            payload_it->second.second.push_back(tablet_ids[i]);
2035
35.7M
        }
2036
35.6M
    }
2037
37.3k
}
2038
2039
void VTabletWriter::_generate_index_channels_payloads(
2040
        std::vector<RowPartTabletIds>& row_part_tablet_ids,
2041
36.3k
        ChannelDistributionPayloadVec& payload) {
2042
73.7k
    for (int i = 0; i < _schema->indexes().size(); i++) {
2043
37.3k
        _generate_one_index_channel_payload(row_part_tablet_ids[i], i, payload[i]);
2044
37.3k
    }
2045
36.3k
}
2046
2047
36.3k
Status VTabletWriter::write(RuntimeState* state, doris::Block& input_block) {
2048
36.3k
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker.get());
2049
36.3k
    Status status = Status::OK();
2050
2051
36.3k
    DCHECK(_state);
2052
36.3k
    DCHECK(_state->query_options().__isset.dry_run_query);
2053
36.3k
    if (_state->query_options().dry_run_query) {
2054
1
        return status;
2055
1
    }
2056
2057
    // check out of limit
2058
36.3k
    RETURN_IF_ERROR(_send_new_partition_batch());
2059
2060
36.3k
    auto rows = input_block.rows();
2061
36.3k
    auto bytes = input_block.bytes();
2062
36.3k
    if (UNLIKELY(rows == 0)) {
2063
0
        return status;
2064
0
    }
2065
36.3k
    SCOPED_TIMER(_operator_profile->total_time_counter());
2066
36.3k
    SCOPED_RAW_TIMER(&_send_data_ns);
2067
2068
36.3k
    std::shared_ptr<Block> block;
2069
36.3k
    _number_input_rows += rows;
2070
    // update incrementally so that FE can get the progress.
2071
    // the real 'num_rows_load_total' will be set when sink being closed.
2072
36.3k
    _state->update_num_rows_load_total(rows);
2073
36.3k
    _state->update_num_bytes_load_total(bytes);
2074
36.3k
    DorisMetrics::instance()->load_rows->increment(rows);
2075
36.3k
    DorisMetrics::instance()->load_bytes->increment(bytes);
2076
2077
36.3k
    _row_distribution_watch.start();
2078
36.3k
    RETURN_IF_ERROR(_row_distribution.generate_rows_distribution(
2079
36.3k
            input_block, block, _row_part_tablet_ids, _number_input_rows));
2080
2081
36.3k
    ChannelDistributionPayloadVec channel_to_payload;
2082
2083
36.3k
    channel_to_payload.resize(_channels.size());
2084
36.3k
    _generate_index_channels_payloads(_row_part_tablet_ids, channel_to_payload);
2085
36.3k
    _row_distribution_watch.stop();
2086
2087
    // Add block to node channel
2088
73.7k
    for (size_t i = 0; i < _channels.size(); i++) {
2089
37.3k
        for (const auto& entry : channel_to_payload[i]) {
2090
            // if this node channel is already failed, this add_row will be skipped
2091
            // entry.second is a [row -> tablet] mapping
2092
37.0k
            auto st = entry.first->add_block(block.get(), &entry.second);
2093
37.0k
            if (!st.ok()) {
2094
0
                _channels[i]->mark_as_failed(entry.first, st.to_string());
2095
0
            }
2096
37.0k
        }
2097
37.3k
    }
2098
2099
    // check intolerable failure
2100
37.3k
    for (const auto& index_channel : _channels) {
2101
37.3k
        RETURN_IF_ERROR(index_channel->check_intolerable_failure());
2102
37.3k
    }
2103
2104
36.3k
    g_sink_write_bytes << bytes;
2105
36.3k
    g_sink_write_rows << rows;
2106
36.3k
    return Status::OK();
2107
36.3k
}
2108
2109
} // namespace doris