Coverage Report

Created: 2026-11-02 17:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/exchange/vdata_stream_recvr.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 <gen_cpp/Types_types.h>
21
#include <glog/logging.h>
22
#include <google/protobuf/stubs/callback.h>
23
24
#include <atomic>
25
#include <condition_variable>
26
#include <cstddef>
27
#include <cstdint>
28
#include <deque>
29
#include <list>
30
#include <memory>
31
#include <mutex>
32
#include <ostream>
33
#include <sstream>
34
#include <thread>
35
#include <unordered_map>
36
#include <unordered_set>
37
#include <utility>
38
#include <vector>
39
40
#include "common/config.h"
41
#include "common/global_types.h"
42
#include "common/object_pool.h"
43
#include "common/status.h"
44
#include "core/block/block.h"
45
#include "exprs/vexpr_fwd.h"
46
#include "runtime/descriptors.h"
47
#include "runtime/runtime_profile.h"
48
#include "runtime/task_execution_context.h"
49
#include "runtime/thread_context.h"
50
#include "util/stopwatch.hpp"
51
52
namespace doris {
53
class MemTracker;
54
class PBlock;
55
class PTransmitDataParams;
56
class MemTrackerLimiter;
57
class RuntimeState;
58
59
class Dependency;
60
class ExchangeLocalState;
61
62
class VDataStreamMgr;
63
class VSortedRunMerger;
64
65
class VDataStreamRecvr;
66
67
class VDataStreamRecvr : public HasTaskExecutionCtx {
68
public:
69
    class SenderQueue;
70
    VDataStreamRecvr(VDataStreamMgr* stream_mgr, RuntimeProfile::HighWaterMarkCounter* counter,
71
                     RuntimeState* state, const TUniqueId& fragment_instance_id,
72
                     PlanNodeId dest_node_id, int num_senders, bool is_merging,
73
                     RuntimeProfile* profile, size_t data_queue_capacity);
74
75
    ~VDataStreamRecvr() override;
76
77
    MOCK_FUNCTION Status create_merger(const VExprContextSPtrs& ordering_expr,
78
                                       const std::vector<bool>& is_asc_order,
79
                                       const std::vector<bool>& nulls_first, size_t batch_size,
80
                                       int64_t limit, size_t offset);
81
82
571k
    std::vector<SenderQueue*> sender_queues() const { return _sender_queues; }
83
84
    Status add_block(std::unique_ptr<PBlock> pblock, int sender_id, int be_number,
85
                     int64_t packet_seq, ::google::protobuf::Closure** done,
86
                     const int64_t wait_for_worker, const uint64_t time_to_find_recvr);
87
88
    Status add_blocks(const PTransmitDataParams* request, ::google::protobuf::Closure** done,
89
                      const int64_t wait_for_worker, const uint64_t time_to_find_recvr);
90
91
    void add_block(Block* block, int sender_id, bool use_move);
92
    std::string debug_string();
93
94
    MOCK_FUNCTION Status get_next(Block* block, bool* eos);
95
96
3.24M
    const TUniqueId& fragment_instance_id() const { return _fragment_instance_id; }
97
3.24M
    PlanNodeId dest_node_id() const { return _dest_node_id; }
98
99
    // Indicate that a particular sender is done. Delegated to the appropriate
100
    // sender queue. Called from DataStreamMgr.
101
    void remove_sender(int sender_id, int be_number, Status exec_status);
102
103
    void cancel_stream(Status exec_status);
104
105
    MOCK_FUNCTION void close();
106
107
    // When the source reaches eos = true
108
    void set_sink_dep_always_ready() const;
109
110
    // Careful: stream sender will call this function for a local receiver,
111
    // accessing members of receiver that are allocated by Object pool
112
    // in this function is not safe.
113
    MOCK_FUNCTION bool exceeds_limit(size_t block_byte_size);
114
    bool queue_exceeds_limit(size_t byte_size) const;
115
1.06M
    bool is_closed() const { return _is_closed; }
116
117
    std::shared_ptr<Dependency> get_local_channel_dependency(int sender_id);
118
119
22
    void set_low_memory_mode() { _sender_queue_mem_limit = 1012 * 1024; }
120
121
private:
122
    friend struct BlockSupplierSortCursorImpl;
123
124
    // DataStreamMgr instance used to create this recvr. (Not owned)
125
    VDataStreamMgr* _mgr = nullptr;
126
127
    RuntimeProfile::HighWaterMarkCounter* _memory_used_counter = nullptr;
128
129
    std::shared_ptr<ResourceContext> _resource_ctx;
130
131
    std::shared_ptr<QueryContext> _query_context;
132
133
    // Fragment and node id of the destination exchange node this receiver is used by.
134
    TUniqueId _fragment_instance_id;
135
    PlanNodeId _dest_node_id;
136
137
    // Row schema, copied from the caller of CreateRecvr().
138
    RowDescriptor _row_desc;
139
140
    // True if this reciver merges incoming rows from different senders. Per-sender
141
    // row batch queues are maintained in this case.
142
    bool _is_merging;
143
    bool _is_closed;
144
145
    std::unique_ptr<MemTracker> _mem_tracker;
146
    // Managed by object pool
147
    std::vector<SenderQueue*> _sender_queues;
148
149
    std::atomic<size_t> _sender_queue_mem_limit;
150
151
    std::unique_ptr<VSortedRunMerger> _merger;
152
153
    ObjectPool _sender_queue_pool;
154
    RuntimeProfile* _profile = nullptr;
155
156
    RuntimeProfile::Counter* _remote_bytes_received_counter = nullptr;
157
    RuntimeProfile::Counter* _local_bytes_received_counter = nullptr;
158
    RuntimeProfile::Counter* _deserialize_row_batch_timer = nullptr;
159
    RuntimeProfile::Counter* _first_batch_wait_total_timer = nullptr;
160
    RuntimeProfile::Counter* _buffer_full_total_timer = nullptr;
161
    RuntimeProfile::Counter* _data_arrival_timer = nullptr;
162
    RuntimeProfile::Counter* _decompress_timer = nullptr;
163
    RuntimeProfile::Counter* _decompress_bytes = nullptr;
164
165
    // Number of blocks received
166
    RuntimeProfile::Counter* _blocks_produced_counter = nullptr;
167
    RuntimeProfile::Counter* _max_wait_worker_time = nullptr;
168
    RuntimeProfile::Counter* _max_wait_to_process_time = nullptr;
169
    RuntimeProfile::Counter* _max_find_recvr_time = nullptr;
170
171
    std::vector<std::shared_ptr<Dependency>> _sender_to_local_channel_dependency;
172
};
173
174
class VDataStreamRecvr::SenderQueue {
175
public:
176
    SenderQueue(VDataStreamRecvr* parent_recvr, int num_senders,
177
                std::shared_ptr<Dependency> local_channel_dependency);
178
179
    ~SenderQueue();
180
181
    Status get_batch(Block* next_block, bool* eos);
182
183
    bool has_data_or_finished();
184
185
    Status add_block(std::unique_ptr<PBlock> pblock, int be_number, int64_t packet_seq,
186
                     ::google::protobuf::Closure** done, const int64_t wait_for_worker,
187
                     const uint64_t time_to_find_recvr);
188
189
    Status add_blocks(const PTransmitDataParams* request, ::google::protobuf::Closure** done,
190
                      const int64_t wait_for_worker, const uint64_t time_to_find_recvr);
191
192
    std::string debug_string();
193
194
    void add_block(Block* block, bool use_move);
195
196
    void decrement_senders(int sender_id);
197
198
    void cancel(Status cancel_status);
199
200
    void close();
201
202
458k
    void set_dependency(std::shared_ptr<Dependency> dependency) {
203
        // Assign under _lock: set_source_ready() (reached via decrement_senders/cancel/close
204
        // on other threads) reads _source_dependency while holding _lock, so a lock-free
205
        // shared_ptr assignment here would race with that read.
206
458k
        std::lock_guard<std::mutex> l(_lock);
207
458k
        _source_dependency = dependency;
208
        // A queue created with zero senders (bucket-shuffle orphan instance, see
209
        // ExchangeLocalState::create_stream_recvr) never goes through decrement_senders,
210
        // so the usual reached-zero set_ready never fires — mark it ready at wiring time
211
        // or its task blocks forever on SHUFFLE_DATA_DEPENDENCY.
212
458k
        if (_num_remaining_senders == 0) {
213
362
            set_source_ready(l);
214
362
        }
215
458k
    }
216
217
protected:
218
    struct BlockItem;
219
220
    void add_blocks_memory_usage(int64_t size);
221
222
    void sub_blocks_memory_usage(int64_t size);
223
224
    bool exceeds_limit();
225
    friend class ExchangeLocalState;
226
227
    void set_source_ready(std::lock_guard<std::mutex>&);
228
    void run_block_queue_done_callbacks(std::list<BlockItem>& block_queue);
229
230
    // Not managed by this class
231
    VDataStreamRecvr* _recvr = nullptr;
232
    std::mutex _lock;
233
    bool _is_cancelled;
234
    Status _cancel_status;
235
    int _num_remaining_senders;
236
    std::unique_ptr<MemTracker> _queue_mem_tracker;
237
238
    // `BlockItem` is used in `_block_queue` to handle both local and remote exchange blocks.
239
    // For local exchange blocks, `BlockUPtr` is used directly without any modification.
240
    // For remote exchange blocks, the `pblock` is stored in `BlockItem`.
241
    // When `getBlock` is called, the `pblock` is deserialized into a usable block.
242
    struct BlockItem {
243
231k
        Status get_block(BlockUPtr& block) {
244
231k
            if (!_block) {
245
89.4k
                DCHECK(_pblock);
246
89.4k
                SCOPED_RAW_TIMER(&_deserialize_time);
247
89.4k
                _block = Block::create_unique();
248
89.4k
                RETURN_IF_ERROR_OR_CATCH_EXCEPTION(
249
89.4k
                        _block->deserialize(*_pblock, &_decompress_bytes, &_decompress_time));
250
89.4k
            }
251
231k
            block.swap(_block);
252
231k
            _block.reset();
253
231k
            return Status::OK();
254
231k
        }
255
256
231k
        size_t block_byte_size() const { return _block_byte_size; }
257
231k
        int64_t deserialize_time() const { return _deserialize_time; }
258
231k
        int64_t decompress_time() const { return _decompress_time; }
259
231k
        size_t decompress_bytes() const { return _decompress_bytes; }
260
        BlockItem();
261
        BlockItem(BlockUPtr&& block, size_t block_byte_size);
262
        BlockItem(std::unique_ptr<PBlock>&& pblock, size_t block_byte_size);
263
        BlockItem(BlockItem&&) noexcept;
264
        BlockItem& operator=(BlockItem&&) noexcept;
265
        ~BlockItem();
266
267
40
        void set_done(google::protobuf::Closure* done) {
268
            // The done callback is only set when the queue memory limit is exceeded.
269
40
            _done_cb = done;
270
40
            _wait_timer.start();
271
40
        }
272
273
232k
        void call_done(VDataStreamRecvr* recvr) {
274
232k
            if (_done_cb != nullptr) {
275
40
                _done_cb->Run();
276
40
                _done_cb = nullptr;
277
40
                _wait_timer.stop();
278
40
                int64_t elapse_time = _wait_timer.elapsed_time();
279
40
                if (recvr->_max_wait_to_process_time->value() < elapse_time) {
280
6
                    recvr->_max_wait_to_process_time->set(elapse_time);
281
6
                }
282
40
                recvr->_buffer_full_total_timer->update(elapse_time);
283
40
            }
284
232k
        }
285
286
    private:
287
        BlockUPtr _block;
288
        std::unique_ptr<PBlock> _pblock;
289
        size_t _block_byte_size = 0;
290
        int64_t _deserialize_time = 0;
291
        int64_t _decompress_time = 0;
292
        size_t _decompress_bytes = 0;
293
294
        google::protobuf::Closure* _done_cb = nullptr;
295
        MonotonicStopWatch _wait_timer;
296
    };
297
298
    std::list<BlockItem> _block_queue;
299
300
    // sender_id
301
    std::unordered_set<int> _sender_eos_set;
302
    // be_number => packet_seq
303
    std::unordered_map<int, int64_t> _packet_seq_map;
304
305
    std::shared_ptr<Dependency> _source_dependency;
306
    std::shared_ptr<Dependency> _local_channel_dependency;
307
};
308
309
} // namespace doris