Coverage Report

Created: 2026-10-26 10:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/runtime/query_context.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/PaloInternalService_types.h>
21
#include <gen_cpp/RuntimeProfile_types.h>
22
#include <gen_cpp/Types_types.h>
23
#include <glog/logging.h>
24
25
#include <atomic>
26
#include <cstdint>
27
#include <memory>
28
#include <mutex>
29
#include <string>
30
#include <unordered_map>
31
#include <unordered_set>
32
33
#include "common/config.h"
34
#include "common/factory_creator.h"
35
#include "common/object_pool.h"
36
#include "common/status.h"
37
#include "exec/common/memory.h"
38
#include "exec/runtime_filter/runtime_filter_mgr.h"
39
#include "exec/scan/scanner_scheduler.h"
40
#include "runtime/exec_env.h"
41
#include "runtime/memory/mem_tracker_limiter.h"
42
#include "runtime/runtime_predicate.h"
43
#include "runtime/workload_group/workload_group_fwd.h"
44
#include "runtime/workload_management/resource_context.h"
45
#include "util/hash_util.hpp"
46
#include "util/threadpool.h"
47
48
namespace doris {
49
50
namespace io {
51
class FileRangeReadContext;
52
class FileRangeReadScheduler;
53
class RemoteScanCacheWriteLimiter;
54
} // namespace io
55
56
class AIExecutionStatistics;
57
class PipelineFragmentContext;
58
class PipelineTask;
59
class QueryTaskController;
60
class Dependency;
61
class RecCTEScanLocalState;
62
class SpillDataDir;
63
64
struct ReportStatusRequest {
65
    const Status status;
66
    std::vector<RuntimeState*> runtime_states;
67
    bool done;
68
    TNetworkAddress coord_addr;
69
    TUniqueId query_id;
70
    int fragment_id;
71
    TUniqueId fragment_instance_id;
72
    int backend_num;
73
    RuntimeState* runtime_state;
74
    std::string load_error_url;
75
    std::string first_error_msg;
76
    std::function<void(const Status&)> cancel_fn;
77
};
78
79
enum class QuerySource {
80
    INTERNAL_FRONTEND,
81
    STREAM_LOAD,
82
    GROUP_COMMIT_LOAD,
83
    ROUTINE_LOAD,
84
    EXTERNAL_CONNECTOR,
85
    EXTERNAL_FRONTEND
86
};
87
88
const std::string toString(QuerySource query_source);
89
90
// Save the common components of fragments in a query.
91
// Some components like DescriptorTbl may be very large
92
// that will slow down each execution of fragments when DeSer them every time.
93
class DescriptorTbl;
94
class QueryContext : public std::enable_shared_from_this<QueryContext> {
95
    ENABLE_FACTORY_CREATOR(QueryContext);
96
97
public:
98
    static std::shared_ptr<QueryContext> create(TUniqueId query_id, ExecEnv* exec_env,
99
                                                const TQueryOptions& query_options,
100
                                                TNetworkAddress coord_addr, bool is_nereids,
101
                                                TNetworkAddress current_connect_fe,
102
                                                QuerySource query_type);
103
104
    // use QueryContext::create, cannot be made private because of ENABLE_FACTORY_CREATOR::create_shared.
105
    QueryContext(TUniqueId query_id, ExecEnv* exec_env, const TQueryOptions& query_options,
106
                 TNetworkAddress coord_addr, bool is_nereids, TNetworkAddress current_connect_fe,
107
                 QuerySource query_type);
108
109
    ~QueryContext();
110
111
    void init_query_task_controller();
112
113
18.1k
    ExecEnv* exec_env() const { return _exec_env; }
114
115
62.6k
    bool is_timeout(timespec now) const {
116
62.6k
        if (_timeout_second <= 0) {
117
1
            return false;
118
1
        }
119
62.6k
        return _query_watcher.elapsed_time_seconds(now) > _timeout_second;
120
62.6k
    }
121
122
292k
    bool is_single_backend_query() const { return _is_single_backend_query; }
123
124
367k
    void set_single_backend_query(bool is_single_backend_query) {
125
367k
        _is_single_backend_query = is_single_backend_query;
126
367k
    }
127
128
1.61k
    int64_t get_remaining_query_time_seconds() const {
129
1.61k
        timespec now;
130
1.61k
        clock_gettime(CLOCK_MONOTONIC, &now);
131
1.61k
        if (is_timeout(now)) {
132
0
            return -1;
133
0
        }
134
1.61k
        int64_t elapsed_seconds = _query_watcher.elapsed_time_seconds(now);
135
1.61k
        return _timeout_second - elapsed_seconds;
136
1.61k
    }
137
138
    void set_ready_to_execute(Status reason);
139
140
42.8M
    [[nodiscard]] bool is_cancelled() const { return !_exec_status.ok(); }
141
142
    std::string print_all_pipeline_context();
143
    void set_pipeline_context(const int fragment_id,
144
                              std::shared_ptr<PipelineFragmentContext> pip_ctx);
145
    // The sole entry point for query cancellation. Only the first error is accepted; it is then
146
    // propagated to every PipelineFragmentContext for fragment-local cleanup.
147
    void cancel(Status new_status);
148
149
807k
    [[nodiscard]] Status exec_status() { return _exec_status.status(); }
150
151
    void set_execution_dependency_ready();
152
153
    void set_memory_sufficient(bool sufficient);
154
155
    void set_ready_to_execute_only();
156
157
267k
    bool has_runtime_predicate(int source_node_id) {
158
267k
        return _runtime_predicates.contains(source_node_id);
159
267k
    }
160
161
33.1k
    RuntimePredicate& get_runtime_predicate(int source_node_id) {
162
33.1k
        DCHECK(has_runtime_predicate(source_node_id));
163
33.1k
        return _runtime_predicates.find(source_node_id)->second;
164
33.1k
    }
165
166
620
    void init_runtime_predicates(const std::vector<TTopnFilterDesc>& topn_filter_descs) {
167
627
        for (auto desc : topn_filter_descs) {
168
627
            _runtime_predicates.try_emplace(desc.source_node_id, desc);
169
627
        }
170
620
    }
171
172
    Status set_workload_group(WorkloadGroupPtr& wg);
173
174
19.9k
    int execution_timeout() const {
175
19.9k
        return _query_options.__isset.execution_timeout ? _query_options.execution_timeout
176
18.4E
                                                        : _query_options.query_timeout;
177
19.9k
    }
178
179
0
    int32_t runtime_filter_wait_time_ms() const {
180
0
        return _query_options.runtime_filter_wait_time_ms;
181
0
    }
182
183
0
    int be_exec_version() const {
184
0
        if (!_query_options.__isset.be_exec_version) {
185
0
            return 0;
186
0
        }
187
0
        return _query_options.be_exec_version;
188
0
    }
189
190
61.0k
    [[nodiscard]] int64_t get_fe_process_uuid() const {
191
61.0k
        return _query_options.__isset.fe_process_uuid ? _query_options.fe_process_uuid : 0;
192
61.0k
    }
193
194
4.03k
    bool ignore_runtime_filter_error() const {
195
4.03k
        return _query_options.__isset.ignore_runtime_filter_error
196
4.03k
                       ? _query_options.ignore_runtime_filter_error
197
18.4E
                       : false;
198
4.03k
    }
199
200
0
    bool enable_force_spill() const {
201
0
        return _query_options.__isset.enable_force_spill && _query_options.enable_force_spill;
202
0
    }
203
15.3M
    const TQueryOptions& query_options() const { return _query_options; }
204
205
    // global runtime filter mgr, the runtime filter have remote target or
206
    // need local merge should regist here. before publish() or push_to_remote()
207
    // the runtime filter should do the local merge work
208
373k
    RuntimeFilterMgr* runtime_filter_mgr() { return _runtime_filter_mgr.get(); }
209
210
679k
    TUniqueId query_id() const { return _query_id; }
211
212
    // Record a spill data directory before opening the first spill part so teardown only visits
213
    // touched roots.
214
    void record_spill_data_dir(SpillDataDir* data_dir);
215
216
    // Expose task-level query progress counters for runtime statistics reporting.
217
    void add_total_task_num(int delta);
218
    void inc_finished_task_num();
219
220
450k
    ScannerScheduler* get_scan_scheduler() { return _scan_task_scheduler; }
221
222
3.28k
    ScannerScheduler* get_remote_scan_scheduler() { return _remote_scan_task_scheduler; }
223
224
1.65M
    Dependency* get_execution_dependency() { return _execution_dependency.get(); }
225
2.88M
    Dependency* get_memory_sufficient_dependency() { return _memory_sufficient_dependency.get(); }
226
227
    doris::TaskScheduler* get_pipe_exec_scheduler();
228
229
    void set_merge_controller_handler(
230
245k
            std::shared_ptr<RuntimeFilterMergeControllerEntity>& handler) {
231
245k
        _merge_controller_handler = handler;
232
245k
    }
233
5.55k
    std::shared_ptr<RuntimeFilterMergeControllerEntity> get_merge_controller_handler() const {
234
5.55k
        return _merge_controller_handler;
235
5.55k
    }
236
237
10.5M
    bool is_nereids() const { return _is_nereids; }
238
184k
    std::shared_ptr<MemShareArbitrator> mem_arb() const { return _mem_arb; }
239
240
6.15M
    WorkloadGroupPtr workload_group() const { return _resource_ctx->workload_group(); }
241
10.0M
    std::shared_ptr<MemTrackerLimiter> query_mem_tracker() const {
242
10.0M
        DCHECK(_resource_ctx->memory_context()->mem_tracker() != nullptr);
243
10.0M
        return _resource_ctx->memory_context()->mem_tracker();
244
10.0M
    }
245
246
1.14M
    int32_t get_slot_count() const {
247
1.14M
        return _query_options.__isset.query_slot_count ? _query_options.query_slot_count : 1;
248
1.14M
    }
249
250
    DescriptorTbl* desc_tbl = nullptr;
251
    bool set_rsc_info = false;
252
    std::string user;
253
    std::string group;
254
    TNetworkAddress coord_addr;
255
    TNetworkAddress current_connect_fe;
256
    TQueryGlobals query_globals;
257
16
    const TQueryGlobals get_query_globals() const { return query_globals; }
258
259
    ObjectPool obj_pool;
260
261
47.0M
    std::shared_ptr<ResourceContext> resource_ctx() { return _resource_ctx; }
262
263
4.55M
    io::RemoteScanCacheWriteLimiter* remote_scan_cache_write_limiter() const {
264
4.55M
        return _remote_scan_cache_write_limiter.get();
265
4.55M
    }
266
267
    /// Return the query-wide read-ahead context, creating it from `scheduler` on first use. Query
268
    /// cancellation propagates to every range registered through the returned context.
269
    std::shared_ptr<io::FileRangeReadContext> get_or_create_file_range_read_context(
270
            io::FileRangeReadScheduler* scheduler);
271
272
    // plan node id -> TFileScanRangeParams
273
    // only for file scan node
274
    std::map<int, TFileScanRangeParams> file_scan_range_params_map;
275
276
    void add_using_brpc_stub(const TNetworkAddress& network_address,
277
1.52M
                             std::shared_ptr<PBackendService_Stub> brpc_stub) {
278
1.52M
        if (network_address.port == 0) {
279
496
            return;
280
496
        }
281
1.51M
        std::lock_guard<std::mutex> lock(_brpc_stubs_mutex);
282
1.51M
        if (!_using_brpc_stubs.contains(network_address)) {
283
47.4k
            _using_brpc_stubs.emplace(network_address, brpc_stub);
284
47.4k
        }
285
286
1.51M
        DCHECK_EQ(_using_brpc_stubs[network_address].get(), brpc_stub.get());
287
1.51M
    }
288
289
322k
    void set_ai_resources(std::map<std::string, TAIResource> ai_resources) {
290
322k
        _ai_resources =
291
322k
                std::make_shared<std::map<std::string, TAIResource>>(std::move(ai_resources));
292
322k
    }
293
294
    // Shared by every AI evaluator in a fragment; profile collection must not create it.
295
    std::shared_ptr<AIExecutionStatistics> get_ai_statistics(int fragment_id);
296
    std::shared_ptr<AIExecutionStatistics> find_ai_statistics(int fragment_id) const;
297
298
129
    const std::shared_ptr<std::map<std::string, TAIResource>>& get_ai_resources() const {
299
129
        return _ai_resources;
300
129
    }
301
302
    std::unordered_map<TNetworkAddress, std::shared_ptr<PBackendService_Stub>>
303
61.0k
    get_using_brpc_stubs() {
304
61.0k
        std::lock_guard<std::mutex> lock(_brpc_stubs_mutex);
305
61.0k
        return _using_brpc_stubs;
306
61.0k
    }
307
308
0
    void set_low_memory_mode() {
309
        // will not return from low memory mode to non-low memory mode.
310
0
        _resource_ctx->task_controller()->set_low_memory_mode(true);
311
0
    }
312
15.4M
    bool low_memory_mode() { return _resource_ctx->task_controller()->low_memory_mode(); }
313
314
2.77M
    bool is_pure_load_task() {
315
2.77M
        return _query_source == QuerySource::STREAM_LOAD ||
316
2.77M
               _query_source == QuerySource::ROUTINE_LOAD ||
317
2.77M
               _query_source == QuerySource::GROUP_COMMIT_LOAD;
318
2.77M
    }
319
320
    void set_load_error_url(std::string error_url);
321
    std::string get_load_error_url();
322
    void set_first_error_msg(std::string error_msg);
323
    std::string get_first_error_msg();
324
325
    Status send_block_to_cte_scan(const TUniqueId& instance_id, int node_id,
326
                                  const google::protobuf::RepeatedPtrField<doris::PBlock>& pblocks,
327
                                  bool eos);
328
    void registe_cte_scan(const TUniqueId& instance_id, int node_id, RecCTEScanLocalState* scan);
329
    void deregiste_cte_scan(const TUniqueId& instance_id, int node_id);
330
331
0
    std::vector<int> get_fragment_ids() {
332
0
        std::vector<int> fragment_ids;
333
0
        for (const auto& it : _fragment_id_to_pipeline_ctx) {
334
0
            fragment_ids.push_back(it.first);
335
0
        }
336
0
        return fragment_ids;
337
0
    }
338
339
    Status reset_global_rf(const google::protobuf::RepeatedField<int32_t>& filter_ids);
340
341
private:
342
    // Task-level progress counters for current query.
343
    friend class QueryTaskController;
344
345
    int _timeout_second;
346
    TUniqueId _query_id;
347
    ExecEnv* _exec_env = nullptr;
348
    MonotonicStopWatch _query_watcher;
349
    bool _is_nereids = false;
350
351
    std::mutex _spill_data_dirs_mutex;
352
    std::unordered_set<SpillDataDir*> _spill_data_dirs;
353
354
    std::shared_ptr<ResourceContext> _resource_ctx;
355
356
    void _init_resource_context();
357
    void _init_query_mem_tracker();
358
359
    std::unordered_map<int, RuntimePredicate> _runtime_predicates;
360
    std::unique_ptr<RuntimeFilterMgr> _runtime_filter_mgr;
361
    const TQueryOptions _query_options;
362
363
    // All pipeline tasks use the same query context to report status. So we need a `_exec_status`
364
    // to report the real message if failed.
365
    AtomicStatus _exec_status;
366
367
    doris::TaskScheduler* _task_scheduler = nullptr;
368
    ScannerScheduler* _scan_task_scheduler = nullptr;
369
    ScannerScheduler* _remote_scan_task_scheduler = nullptr;
370
    // This dependency indicates if the 2nd phase RPC received from FE.
371
    std::unique_ptr<Dependency> _execution_dependency;
372
    // This dependency indicates if memory is sufficient to execute.
373
    std::unique_ptr<Dependency> _memory_sufficient_dependency;
374
375
    // This shared ptr is never used. It is just a reference to hold the object.
376
    // There is a weak ptr in runtime filter manager to reference this object.
377
    std::shared_ptr<RuntimeFilterMergeControllerEntity> _merge_controller_handler;
378
379
    std::map<int, std::weak_ptr<PipelineFragmentContext>> _fragment_id_to_pipeline_ctx;
380
    std::mutex _pipeline_map_write_lock;
381
382
    std::mutex _profile_mutex;
383
    timespec _query_arrival_timestamp;
384
    // Distinguish the query source, for query that comes from fe, we will have some memory structure on FE to
385
    // help us manage the query.
386
    QuerySource _query_source;
387
388
    std::mutex _brpc_stubs_mutex;
389
    std::unordered_map<TNetworkAddress, std::shared_ptr<PBackendService_Stub>> _using_brpc_stubs;
390
391
    // when fragment of pipeline is closed, it will register its profile to this map by using add_fragment_profile
392
    // flatten profile of one fragment:
393
    // Pipeline 0
394
    //      PipelineTask 0
395
    //              Operator 1
396
    //              Operator 2
397
    //              Scanner
398
    //      PipelineTask 1
399
    //              Operator 1
400
    //              Operator 2
401
    //              Scanner
402
    // Pipeline 1
403
    //      PipelineTask 2
404
    //              Operator 3
405
    //      PipelineTask 3
406
    //              Operator 3
407
    // fragment_id -> list<profile>
408
    std::unordered_map<int, std::vector<std::shared_ptr<TRuntimeProfileTree>>> _profile_map;
409
    std::unordered_map<int, std::shared_ptr<TRuntimeProfileTree>> _load_channel_profile_map;
410
411
    std::shared_ptr<std::map<std::string, TAIResource>> _ai_resources;
412
    mutable std::mutex _ai_statistics_mutex;
413
    std::unordered_map<int, std::shared_ptr<AIExecutionStatistics>> _ai_statistics;
414
415
    void _report_query_profile();
416
417
    std::unordered_map<int, std::vector<std::shared_ptr<TRuntimeProfileTree>>>
418
    _collect_realtime_query_profile();
419
420
    std::mutex _error_url_lock;
421
    std::string _load_error_url;
422
    std::string _first_error_msg;
423
424
    bool _is_single_backend_query = false;
425
426
    // file cache context holders
427
    std::vector<io::BlockFileCache::QueryFileCacheContextHolderPtr> _query_context_holders;
428
    // instance id + node id -> cte scan
429
    std::map<std::pair<TUniqueId, int>, RecCTEScanLocalState*> _cte_scan;
430
    std::mutex _cte_scan_lock;
431
    std::shared_ptr<MemShareArbitrator> _mem_arb = nullptr;
432
    std::unique_ptr<io::RemoteScanCacheWriteLimiter> _remote_scan_cache_write_limiter;
433
    std::mutex _file_range_read_context_mutex;
434
    io::FileRangeReadScheduler* _file_range_read_scheduler = nullptr;
435
    std::shared_ptr<io::FileRangeReadContext> _file_range_read_context;
436
437
public:
438
    // when fragment of pipeline is closed, it will register its profile to this map by using add_fragment_profile
439
    void add_fragment_profile(
440
            int fragment_id,
441
            const std::vector<std::shared_ptr<TRuntimeProfileTree>>& pipeline_profile,
442
            std::shared_ptr<TRuntimeProfileTree> load_channel_profile);
443
444
    TReportExecStatusParams get_realtime_exec_status();
445
446
740k
    bool enable_profile() const {
447
741k
        return _query_options.__isset.enable_profile && _query_options.enable_profile;
448
740k
    }
449
450
0
    timespec get_query_arrival_timestamp() const { return this->_query_arrival_timestamp; }
451
71.7k
    QuerySource get_query_source() const { return this->_query_source; }
452
453
639k
    TQueryOptions get_query_options() const { return _query_options; }
454
};
455
456
} // namespace doris