Coverage Report

Created: 2026-07-07 22:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/exchange/exchange_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/exchange/exchange_writer.h"
19
20
#include <glog/logging.h>
21
22
#include <algorithm>
23
#include <cstdint>
24
#include <vector>
25
26
#include "common/logging.h"
27
#include "common/status.h"
28
#include "core/assert_cast.h"
29
#include "core/block/block.h"
30
#include "exec/operator/exchange_sink_operator.h"
31
#include "exec/sink/tablet_sink_hash_partitioner.h"
32
33
namespace doris {
34
35
ExchangeWriterBase::ExchangeWriterBase(ExchangeSinkLocalState& local_state)
36
28
        : _local_state(local_state), _partitioner(local_state.partitioner()) {}
37
38
template <typename ChannelPtrType>
39
Status ExchangeWriterBase::_handle_eof_channel(RuntimeState* state, ChannelPtrType channel,
40
0
                                               Status st) const {
41
0
    channel->set_receiver_eof(st);
42
    // Channel will not send RPC to the downstream when eof, so close channel by OK status.
43
0
    return channel->close(state);
44
0
}
45
46
// NOLINTBEGIN(readability-function-cognitive-complexity)
47
Status ExchangeWriterBase::_add_rows_impl(RuntimeState* state,
48
                                          std::vector<std::shared_ptr<Channel>>& channels,
49
14
                                          size_t channel_count, Block* block, bool eos) {
50
14
    Status status = Status::OK();
51
14
    uint32_t offset = 0;
52
52
    for (size_t i = 0; i < channel_count; ++i) {
53
38
        uint32_t size = _channel_rows_histogram[i];
54
38
        if (!channels[i]->is_receiver_eof() && size > 0) {
55
0
            VLOG_DEBUG << fmt::format("partition {} of {}, block:\n{}, start: {}, size: {}", i,
56
0
                                      channel_count, block->dump_data(), offset, size);
57
0
            status = channels[i]->add_rows(block, _origin_row_idx.data(), offset, size, false);
58
0
            HANDLE_CHANNEL_STATUS(state, channels[i], status);
59
0
        }
60
38
        offset += size;
61
38
    }
62
14
    if (eos) {
63
0
        for (int i = 0; i < channel_count; ++i) {
64
0
            if (!channels[i]->is_receiver_eof()) {
65
0
                VLOG_DEBUG << fmt::format("EOS partition {} of {}, block:\n{}", i, channel_count,
66
0
                                          block->dump_data());
67
0
                status = channels[i]->add_rows(block, _origin_row_idx.data(), 0, 0, true);
68
0
                HANDLE_CHANNEL_STATUS(state, channels[i], status);
69
0
            }
70
0
        }
71
0
    }
72
14
    return Status::OK();
73
14
}
74
// NOLINTEND(readability-function-cognitive-complexity)
75
76
0
Status ExchangeOlapWriter::write(RuntimeState* state, Block* block, bool eos) {
77
0
    Block prior_block;
78
0
    auto* tablet_partitioner = assert_cast<TabletSinkHashPartitioner*>(_partitioner);
79
0
    RETURN_IF_ERROR(tablet_partitioner->try_cut_in_line(prior_block));
80
0
    if (!prior_block.empty()) {
81
        // prior_block (batching rows) cuts in line, deal it first.
82
0
        RETURN_IF_ERROR(_write_impl(state, &prior_block));
83
0
        tablet_partitioner->finish_cut_in_line();
84
0
    }
85
86
0
    RETURN_IF_ERROR(_write_impl(state, block));
87
88
    // all data wrote. consider batched rows before eos.
89
0
    if (eos) {
90
        // get all batched rows
91
0
        tablet_partitioner->mark_last_block();
92
0
        Block final_batching_block;
93
0
        RETURN_IF_ERROR(tablet_partitioner->try_cut_in_line(final_batching_block));
94
0
        if (!final_batching_block.empty()) {
95
0
            RETURN_IF_ERROR(_write_impl(state, &final_batching_block, true));
96
0
        } else {
97
            // No batched rows, send empty block with eos signal.
98
0
            Block empty_block = block->clone_empty();
99
0
            RETURN_IF_ERROR(_write_impl(state, &empty_block, true));
100
0
        }
101
0
    }
102
0
    return Status::OK();
103
0
}
104
105
0
Status ExchangeOlapWriter::_write_impl(RuntimeState* state, Block* block, bool eos) {
106
0
    auto rows = block->rows();
107
0
    auto* tablet_partitioner = assert_cast<TabletSinkHashPartitioner*>(_partitioner);
108
0
    {
109
0
        SCOPED_TIMER(_local_state.split_block_hash_compute_timer());
110
0
        RETURN_IF_ERROR(tablet_partitioner->do_partitioning(state, block));
111
0
    }
112
0
    {
113
0
        SCOPED_TIMER(_local_state.distribute_rows_into_channels_timer());
114
0
        const auto& channel_ids = tablet_partitioner->get_channel_ids();
115
0
        const auto invalid_val = tablet_partitioner->invalid_sentinel();
116
0
        DCHECK_EQ(channel_ids.size(), rows);
117
118
        // decrease not sinked rows this time
119
0
        COUNTER_UPDATE(_local_state.rows_input_counter(),
120
0
                       -1LL * std::ranges::count(channel_ids, invalid_val));
121
122
0
        RETURN_IF_ERROR(_channel_add_rows(state, _local_state.channels,
123
0
                                          _local_state.channels.size(), channel_ids, rows, block,
124
0
                                          eos, invalid_val));
125
0
    }
126
0
    return Status::OK();
127
0
}
128
129
2
Status ExchangeTrivialWriter::write(RuntimeState* state, Block* block, bool eos) {
130
2
    {
131
2
        SCOPED_TIMER(_local_state.split_block_hash_compute_timer());
132
2
        RETURN_IF_ERROR(_partitioner->do_partitioning(state, block));
133
2
    }
134
2
    {
135
2
        auto rows = block->rows();
136
2
        SCOPED_TIMER(_local_state.distribute_rows_into_channels_timer());
137
2
        const auto& channel_ids = _partitioner->get_channel_ids();
138
139
2
        RETURN_IF_ERROR(_channel_add_rows(state, _local_state.channels,
140
2
                                          _local_state.channels.size(), channel_ids, rows, block,
141
2
                                          eos));
142
2
    }
143
144
2
    return Status::OK();
145
2
}
146
147
Status ExchangeOlapWriter::_channel_add_rows(RuntimeState* state,
148
                                             std::vector<std::shared_ptr<Channel>>& channels,
149
                                             size_t channel_count,
150
                                             const std::vector<HashValType>& channel_ids,
151
                                             size_t rows, Block* block, bool eos,
152
6
                                             HashValType invalid_val) {
153
6
    size_t effective_rows = 0;
154
6
    effective_rows =
155
16
            std::ranges::count_if(channel_ids, [=](int64_t cid) { return cid != invalid_val; });
156
157
    // row index will skip all skipped rows.
158
6
    _origin_row_idx.resize(effective_rows);
159
6
    _channel_rows_histogram.assign(channel_count, 0U);
160
6
    _channel_pos_offsets.resize(channel_count);
161
22
    for (size_t i = 0; i < rows; ++i) {
162
16
        if (channel_ids[i] == invalid_val) {
163
4
            continue;
164
4
        }
165
12
        auto cid = channel_ids[i];
166
12
        _channel_rows_histogram[cid]++;
167
12
    }
168
6
    _channel_pos_offsets[0] = 0;
169
16
    for (size_t i = 1; i < channel_count; ++i) {
170
10
        _channel_pos_offsets[i] = _channel_pos_offsets[i - 1] + _channel_rows_histogram[i - 1];
171
10
    }
172
22
    for (uint32_t i = 0; i < rows; ++i) {
173
16
        if (channel_ids[i] == invalid_val) {
174
4
            continue;
175
4
        }
176
12
        auto cid = channel_ids[i];
177
12
        auto pos = _channel_pos_offsets[cid]++;
178
12
        _origin_row_idx[pos] = i;
179
12
    }
180
181
6
    return _add_rows_impl(state, channels, channel_count, block, eos);
182
6
}
183
184
Status ExchangeTrivialWriter::_channel_add_rows(RuntimeState* state,
185
                                                std::vector<std::shared_ptr<Channel>>& channels,
186
                                                size_t channel_count,
187
                                                const std::vector<HashValType>& channel_ids,
188
8
                                                size_t rows, Block* block, bool eos) {
189
8
    _origin_row_idx.resize(rows);
190
8
    _channel_rows_histogram.assign(channel_count, 0U);
191
8
    _channel_pos_offsets.resize(channel_count);
192
30
    for (size_t i = 0; i < rows; ++i) {
193
22
        _channel_rows_histogram[channel_ids[i]]++;
194
22
    }
195
8
    _channel_pos_offsets[0] = 0;
196
22
    for (size_t i = 1; i < channel_count; ++i) {
197
14
        _channel_pos_offsets[i] = _channel_pos_offsets[i - 1] + _channel_rows_histogram[i - 1];
198
14
    }
199
30
    for (uint32_t i = 0; i < rows; i++) {
200
22
        auto cid = channel_ids[i];
201
22
        auto pos = _channel_pos_offsets[cid]++;
202
22
        _origin_row_idx[pos] = i;
203
22
    }
204
205
8
    return _add_rows_impl(state, channels, channel_count, block, eos);
206
8
}
207
208
} // namespace doris