Coverage Report

Created: 2026-08-07 09:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/exchange/local_exchanger.h
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
#pragma once
19
20
#include <concurrentqueue.h>
21
22
#include "exec/operator/operator.h"
23
#include "exec/pipeline/dependency.h"
24
25
namespace doris {
26
template <typename T>
27
void clear_blocks(moodycamel::ConcurrentQueue<T>& blocks,
28
                  RuntimeProfile::Counter* memory_used_counter = nullptr);
29
30
class PartitionerBase;
31
class LocalExchangeSourceLocalState;
32
class LocalExchangeSinkLocalState;
33
34
struct Profile {
35
    RuntimeProfile::Counter* compute_hash_value_timer = nullptr;
36
    RuntimeProfile::Counter* distribute_timer = nullptr;
37
    RuntimeProfile::Counter* copy_data_timer = nullptr;
38
};
39
40
struct SinkInfo {
41
    int* channel_id;
42
    PartitionerBase* partitioner;
43
    LocalExchangeSinkLocalState* local_state;
44
    std::map<int, int>* shuffle_idx_to_instance_idx;
45
    int ins_idx;
46
};
47
48
struct SourceInfo {
49
    int channel_id;
50
    LocalExchangeSourceLocalState* local_state;
51
};
52
/**
53
 * One exchanger is hold by one `LocalExchangeSharedState`. And one `LocalExchangeSharedState` is
54
 * shared by all local exchange sink operators and source operators with the same id.
55
 *
56
 * In exchanger, two block queues is maintained, one is data block queue and another is free block queue.
57
 *
58
 * In details, data block queue has queues as many as source operators. Each source operator will get
59
 * data block from the corresponding queue. Data blocks is push into the queue by sink operators. One
60
 * sink operator will push blocks into one or more queues.
61
 *
62
 * Free block is used to reuse the allocated memory. To reduce the memory limit, we also use a conf
63
 * to limit the size of free block queue.
64
 */
65
class ExchangerBase {
66
public:
67
    /**
68
     * `BlockWrapper` is used to wrap a data block with a reference count.
69
     *
70
     * In function `unref()`, if `ref_count` decremented to 0, which means this block is not needed by
71
     * operators, so we put it into `_free_blocks` to reuse its memory if needed and refresh memory usage
72
     * in current queue.
73
     *
74
     * Note: `ref_count` will be larger than 1 only if this block is shared between multiple queues in
75
     * shuffle exchanger.
76
     */
77
    class BlockWrapper {
78
    public:
79
        ENABLE_FACTORY_CREATOR(BlockWrapper);
80
        BlockWrapper(Block&& data_block, LocalExchangeSharedState* shared_state, int channel_id)
81
103
                : _data_block(std::move(data_block)),
82
103
                  _shared_state(shared_state),
83
103
                  _allocated_bytes(_data_block.allocated_bytes()) {
84
103
            if (_shared_state) {
85
103
                _shared_state->add_total_mem_usage(_allocated_bytes);
86
103
            }
87
103
        }
88
103
        ~BlockWrapper() {
89
103
            if (_shared_state != nullptr) {
90
103
                DCHECK_GT(_allocated_bytes, 0);
91
                // `_channel_ids` may be empty if exchanger is shuffled exchanger and channel id is
92
                // not used by `sub_total_mem_usage`. So we just pass -1 here.
93
103
                _shared_state->sub_total_mem_usage(_allocated_bytes);
94
103
                if (_shared_state->exchanger->_free_block_limit == 0 ||
95
103
                    _shared_state->exchanger->_free_blocks.size_approx() <
96
28
                            _shared_state->exchanger->_free_block_limit *
97
87
                                    _shared_state->exchanger->_num_sources) {
98
87
                    _data_block.clear_column_data();
99
                    // Free blocks is used to improve memory efficiency. Failure during pushing back
100
                    // free block will not incur any bad result so just ignore the return value.
101
87
                    _shared_state->exchanger->_free_blocks.enqueue(std::move(_data_block));
102
87
                }
103
103
            };
104
103
        }
105
162
        void record_channel_id(int channel_id) {
106
162
            _channel_ids.push_back(channel_id);
107
162
            if (_shared_state) {
108
162
                _shared_state->add_mem_usage(channel_id, _allocated_bytes);
109
162
            }
110
162
        }
111
112
    private:
113
        friend class ShuffleExchanger;
114
        friend class BucketShuffleExchanger;
115
        friend class PassthroughExchanger;
116
        friend class BroadcastExchanger;
117
        friend class PassToOneExchanger;
118
        friend class AdaptivePassthroughExchanger;
119
        template <typename BlockType>
120
        friend class Exchanger;
121
122
        Block _data_block;
123
        LocalExchangeSharedState* _shared_state;
124
        std::vector<int> _channel_ids;
125
        const size_t _allocated_bytes;
126
    };
127
    ExchangerBase(int running_sink_operators, int num_partitions, int free_block_limit)
128
4
            : _running_sink_operators(running_sink_operators),
129
4
              _running_source_operators(num_partitions),
130
4
              _num_partitions(num_partitions),
131
4
              _num_senders(running_sink_operators),
132
4
              _num_sources(num_partitions),
133
4
              _free_block_limit(free_block_limit) {}
134
    ExchangerBase(int running_sink_operators, int num_sources, int num_partitions,
135
                  int free_block_limit)
136
3
            : _running_sink_operators(running_sink_operators),
137
3
              _running_source_operators(num_sources),
138
3
              _num_partitions(num_partitions),
139
3
              _num_senders(running_sink_operators),
140
3
              _num_sources(num_sources),
141
3
              _free_block_limit(free_block_limit) {}
142
7
    virtual ~ExchangerBase() = default;
143
    virtual Status get_block(RuntimeState* state, Block* block, bool* eos, Profile&& profile,
144
                             SourceInfo&& source_info) = 0;
145
    virtual Status sink(RuntimeState* state, Block* in_block, bool eos, Profile&& profile,
146
                        SinkInfo& sink_info) = 0;
147
    virtual TLocalPartitionType::type get_type() const = 0;
148
    // Called if a local exchanger source operator are closed. Free the unused data block in data_queue.
149
    virtual void close(SourceInfo&& source_info) = 0;
150
    // Called if all local exchanger source operators are closed. We free the memory in
151
    // `_free_blocks` here.
152
    virtual void finalize();
153
154
    virtual std::string data_queue_debug_string(int i) = 0;
155
156
0
    void set_low_memory_mode() {
157
0
        _free_block_limit = 0;
158
0
        clear_blocks(_free_blocks);
159
0
    }
160
161
protected:
162
    friend struct LocalExchangeSharedState;
163
    friend class LocalExchangeSourceLocalState;
164
    friend class LocalExchangeSinkOperatorX;
165
    friend class LocalExchangeSinkLocalState;
166
    std::atomic<int> _running_sink_operators = 0;
167
    std::atomic<int> _running_source_operators = 0;
168
    const int _num_partitions;
169
    const int _num_senders;
170
    const int _num_sources;
171
    std::atomic_int _free_block_limit = 0;
172
    moodycamel::ConcurrentQueue<Block> _free_blocks;
173
};
174
175
struct PartitionedRowIdxs {
176
    std::shared_ptr<PODArray<uint32_t>> row_idxs;
177
    uint32_t offset_start;
178
    uint32_t length;
179
};
180
181
using PartitionedBlock =
182
        std::pair<std::shared_ptr<ExchangerBase::BlockWrapper>, PartitionedRowIdxs>;
183
184
struct BroadcastRowRange {
185
    uint32_t offset_start;
186
    size_t length;
187
};
188
using BroadcastBlock = std::pair<std::shared_ptr<ExchangerBase::BlockWrapper>, BroadcastRowRange>;
189
190
template <typename BlockType>
191
struct BlockQueue {
192
    std::atomic<bool> eos = false;
193
    moodycamel::ConcurrentQueue<BlockType> data_queue;
194
    moodycamel::ProducerToken ptok {data_queue};
195
25
    BlockQueue() : eos(false), data_queue(moodycamel::ConcurrentQueue<BlockType>()) {}
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEEC2Ev
Line
Count
Source
195
13
    BlockQueue() : eos(false), data_queue(moodycamel::ConcurrentQueue<BlockType>()) {}
_ZN5doris10BlockQueueISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEEC2Ev
Line
Count
Source
195
8
    BlockQueue() : eos(false), data_queue(moodycamel::ConcurrentQueue<BlockType>()) {}
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEEC2Ev
Line
Count
Source
195
4
    BlockQueue() : eos(false), data_queue(moodycamel::ConcurrentQueue<BlockType>()) {}
196
    BlockQueue(BlockQueue<BlockType>&& other)
197
0
            : eos(other.eos.load()), data_queue(std::move(other.data_queue)) {}
Unexecuted instantiation: _ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEEC2EOS8_
Unexecuted instantiation: _ZN5doris10BlockQueueISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEEC2EOS5_
Unexecuted instantiation: _ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEEC2EOS8_
198
    inline bool enqueue(BlockType const& item) {
199
        if (!eos) {
200
            if (!data_queue.enqueue(ptok, item)) [[unlikely]] {
201
                throw Exception(ErrorCode::INTERNAL_ERROR,
202
                                "Exception occurs in data queue [size = {}] of local exchange.",
203
                                data_queue.size_approx());
204
            }
205
            return true;
206
        }
207
        return false;
208
    }
209
210
162
    inline bool enqueue(BlockType&& item) {
211
162
        if (!eos) {
212
130
            if (!data_queue.enqueue(ptok, std::move(item))) [[unlikely]] {
213
0
                throw Exception(ErrorCode::INTERNAL_ERROR,
214
0
                                "Exception occurs in data queue [size = {}] of local exchange.",
215
0
                                data_queue.size_approx());
216
0
            }
217
130
            return true;
218
130
        }
219
32
        return false;
220
162
    }
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEE7enqueueEOS7_
Line
Count
Source
210
57
    inline bool enqueue(BlockType&& item) {
211
57
        if (!eos) {
212
49
            if (!data_queue.enqueue(ptok, std::move(item))) [[unlikely]] {
213
0
                throw Exception(ErrorCode::INTERNAL_ERROR,
214
0
                                "Exception occurs in data queue [size = {}] of local exchange.",
215
0
                                data_queue.size_approx());
216
0
            }
217
49
            return true;
218
49
        }
219
8
        return false;
220
57
    }
_ZN5doris10BlockQueueISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEE7enqueueEOS4_
Line
Count
Source
210
41
    inline bool enqueue(BlockType&& item) {
211
41
        if (!eos) {
212
33
            if (!data_queue.enqueue(ptok, std::move(item))) [[unlikely]] {
213
0
                throw Exception(ErrorCode::INTERNAL_ERROR,
214
0
                                "Exception occurs in data queue [size = {}] of local exchange.",
215
0
                                data_queue.size_approx());
216
0
            }
217
33
            return true;
218
33
        }
219
8
        return false;
220
41
    }
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEE7enqueueEOS7_
Line
Count
Source
210
64
    inline bool enqueue(BlockType&& item) {
211
64
        if (!eos) {
212
48
            if (!data_queue.enqueue(ptok, std::move(item))) [[unlikely]] {
213
0
                throw Exception(ErrorCode::INTERNAL_ERROR,
214
0
                                "Exception occurs in data queue [size = {}] of local exchange.",
215
0
                                data_queue.size_approx());
216
0
            }
217
48
            return true;
218
48
        }
219
16
        return false;
220
64
    }
221
222
255
    bool try_dequeue(BlockType& item) { return data_queue.try_dequeue(item); }
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEE11try_dequeueERS7_
Line
Count
Source
222
117
    bool try_dequeue(BlockType& item) { return data_queue.try_dequeue(item); }
_ZN5doris10BlockQueueISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEE11try_dequeueERS4_
Line
Count
Source
222
66
    bool try_dequeue(BlockType& item) { return data_queue.try_dequeue(item); }
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEE11try_dequeueERS7_
Line
Count
Source
222
72
    bool try_dequeue(BlockType& item) { return data_queue.try_dequeue(item); }
223
224
20
    void set_eos() { eos = true; }
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEE7set_eosEv
Line
Count
Source
224
8
    void set_eos() { eos = true; }
_ZN5doris10BlockQueueISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEE7set_eosEv
Line
Count
Source
224
8
    void set_eos() { eos = true; }
_ZN5doris10BlockQueueISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEE7set_eosEv
Line
Count
Source
224
4
    void set_eos() { eos = true; }
225
};
226
227
using BlockWrapperSPtr = std::shared_ptr<ExchangerBase::BlockWrapper>;
228
229
template <typename BlockType>
230
class Exchanger : public ExchangerBase {
231
public:
232
    Exchanger(int running_sink_operators, int num_partitions, int free_block_limit,
233
              TLocalPartitionType::type type)
234
4
            : ExchangerBase(running_sink_operators, num_partitions, free_block_limit), _type(type) {
235
4
        _data_queue.resize(num_partitions);
236
4
        _m.resize(num_partitions);
237
20
        for (size_t i = 0; i < num_partitions; i++) {
238
16
            _m[i] = std::make_unique<std::mutex>();
239
16
        }
240
4
    }
_ZN5doris9ExchangerISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEEC2EiiiNS_19TLocalPartitionType4typeE
Line
Count
Source
234
2
            : ExchangerBase(running_sink_operators, num_partitions, free_block_limit), _type(type) {
235
2
        _data_queue.resize(num_partitions);
236
2
        _m.resize(num_partitions);
237
10
        for (size_t i = 0; i < num_partitions; i++) {
238
8
            _m[i] = std::make_unique<std::mutex>();
239
8
        }
240
2
    }
_ZN5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEEC2EiiiNS_19TLocalPartitionType4typeE
Line
Count
Source
234
1
            : ExchangerBase(running_sink_operators, num_partitions, free_block_limit), _type(type) {
235
1
        _data_queue.resize(num_partitions);
236
1
        _m.resize(num_partitions);
237
5
        for (size_t i = 0; i < num_partitions; i++) {
238
4
            _m[i] = std::make_unique<std::mutex>();
239
4
        }
240
1
    }
_ZN5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEEC2EiiiNS_19TLocalPartitionType4typeE
Line
Count
Source
234
1
            : ExchangerBase(running_sink_operators, num_partitions, free_block_limit), _type(type) {
235
1
        _data_queue.resize(num_partitions);
236
1
        _m.resize(num_partitions);
237
5
        for (size_t i = 0; i < num_partitions; i++) {
238
4
            _m[i] = std::make_unique<std::mutex>();
239
4
        }
240
1
    }
241
    Exchanger(int running_sink_operators, int num_sources, int num_partitions, int free_block_limit,
242
              TLocalPartitionType::type type)
243
3
            : ExchangerBase(running_sink_operators, num_sources, num_partitions, free_block_limit),
244
3
              _type(type) {
245
3
        _data_queue.resize(num_sources);
246
3
        _m.resize(num_sources);
247
12
        for (size_t i = 0; i < num_sources; i++) {
248
9
            _m[i] = std::make_unique<std::mutex>();
249
9
        }
250
3
    }
251
7
    ~Exchanger() override = default;
_ZN5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEED2Ev
Line
Count
Source
251
4
    ~Exchanger() override = default;
_ZN5doris9ExchangerISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEED2Ev
Line
Count
Source
251
2
    ~Exchanger() override = default;
_ZN5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEED2Ev
Line
Count
Source
251
1
    ~Exchanger() override = default;
252
1
    TLocalPartitionType::type get_type() const override { return _type; }
_ZNK5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEE8get_typeEv
Line
Count
Source
252
1
    TLocalPartitionType::type get_type() const override { return _type; }
Unexecuted instantiation: _ZNK5doris9ExchangerISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEE8get_typeEv
Unexecuted instantiation: _ZNK5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEE8get_typeEv
253
0
    std::string data_queue_debug_string(int i) override {
254
0
        return fmt::format("Data Queue {}: [size approx = {}, eos = {}]", i,
255
0
                           _data_queue[i].data_queue.size_approx(), _data_queue[i].eos);
256
0
    }
Unexecuted instantiation: _ZN5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_18PartitionedRowIdxsEEE23data_queue_debug_stringB5cxx11Ei
Unexecuted instantiation: _ZN5doris9ExchangerISt10shared_ptrINS_13ExchangerBase12BlockWrapperEEE23data_queue_debug_stringB5cxx11Ei
Unexecuted instantiation: _ZN5doris9ExchangerISt4pairISt10shared_ptrINS_13ExchangerBase12BlockWrapperEENS_17BroadcastRowRangeEEE23data_queue_debug_stringB5cxx11Ei
257
258
protected:
259
    // Enqueue data block and set downstream source operator to read.
260
    void _enqueue_data_and_set_ready(int channel_id, LocalExchangeSinkLocalState* local_state,
261
                                     BlockType&& block);
262
    bool _dequeue_data(LocalExchangeSourceLocalState* local_state, BlockType& block, bool* eos,
263
                       Block* data_block, int channel_id);
264
265
    void _enqueue_data_and_set_ready(int channel_id, BlockType&& block);
266
    bool _dequeue_data(BlockType& block, bool* eos, Block* data_block, int channel_id);
267
    std::vector<BlockQueue<BlockType>> _data_queue;
268
    std::vector<std::unique_ptr<std::mutex>> _m;
269
    const TLocalPartitionType::type _type;
270
};
271
272
class LocalExchangeSourceLocalState;
273
class LocalExchangeSinkLocalState;
274
275
class ShuffleExchanger : public Exchanger<PartitionedBlock> {
276
public:
277
    ENABLE_FACTORY_CREATOR(ShuffleExchanger);
278
    ShuffleExchanger(int running_sink_operators, int num_sources, int num_partitions,
279
                     int free_block_limit, TLocalPartitionType::type type)
280
3
            : Exchanger<PartitionedBlock>(running_sink_operators, num_sources, num_partitions,
281
3
                                          free_block_limit, type) {
282
3
        DCHECK_GT(num_partitions, 0);
283
3
        DCHECK_GT(num_sources, 0);
284
3
        _partition_rows_histogram.resize(running_sink_operators);
285
3
    }
286
3
    ~ShuffleExchanger() override = default;
287
    Status sink(RuntimeState* state, Block* in_block, bool eos, Profile&& profile,
288
                SinkInfo& sink_info) override;
289
290
    Status get_block(RuntimeState* state, Block* block, bool* eos, Profile&& profile,
291
                     SourceInfo&& source_info) override;
292
    void close(SourceInfo&& source_info) override;
293
294
protected:
295
    Status _split_rows(RuntimeState* state, const std::vector<uint32_t>& channel_ids, Block* block,
296
                       int channel_id, LocalExchangeSinkLocalState* local_state,
297
                       std::map<int, int>* shuffle_idx_to_instance_idx);
298
    Status _split_rows(RuntimeState* state, const std::vector<uint32_t>& channel_ids, Block* block,
299
                       int channel_id);
300
    std::vector<std::vector<uint32_t>> _partition_rows_histogram;
301
};
302
303
class BucketShuffleExchanger final : public ShuffleExchanger {
304
    ENABLE_FACTORY_CREATOR(BucketShuffleExchanger);
305
    BucketShuffleExchanger(int running_sink_operators, int num_sources, int num_partitions,
306
                           int free_block_limit)
307
0
            : ShuffleExchanger(running_sink_operators, num_sources, num_partitions,
308
0
                               free_block_limit, TLocalPartitionType::BUCKET_HASH_SHUFFLE) {}
309
0
    ~BucketShuffleExchanger() override = default;
310
};
311
312
class PassthroughExchanger final : public Exchanger<BlockWrapperSPtr> {
313
public:
314
    ENABLE_FACTORY_CREATOR(PassthroughExchanger);
315
    PassthroughExchanger(int running_sink_operators, int num_partitions, int free_block_limit)
316
1
            : Exchanger<BlockWrapperSPtr>(running_sink_operators, num_partitions, free_block_limit,
317
1
                                          TLocalPartitionType::PASSTHROUGH) {}
318
1
    ~PassthroughExchanger() override = default;
319
    Status sink(RuntimeState* state, Block* in_block, bool eos, Profile&& profile,
320
                SinkInfo& sink_info) override;
321
322
    Status get_block(RuntimeState* state, Block* block, bool* eos, Profile&& profile,
323
                     SourceInfo&& source_info) override;
324
    void close(SourceInfo&& source_info) override;
325
};
326
327
class PassToOneExchanger final : public Exchanger<BlockWrapperSPtr> {
328
public:
329
    ENABLE_FACTORY_CREATOR(PassToOneExchanger);
330
    PassToOneExchanger(int running_sink_operators, int num_partitions, int free_block_limit)
331
1
            : Exchanger<BlockWrapperSPtr>(running_sink_operators, num_partitions, free_block_limit,
332
1
                                          TLocalPartitionType::PASS_TO_ONE) {}
333
1
    ~PassToOneExchanger() override = default;
334
    Status sink(RuntimeState* state, Block* in_block, bool eos, Profile&& profile,
335
                SinkInfo& sink_info) override;
336
337
    Status get_block(RuntimeState* state, Block* block, bool* eos, Profile&& profile,
338
                     SourceInfo&& source_info) override;
339
    void close(SourceInfo&& source_info) override;
340
};
341
class BroadcastExchanger final : public Exchanger<BroadcastBlock> {
342
public:
343
    ENABLE_FACTORY_CREATOR(BroadcastExchanger);
344
    BroadcastExchanger(int running_sink_operators, int num_partitions, int free_block_limit)
345
1
            : Exchanger<BroadcastBlock>(running_sink_operators, num_partitions, free_block_limit,
346
1
                                        TLocalPartitionType::BROADCAST) {}
347
1
    ~BroadcastExchanger() override = default;
348
    Status sink(RuntimeState* state, Block* in_block, bool eos, Profile&& profile,
349
                SinkInfo& sink_info) override;
350
351
    Status get_block(RuntimeState* state, Block* block, bool* eos, Profile&& profile,
352
                     SourceInfo&& source_info) override;
353
    void close(SourceInfo&& source_info) override;
354
};
355
356
//The code in AdaptivePassthroughExchanger is essentially
357
// a copy of ShuffleExchanger and PassthroughExchanger.
358
class AdaptivePassthroughExchanger : public Exchanger<PartitionedBlock> {
359
public:
360
    ENABLE_FACTORY_CREATOR(AdaptivePassthroughExchanger);
361
    AdaptivePassthroughExchanger(int running_sink_operators, int num_partitions,
362
                                 int free_block_limit)
363
1
            : Exchanger<PartitionedBlock>(running_sink_operators, num_partitions, free_block_limit,
364
1
                                          TLocalPartitionType::ADAPTIVE_PASSTHROUGH) {
365
1
        _partition_rows_histogram.resize(running_sink_operators);
366
1
        _tmp_eos.resize(num_partitions);
367
1
        _tmp_block.resize(num_partitions);
368
1
    }
369
    Status sink(RuntimeState* state, Block* in_block, bool eos, Profile&& profile,
370
                SinkInfo& sink_info) override;
371
372
    Status get_block(RuntimeState* state, Block* block, bool* eos, Profile&& profile,
373
                     SourceInfo&& source_info) override;
374
375
    void close(SourceInfo&& source_info) override;
376
377
private:
378
    Status _passthrough_sink(RuntimeState* state, Block* in_block, SinkInfo& sink_info);
379
    Status _shuffle_sink(RuntimeState* state, Block* in_block, SinkInfo& sink_info);
380
    Status _split_rows(RuntimeState* state, const std::vector<uint32_t>& channel_ids, Block* block,
381
                       SinkInfo& sink_info);
382
383
    std::atomic_bool _is_pass_through = false;
384
    std::atomic_int32_t _total_block = 0;
385
    std::vector<std::vector<uint32_t>> _partition_rows_histogram;
386
387
    std::vector<Block> _tmp_block;
388
    std::vector<bool> _tmp_eos;
389
};
390
} // namespace doris