Coverage Report

Created: 2026-08-07 14:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/runtime/fragment_mgr.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 "runtime/fragment_mgr.h"
19
20
#include <brpc/controller.h>
21
#include <bvar/latency_recorder.h>
22
#include <fmt/format.h>
23
#include <gen_cpp/DorisExternalService_types.h>
24
#include <gen_cpp/FrontendService.h>
25
#include <gen_cpp/FrontendService_types.h>
26
#include <gen_cpp/HeartbeatService_types.h>
27
#include <gen_cpp/Metrics_types.h>
28
#include <gen_cpp/PaloInternalService_types.h>
29
#include <gen_cpp/PlanNodes_types.h>
30
#include <gen_cpp/Planner_types.h>
31
#include <gen_cpp/QueryPlanExtra_types.h>
32
#include <gen_cpp/RuntimeProfile_types.h>
33
#include <gen_cpp/Types_types.h>
34
#include <gen_cpp/internal_service.pb.h>
35
#include <pthread.h>
36
#include <sys/time.h>
37
#include <thrift/TApplicationException.h>
38
#include <thrift/Thrift.h>
39
#include <thrift/protocol/TDebugProtocol.h>
40
#include <thrift/transport/TTransportException.h>
41
#include <unistd.h>
42
43
#include <algorithm>
44
#include <cstddef>
45
#include <ctime>
46
47
// IWYU pragma: no_include <bits/chrono.h>
48
#include <chrono> // IWYU pragma: keep
49
#include <cstdint>
50
#include <map>
51
#include <memory>
52
#include <mutex>
53
#include <sstream>
54
#include <unordered_map>
55
#include <unordered_set>
56
#include <utility>
57
58
#include "common/config.h"
59
#include "common/exception.h"
60
#include "common/logging.h"
61
#include "common/metrics/doris_metrics.h"
62
#include "common/object_pool.h"
63
#include "common/status.h"
64
#include "common/utils.h"
65
#include "core/data_type/primitive_type.h"
66
#include "exec/pipeline/pipeline_fragment_context.h"
67
#include "exec/runtime_filter/runtime_filter_consumer.h"
68
#include "exec/runtime_filter/runtime_filter_mgr.h"
69
#include "io/fs/stream_load_pipe.h"
70
#include "load/stream_load/new_load_stream_mgr.h"
71
#include "load/stream_load/stream_load_context.h"
72
#include "load/stream_load/stream_load_executor.h"
73
#include "runtime/descriptors.h"
74
#include "runtime/exec_env.h"
75
#include "runtime/frontend_info.h"
76
#include "runtime/query_context.h"
77
#include "runtime/runtime_profile.h"
78
#include "runtime/runtime_query_statistics_mgr.h"
79
#include "runtime/runtime_state.h"
80
#include "runtime/thread_context.h"
81
#include "runtime/workload_group/workload_group.h"
82
#include "runtime/workload_group/workload_group_manager.h"
83
#include "service/backend_options.h"
84
#include "storage/id_manager.h"
85
#include "util/brpc_client_cache.h"
86
#include "util/client_cache.h"
87
#include "util/debug_points.h"
88
#include "util/debug_util.h"
89
#include "util/network_util.h"
90
#include "util/thread.h"
91
#include "util/threadpool.h"
92
#include "util/thrift_util.h"
93
#include "util/uid_util.h"
94
95
namespace doris {
96
97
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(fragment_instance_count, MetricUnit::NOUNIT);
98
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(timeout_canceled_fragment_count, MetricUnit::NOUNIT);
99
100
bvar::LatencyRecorder g_fragmentmgr_prepare_latency("doris_FragmentMgr", "prepare");
101
102
bvar::Adder<uint64_t> g_fragment_executing_count("fragment_executing_count");
103
bvar::Status<uint64_t> g_fragment_last_active_time(
104
        "fragment_last_active_time", duration_cast<std::chrono::milliseconds>(
105
                                             std::chrono::system_clock::now().time_since_epoch())
106
                                             .count());
107
108
1.25k
uint64_t get_fragment_executing_count() {
109
1.25k
    return g_fragment_executing_count.get_value();
110
1.25k
}
111
1.25k
uint64_t get_fragment_last_active_time() {
112
1.25k
    return g_fragment_last_active_time.get_value();
113
1.25k
}
114
115
5.55k
std::string to_load_error_http_path(const std::string& file_name) {
116
5.55k
    if (file_name.empty()) {
117
4.90k
        return "";
118
4.90k
    }
119
647
    if (file_name.compare(0, 4, "http") == 0) {
120
599
        return file_name;
121
599
    }
122
48
    std::stringstream url;
123
48
    url << (config::enable_https ? "https" : "http") << "://"
124
48
        << get_host_port(BackendOptions::get_localhost(), config::webserver_port)
125
48
        << "/api/_load_error_log?"
126
48
        << "file=" << file_name;
127
48
    return url.str();
128
647
}
129
130
static Status _do_fetch_running_queries_rpc(const FrontendInfo& fe_info,
131
0
                                            std::unordered_set<TUniqueId>& query_set) {
132
0
    TFetchRunningQueriesResult rpc_result;
133
0
    TFetchRunningQueriesRequest rpc_request;
134
135
0
    Status client_status;
136
0
    const int32_t timeout_ms = 3 * 1000;
137
0
    FrontendServiceConnection rpc_client(ExecEnv::GetInstance()->frontend_client_cache(),
138
0
                                         fe_info.info.coordinator_address, timeout_ms,
139
0
                                         &client_status);
140
    // Abort this fe.
141
0
    if (!client_status.ok()) {
142
0
        LOG_WARNING("Failed to get client for {}, reason is {}",
143
0
                    PrintThriftNetworkAddress(fe_info.info.coordinator_address),
144
0
                    client_status.to_string());
145
0
        return Status::InternalError("Failed to get client for {}, reason is {}",
146
0
                                     PrintThriftNetworkAddress(fe_info.info.coordinator_address),
147
0
                                     client_status.to_string());
148
0
    }
149
150
    // do rpc
151
0
    try {
152
0
        try {
153
0
            rpc_client->fetchRunningQueries(rpc_result, rpc_request);
154
0
        } catch (const apache::thrift::transport::TTransportException& e) {
155
0
            LOG_WARNING("Transport exception reason: {}, reopening", e.what());
156
0
            client_status = rpc_client.reopen(config::thrift_rpc_timeout_ms);
157
0
            if (!client_status.ok()) {
158
0
                LOG_WARNING("Reopen failed, reason: {}", client_status.to_string_no_stack());
159
0
                return Status::InternalError("Reopen failed, reason: {}",
160
0
                                             client_status.to_string_no_stack());
161
0
            }
162
163
0
            rpc_client->fetchRunningQueries(rpc_result, rpc_request);
164
0
        }
165
0
    } catch (apache::thrift::TException& e) {
166
        // During upgrading cluster or meet any other network error.
167
0
        LOG_WARNING("Failed to fetch running queries from {}, reason: {}",
168
0
                    PrintThriftNetworkAddress(fe_info.info.coordinator_address), e.what());
169
0
        return Status::InternalError("Failed to fetch running queries from {}, reason: {}",
170
0
                                     PrintThriftNetworkAddress(fe_info.info.coordinator_address),
171
0
                                     e.what());
172
0
    }
173
174
    // Avoid logic error in frontend.
175
0
    if (!rpc_result.__isset.status || rpc_result.status.status_code != TStatusCode::OK) {
176
0
        LOG_WARNING("Failed to fetch running queries from {}, reason: {}",
177
0
                    PrintThriftNetworkAddress(fe_info.info.coordinator_address),
178
0
                    doris::to_string(rpc_result.status.status_code));
179
0
        return Status::InternalError("Failed to fetch running queries from {}, reason: {}",
180
0
                                     PrintThriftNetworkAddress(fe_info.info.coordinator_address),
181
0
                                     doris::to_string(rpc_result.status.status_code));
182
0
    }
183
184
0
    if (!rpc_result.__isset.running_queries) {
185
0
        return Status::InternalError("Failed to fetch running queries from {}, reason: {}",
186
0
                                     PrintThriftNetworkAddress(fe_info.info.coordinator_address),
187
0
                                     "running_queries is not set");
188
0
    }
189
190
0
    query_set = std::unordered_set<TUniqueId>(rpc_result.running_queries.begin(),
191
0
                                              rpc_result.running_queries.end());
192
0
    return Status::OK();
193
0
};
194
195
0
static std::map<int64_t, std::unordered_set<TUniqueId>> _get_all_running_queries_from_fe() {
196
0
    const std::map<TNetworkAddress, FrontendInfo>& running_fes =
197
0
            ExecEnv::GetInstance()->get_running_frontends();
198
199
0
    std::map<int64_t, std::unordered_set<TUniqueId>> result;
200
0
    std::vector<FrontendInfo> qualified_fes;
201
202
0
    for (const auto& fe : running_fes) {
203
        // Only consider normal frontend.
204
0
        if (fe.first.port != 0 && fe.second.info.process_uuid != 0) {
205
0
            qualified_fes.push_back(fe.second);
206
0
        } else {
207
0
            return {};
208
0
        }
209
0
    }
210
211
0
    for (const auto& fe_addr : qualified_fes) {
212
0
        const int64_t process_uuid = fe_addr.info.process_uuid;
213
0
        std::unordered_set<TUniqueId> query_set;
214
0
        Status st = _do_fetch_running_queries_rpc(fe_addr, query_set);
215
0
        if (!st.ok()) {
216
            // Empty result, cancel worker will not do anything
217
0
            return {};
218
0
        }
219
220
        // frontend_info and process_uuid has been checked in rpc threads.
221
0
        result[process_uuid] = query_set;
222
0
    }
223
224
0
    return result;
225
0
}
226
227
2.06M
inline uint32_t get_map_id(const TUniqueId& query_id, size_t capacity) {
228
2.06M
    uint32_t value = HashUtil::hash(&query_id.lo, 8, 0);
229
2.06M
    value = HashUtil::hash(&query_id.hi, 8, value);
230
2.06M
    return value % capacity;
231
2.06M
}
232
233
1.41M
inline uint32_t get_map_id(std::pair<TUniqueId, int> key, size_t capacity) {
234
1.41M
    uint32_t value = HashUtil::hash(&key.first.lo, 8, 0);
235
1.41M
    value = HashUtil::hash(&key.first.hi, 8, value);
236
1.41M
    return value % capacity;
237
1.41M
}
238
239
template <typename Key, typename Value, typename ValueType>
240
54
ConcurrentContextMap<Key, Value, ValueType>::ConcurrentContextMap() {
241
54
    _internal_map.resize(config::num_query_ctx_map_partitions);
242
6.96k
    for (size_t i = 0; i < config::num_query_ctx_map_partitions; i++) {
243
6.91k
        _internal_map[i] = {std::make_unique<std::shared_mutex>(),
244
6.91k
                            phmap::flat_hash_map<Key, Value>()};
245
6.91k
    }
246
54
}
_ZN5doris20ConcurrentContextMapISt4pairINS_9TUniqueIdEiESt10shared_ptrINS_23PipelineFragmentContextEES5_EC2Ev
Line
Count
Source
240
18
ConcurrentContextMap<Key, Value, ValueType>::ConcurrentContextMap() {
241
18
    _internal_map.resize(config::num_query_ctx_map_partitions);
242
2.32k
    for (size_t i = 0; i < config::num_query_ctx_map_partitions; i++) {
243
2.30k
        _internal_map[i] = {std::make_unique<std::shared_mutex>(),
244
2.30k
                            phmap::flat_hash_map<Key, Value>()};
245
2.30k
    }
246
18
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt8weak_ptrINS_12QueryContextEES3_EC2Ev
Line
Count
Source
240
18
ConcurrentContextMap<Key, Value, ValueType>::ConcurrentContextMap() {
241
18
    _internal_map.resize(config::num_query_ctx_map_partitions);
242
2.32k
    for (size_t i = 0; i < config::num_query_ctx_map_partitions; i++) {
243
2.30k
        _internal_map[i] = {std::make_unique<std::shared_mutex>(),
244
2.30k
                            phmap::flat_hash_map<Key, Value>()};
245
2.30k
    }
246
18
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt10shared_ptrINS_12QueryContextEES3_EC2Ev
Line
Count
Source
240
18
ConcurrentContextMap<Key, Value, ValueType>::ConcurrentContextMap() {
241
18
    _internal_map.resize(config::num_query_ctx_map_partitions);
242
2.32k
    for (size_t i = 0; i < config::num_query_ctx_map_partitions; i++) {
243
2.30k
        _internal_map[i] = {std::make_unique<std::shared_mutex>(),
244
2.30k
                            phmap::flat_hash_map<Key, Value>()};
245
2.30k
    }
246
18
}
247
248
template <typename Key, typename Value, typename ValueType>
249
1.30M
Value ConcurrentContextMap<Key, Value, ValueType>::find(const Key& query_id) {
250
1.30M
    auto id = get_map_id(query_id, _internal_map.size());
251
1.30M
    {
252
1.30M
        std::shared_lock lock(*_internal_map[id].first);
253
1.30M
        auto& map = _internal_map[id].second;
254
1.30M
        auto search = map.find(query_id);
255
1.30M
        if (search != map.end()) {
256
490k
            return search->second;
257
490k
        }
258
810k
        return std::shared_ptr<ValueType>(nullptr);
259
1.30M
    }
260
1.30M
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt8weak_ptrINS_12QueryContextEES3_E4findERKS1_
Line
Count
Source
249
829k
Value ConcurrentContextMap<Key, Value, ValueType>::find(const Key& query_id) {
250
829k
    auto id = get_map_id(query_id, _internal_map.size());
251
829k
    {
252
829k
        std::shared_lock lock(*_internal_map[id].first);
253
829k
        auto& map = _internal_map[id].second;
254
829k
        auto search = map.find(query_id);
255
829k
        if (search != map.end()) {
256
483k
            return search->second;
257
483k
        }
258
345k
        return std::shared_ptr<ValueType>(nullptr);
259
829k
    }
260
829k
}
_ZN5doris20ConcurrentContextMapISt4pairINS_9TUniqueIdEiESt10shared_ptrINS_23PipelineFragmentContextEES5_E4findERKS3_
Line
Count
Source
249
471k
Value ConcurrentContextMap<Key, Value, ValueType>::find(const Key& query_id) {
250
471k
    auto id = get_map_id(query_id, _internal_map.size());
251
471k
    {
252
471k
        std::shared_lock lock(*_internal_map[id].first);
253
471k
        auto& map = _internal_map[id].second;
254
471k
        auto search = map.find(query_id);
255
471k
        if (search != map.end()) {
256
7.22k
            return search->second;
257
7.22k
        }
258
464k
        return std::shared_ptr<ValueType>(nullptr);
259
471k
    }
260
471k
}
261
262
template <typename Key, typename Value, typename ValueType>
263
Status ConcurrentContextMap<Key, Value, ValueType>::apply_if_not_exists(
264
314k
        const Key& query_id, std::shared_ptr<ValueType>& query_ctx, ApplyFunction&& function) {
265
314k
    auto id = get_map_id(query_id, _internal_map.size());
266
314k
    {
267
314k
        std::unique_lock lock(*_internal_map[id].first);
268
314k
        auto& map = _internal_map[id].second;
269
314k
        auto search = map.find(query_id);
270
314k
        if (search != map.end()) {
271
0
            query_ctx = search->second.lock();
272
0
        }
273
314k
        if (!query_ctx) {
274
314k
            return function(map);
275
314k
        }
276
59
        return Status::OK();
277
314k
    }
278
314k
}
279
280
template <typename Key, typename Value, typename ValueType>
281
1.38M
bool ConcurrentContextMap<Key, Value, ValueType>::erase(const Key& query_id) {
282
1.38M
    auto id = get_map_id(query_id, _internal_map.size());
283
1.38M
    std::unique_lock lock(*_internal_map[id].first);
284
1.38M
    auto& map = _internal_map[id].second;
285
1.38M
    return map.erase(query_id) != 0;
286
1.38M
}
_ZN5doris20ConcurrentContextMapISt4pairINS_9TUniqueIdEiESt10shared_ptrINS_23PipelineFragmentContextEES5_E5eraseERKS3_
Line
Count
Source
281
469k
bool ConcurrentContextMap<Key, Value, ValueType>::erase(const Key& query_id) {
282
469k
    auto id = get_map_id(query_id, _internal_map.size());
283
469k
    std::unique_lock lock(*_internal_map[id].first);
284
469k
    auto& map = _internal_map[id].second;
285
469k
    return map.erase(query_id) != 0;
286
469k
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt10shared_ptrINS_12QueryContextEES3_E5eraseERKS1_
Line
Count
Source
281
458k
bool ConcurrentContextMap<Key, Value, ValueType>::erase(const Key& query_id) {
282
458k
    auto id = get_map_id(query_id, _internal_map.size());
283
458k
    std::unique_lock lock(*_internal_map[id].first);
284
458k
    auto& map = _internal_map[id].second;
285
458k
    return map.erase(query_id) != 0;
286
458k
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt8weak_ptrINS_12QueryContextEES3_E5eraseERKS1_
Line
Count
Source
281
458k
bool ConcurrentContextMap<Key, Value, ValueType>::erase(const Key& query_id) {
282
458k
    auto id = get_map_id(query_id, _internal_map.size());
283
458k
    std::unique_lock lock(*_internal_map[id].first);
284
458k
    auto& map = _internal_map[id].second;
285
458k
    return map.erase(query_id) != 0;
286
458k
}
287
288
template <typename Key, typename Value, typename ValueType>
289
void ConcurrentContextMap<Key, Value, ValueType>::insert(const Key& query_id,
290
471k
                                                         std::shared_ptr<ValueType> query_ctx) {
291
471k
    auto id = get_map_id(query_id, _internal_map.size());
292
471k
    {
293
471k
        std::unique_lock lock(*_internal_map[id].first);
294
471k
        auto& map = _internal_map[id].second;
295
471k
        map.insert({query_id, query_ctx});
296
471k
    }
297
471k
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt10shared_ptrINS_12QueryContextEES3_E6insertERKS1_S4_
Line
Count
Source
290
2.92k
                                                         std::shared_ptr<ValueType> query_ctx) {
291
2.92k
    auto id = get_map_id(query_id, _internal_map.size());
292
2.92k
    {
293
2.92k
        std::unique_lock lock(*_internal_map[id].first);
294
2.92k
        auto& map = _internal_map[id].second;
295
2.92k
        map.insert({query_id, query_ctx});
296
2.92k
    }
297
2.92k
}
_ZN5doris20ConcurrentContextMapISt4pairINS_9TUniqueIdEiESt10shared_ptrINS_23PipelineFragmentContextEES5_E6insertERKS3_S6_
Line
Count
Source
290
468k
                                                         std::shared_ptr<ValueType> query_ctx) {
291
468k
    auto id = get_map_id(query_id, _internal_map.size());
292
468k
    {
293
468k
        std::unique_lock lock(*_internal_map[id].first);
294
468k
        auto& map = _internal_map[id].second;
295
468k
        map.insert({query_id, query_ctx});
296
468k
    }
297
468k
}
298
299
template <typename Key, typename Value, typename ValueType>
300
43
void ConcurrentContextMap<Key, Value, ValueType>::clear() {
301
    // Avoid self-deadlock from releasing the last QueryContext
302
    // in _query_ctx_map_delay_delete:
303
    // FragmentMgr::stop()
304
    //   -> _query_ctx_map_delay_delete.clear()
305
    //     -> unique_lock(query_id_lock)
306
    //     -> map.clear()
307
    //       -> QueryContext::~QueryContext()
308
    //         -> FragmentMgr::remove_query_context(query_id)
309
    //           -> _query_ctx_map_delay_delete.erase(query_id)
310
    //             -> unique_lock(query_id_lock) <- deadlock
311
5.50k
    for (auto& pair : _internal_map) {
312
5.50k
        phmap::flat_hash_map<Key, Value> map;
313
5.50k
        {
314
5.50k
            std::unique_lock lock(*pair.first);
315
5.50k
            map.swap(pair.second);
316
5.50k
        }
317
5.50k
        map.clear();
318
5.50k
    }
319
43
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt8weak_ptrINS_12QueryContextEES3_E5clearEv
Line
Count
Source
300
14
void ConcurrentContextMap<Key, Value, ValueType>::clear() {
301
    // Avoid self-deadlock from releasing the last QueryContext
302
    // in _query_ctx_map_delay_delete:
303
    // FragmentMgr::stop()
304
    //   -> _query_ctx_map_delay_delete.clear()
305
    //     -> unique_lock(query_id_lock)
306
    //     -> map.clear()
307
    //       -> QueryContext::~QueryContext()
308
    //         -> FragmentMgr::remove_query_context(query_id)
309
    //           -> _query_ctx_map_delay_delete.erase(query_id)
310
    //             -> unique_lock(query_id_lock) <- deadlock
311
1.79k
    for (auto& pair : _internal_map) {
312
1.79k
        phmap::flat_hash_map<Key, Value> map;
313
1.79k
        {
314
1.79k
            std::unique_lock lock(*pair.first);
315
1.79k
            map.swap(pair.second);
316
1.79k
        }
317
1.79k
        map.clear();
318
1.79k
    }
319
14
}
_ZN5doris20ConcurrentContextMapINS_9TUniqueIdESt10shared_ptrINS_12QueryContextEES3_E5clearEv
Line
Count
Source
300
15
void ConcurrentContextMap<Key, Value, ValueType>::clear() {
301
    // Avoid self-deadlock from releasing the last QueryContext
302
    // in _query_ctx_map_delay_delete:
303
    // FragmentMgr::stop()
304
    //   -> _query_ctx_map_delay_delete.clear()
305
    //     -> unique_lock(query_id_lock)
306
    //     -> map.clear()
307
    //       -> QueryContext::~QueryContext()
308
    //         -> FragmentMgr::remove_query_context(query_id)
309
    //           -> _query_ctx_map_delay_delete.erase(query_id)
310
    //             -> unique_lock(query_id_lock) <- deadlock
311
1.92k
    for (auto& pair : _internal_map) {
312
1.92k
        phmap::flat_hash_map<Key, Value> map;
313
1.92k
        {
314
1.92k
            std::unique_lock lock(*pair.first);
315
1.92k
            map.swap(pair.second);
316
1.92k
        }
317
1.92k
        map.clear();
318
1.92k
    }
319
15
}
_ZN5doris20ConcurrentContextMapISt4pairINS_9TUniqueIdEiESt10shared_ptrINS_23PipelineFragmentContextEES5_E5clearEv
Line
Count
Source
300
14
void ConcurrentContextMap<Key, Value, ValueType>::clear() {
301
    // Avoid self-deadlock from releasing the last QueryContext
302
    // in _query_ctx_map_delay_delete:
303
    // FragmentMgr::stop()
304
    //   -> _query_ctx_map_delay_delete.clear()
305
    //     -> unique_lock(query_id_lock)
306
    //     -> map.clear()
307
    //       -> QueryContext::~QueryContext()
308
    //         -> FragmentMgr::remove_query_context(query_id)
309
    //           -> _query_ctx_map_delay_delete.erase(query_id)
310
    //             -> unique_lock(query_id_lock) <- deadlock
311
1.79k
    for (auto& pair : _internal_map) {
312
1.79k
        phmap::flat_hash_map<Key, Value> map;
313
1.79k
        {
314
1.79k
            std::unique_lock lock(*pair.first);
315
1.79k
            map.swap(pair.second);
316
1.79k
        }
317
1.79k
        map.clear();
318
1.79k
    }
319
14
}
320
321
FragmentMgr::FragmentMgr(ExecEnv* exec_env)
322
18
        : _exec_env(exec_env), _stop_background_threads_latch(1) {
323
18
    _entity = DorisMetrics::instance()->metric_registry()->register_entity("FragmentMgr");
324
18
    INT_UGAUGE_METRIC_REGISTER(_entity, timeout_canceled_fragment_count);
325
326
18
    auto s = Thread::create(
327
18
            "FragmentMgr", "cancel_timeout_plan_fragment", [this]() { this->cancel_worker(); },
328
18
            &_cancel_thread);
329
18
    CHECK(s.ok()) << s.to_string();
330
331
18
    s = ThreadPoolBuilder("FragmentMgrAsyncWorkThreadPool")
332
18
                .set_min_threads(config::fragment_mgr_async_work_pool_thread_num_min)
333
18
                .set_max_threads(config::fragment_mgr_async_work_pool_thread_num_max)
334
18
                .set_max_queue_size(config::fragment_mgr_async_work_pool_queue_size)
335
18
                .build(&_thread_pool);
336
18
    CHECK(s.ok()) << s.to_string();
337
18
}
338
339
14
FragmentMgr::~FragmentMgr() = default;
340
341
14
void FragmentMgr::stop() {
342
14
    DEREGISTER_HOOK_METRIC(fragment_instance_count);
343
14
    _stop_background_threads_latch.count_down();
344
14
    if (_cancel_thread) {
345
14
        _cancel_thread->join();
346
14
    }
347
348
14
    _thread_pool->shutdown();
349
    // Only me can delete
350
14
    _query_ctx_map.clear();
351
    // in one BE's graceful shutdown, cancel_worker will get related running queries via _get_all_running_queries_from_fe and cancel them.
352
    // so clearing here will not make RF consumer hang. if we dont do this, in ~FragmentMgr() there may be QueryContext in _query_ctx_map_delay_delete
353
    // destructred and remove it from _query_ctx_map_delay_delete which is destructring. it's UB.
354
14
    _query_ctx_map_delay_delete.clear();
355
14
    _pipeline_map.clear();
356
14
    decltype(_rerunnable_params_map) rerunnable_params_map;
357
14
    {
358
14
        std::lock_guard<std::mutex> lk(_rerunnable_params_lock);
359
14
        rerunnable_params_map.swap(_rerunnable_params_map);
360
14
    }
361
    // RerunableFragmentInfo holds QueryContext. Destroy it after releasing the lock because
362
    // QueryContext::~QueryContext() can re-enter remove_query_context().
363
14
    rerunnable_params_map.clear();
364
14
}
365
366
1.12M
static void empty_function(RuntimeState*, Status*) {}
367
368
Status FragmentMgr::exec_plan_fragment(const TPipelineFragmentParams& params,
369
                                       const QuerySource query_source,
370
461k
                                       const TPipelineFragmentParamsList& parent) {
371
461k
    if (params.txn_conf.need_txn) {
372
49
        std::shared_ptr<StreamLoadContext> stream_load_ctx =
373
49
                std::make_shared<StreamLoadContext>(_exec_env);
374
49
        stream_load_ctx->db = params.txn_conf.db;
375
49
        stream_load_ctx->db_id = params.txn_conf.db_id;
376
49
        stream_load_ctx->table = params.txn_conf.tbl;
377
49
        stream_load_ctx->txn_id = params.txn_conf.txn_id;
378
49
        stream_load_ctx->id = UniqueId(params.query_id);
379
49
        stream_load_ctx->put_result.__set_pipeline_params(params);
380
49
        stream_load_ctx->use_streaming = true;
381
49
        stream_load_ctx->load_type = TLoadType::MANUL_LOAD;
382
49
        stream_load_ctx->load_src_type = TLoadSourceType::RAW;
383
49
        stream_load_ctx->label = params.import_label;
384
49
        stream_load_ctx->format = TFileFormatType::FORMAT_CSV_PLAIN;
385
49
        stream_load_ctx->timeout_second = 3600;
386
49
        stream_load_ctx->auth.token = params.txn_conf.token;
387
49
        stream_load_ctx->need_commit_self = true;
388
49
        stream_load_ctx->need_rollback = true;
389
49
        auto pipe = std::make_shared<io::StreamLoadPipe>(
390
49
                io::kMaxPipeBufferedBytes /* max_buffered_bytes */, 64 * 1024 /* min_chunk_size */,
391
49
                -1 /* total_length */, true /* use_proto */);
392
49
        stream_load_ctx->body_sink = pipe;
393
49
        stream_load_ctx->pipe = pipe;
394
49
        stream_load_ctx->max_filter_ratio = params.txn_conf.max_filter_ratio;
395
396
49
        RETURN_IF_ERROR(
397
49
                _exec_env->new_load_stream_mgr()->put(stream_load_ctx->id, stream_load_ctx));
398
399
49
        RETURN_IF_ERROR(
400
49
                _exec_env->stream_load_executor()->execute_plan_fragment(stream_load_ctx, parent));
401
49
        return Status::OK();
402
461k
    } else {
403
461k
        return exec_plan_fragment(params, query_source, empty_function, parent);
404
461k
    }
405
461k
}
406
407
// Stage 2. prepare finished. then get FE instruction to execute
408
127k
Status FragmentMgr::start_query_execution(const PExecPlanFragmentStartRequest* request) {
409
127k
    TUniqueId query_id;
410
127k
    query_id.__set_hi(request->query_id().hi());
411
127k
    query_id.__set_lo(request->query_id().lo());
412
127k
    auto q_ctx = get_query_ctx(query_id);
413
127k
    if (q_ctx) {
414
127k
        q_ctx->set_ready_to_execute(Status::OK());
415
127k
        LOG_INFO("Query {} start execution", print_id(query_id));
416
127k
    } else {
417
0
        return Status::InternalError(
418
0
                "Failed to get query fragments context. Query {} may be "
419
0
                "timeout or be cancelled. host: {}",
420
0
                print_id(query_id), BackendOptions::get_localhost());
421
0
    }
422
127k
    return Status::OK();
423
127k
}
424
425
469k
void FragmentMgr::remove_pipeline_context(std::pair<TUniqueId, int> key) {
426
470k
    if (_pipeline_map.erase(key)) {
427
470k
        int64_t now = duration_cast<std::chrono::milliseconds>(
428
470k
                              std::chrono::system_clock::now().time_since_epoch())
429
470k
                              .count();
430
470k
        g_fragment_executing_count << -1;
431
470k
        g_fragment_last_active_time.set_value(now);
432
470k
    }
433
469k
}
434
435
458k
void FragmentMgr::remove_query_context(const TUniqueId& key) {
436
    // Successful FINAL_CLOSE removes rerunnable entries one fragment at a time. Detach any entries
437
    // that remain when the query is removed, such as on cancellation.
438
458k
    decltype(_rerunnable_params_map) rerunnable_params_map;
439
458k
    {
440
458k
        std::lock_guard<std::mutex> lk(_rerunnable_params_lock);
441
475k
        for (auto it = _rerunnable_params_map.begin(); it != _rerunnable_params_map.end();) {
442
17.5k
            if (it->first.first == key) {
443
223
                auto current = it++;
444
223
                rerunnable_params_map.insert(_rerunnable_params_map.extract(current));
445
17.3k
            } else {
446
17.3k
                ++it;
447
17.3k
            }
448
17.5k
        }
449
458k
    }
450
    // Destroy detached entries outside _rerunnable_params_lock. Releasing their last QueryContext
451
    // reference can run QueryContext::~QueryContext(), which calls this method.
452
458k
    rerunnable_params_map.clear();
453
458k
    _query_ctx_map_delay_delete.erase(key);
454
458k
#ifndef BE_TEST
455
458k
    _query_ctx_map.erase(key);
456
458k
#endif
457
458k
}
458
459
829k
std::shared_ptr<QueryContext> FragmentMgr::get_query_ctx(const TUniqueId& query_id) {
460
829k
    auto val = _query_ctx_map.find(query_id);
461
829k
    if (auto q_ctx = val.lock()) {
462
479k
        return q_ctx;
463
479k
    }
464
349k
    return nullptr;
465
829k
}
466
467
Status FragmentMgr::_get_or_create_query_ctx(const TPipelineFragmentParams& params,
468
                                             const TPipelineFragmentParamsList& parent,
469
                                             QuerySource query_source,
470
466k
                                             std::shared_ptr<QueryContext>& query_ctx) {
471
466k
    auto query_id = params.query_id;
472
466k
    DBUG_EXECUTE_IF("FragmentMgr._get_query_ctx.failed", {
473
466k
        return Status::InternalError("FragmentMgr._get_query_ctx.failed, query id {}",
474
466k
                                     print_id(query_id));
475
466k
    });
476
477
    // Find _query_ctx_map, in case some other request has already
478
    // create the query fragments context.
479
466k
    query_ctx = get_query_ctx(query_id);
480
466k
    if (params.is_simplified_param) {
481
        // Get common components from _query_ctx_map
482
153k
        if (!query_ctx) {
483
0
            return Status::InternalError(
484
0
                    "Failed to get query fragments context. Query {} may be timeout or be "
485
0
                    "cancelled. host: {}",
486
0
                    print_id(query_id), BackendOptions::get_localhost());
487
0
        }
488
313k
    } else {
489
314k
        if (!query_ctx) {
490
314k
            RETURN_IF_ERROR(_query_ctx_map.apply_if_not_exists(
491
314k
                    query_id, query_ctx,
492
314k
                    [&](phmap::flat_hash_map<TUniqueId, std::weak_ptr<QueryContext>>& map)
493
314k
                            -> Status {
494
314k
                        WorkloadGroupPtr workload_group_ptr = nullptr;
495
314k
                        std::vector<uint64_t> wg_id_set;
496
314k
                        if (params.__isset.workload_groups && !params.workload_groups.empty()) {
497
314k
                            for (auto& wg : params.workload_groups) {
498
314k
                                wg_id_set.push_back(wg.id);
499
314k
                            }
500
314k
                        }
501
314k
                        workload_group_ptr = _exec_env->workload_group_mgr()->get_group(wg_id_set);
502
503
                        // First time a fragment of a query arrived. print logs.
504
314k
                        LOG(INFO) << "query_id: " << print_id(query_id)
505
314k
                                  << ", coord_addr: " << params.coord
506
314k
                                  << ", total fragment num on current host: "
507
314k
                                  << params.fragment_num_on_host
508
314k
                                  << ", fe process uuid: " << params.query_options.fe_process_uuid
509
314k
                                  << ", query type: " << params.query_options.query_type
510
314k
                                  << ", report audit fe:" << params.current_connect_fe
511
314k
                                  << ", use wg:" << workload_group_ptr->id() << ","
512
314k
                                  << workload_group_ptr->name();
513
514
                        // This may be a first fragment request of the query.
515
                        // Create the query fragments context.
516
                        // Cross-cluster query: coordinator FE may not belong to local cluster.
517
                        // In that case, cancel_worker() should not cancel it based on local FE liveness.
518
314k
                        QuerySource actual_query_source = query_source;
519
314k
                        if (query_source == QuerySource::INTERNAL_FRONTEND &&
520
314k
                            !_exec_env->get_running_frontends().contains(params.coord)) {
521
314k
                            actual_query_source = QuerySource::EXTERNAL_FRONTEND;
522
314k
                        }
523
314k
                        query_ctx = QueryContext::create(
524
314k
                                query_id, _exec_env, params.query_options, params.coord,
525
314k
                                params.is_nereids, params.current_connect_fe, actual_query_source);
526
314k
                        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(query_ctx->query_mem_tracker());
527
314k
                        RETURN_IF_ERROR(DescriptorTbl::create(
528
314k
                                &(query_ctx->obj_pool), params.desc_tbl, &(query_ctx->desc_tbl)));
529
                        // set file scan range params
530
314k
                        if (params.__isset.file_scan_params) {
531
314k
                            query_ctx->file_scan_range_params_map = params.file_scan_params;
532
314k
                        }
533
534
314k
                        query_ctx->query_globals = params.query_globals;
535
536
314k
                        if (params.__isset.resource_info) {
537
314k
                            query_ctx->user = params.resource_info.user;
538
314k
                            query_ctx->group = params.resource_info.group;
539
314k
                            query_ctx->set_rsc_info = true;
540
314k
                        }
541
542
314k
                        if (params.__isset.ai_resources) {
543
314k
                            query_ctx->set_ai_resources(params.ai_resources);
544
314k
                        }
545
546
314k
                        RETURN_IF_ERROR(query_ctx->set_workload_group(workload_group_ptr));
547
548
314k
                        if (parent.__isset.runtime_filter_info) {
549
314k
                            auto info = parent.runtime_filter_info;
550
314k
                            if (info.__isset.runtime_filter_params) {
551
314k
                                auto handler =
552
314k
                                        std::make_shared<RuntimeFilterMergeControllerEntity>();
553
314k
                                RETURN_IF_ERROR(
554
314k
                                        handler->init(query_ctx, info.runtime_filter_params));
555
314k
                                query_ctx->set_merge_controller_handler(handler);
556
557
314k
                                query_ctx->runtime_filter_mgr()->set_runtime_filter_params(
558
314k
                                        info.runtime_filter_params);
559
314k
                                if (!handler->empty()) {
560
314k
                                    _query_ctx_map_delay_delete.insert(query_id, query_ctx);
561
314k
                                }
562
314k
                            }
563
314k
                            if (info.__isset.topn_filter_descs) {
564
314k
                                query_ctx->init_runtime_predicates(info.topn_filter_descs);
565
314k
                            }
566
314k
                        }
567
568
                        // There is some logic in query ctx's dctor, we could not check if exists and delete the
569
                        // temp query ctx now. For example, the query id maybe removed from workload group's queryset.
570
314k
                        map.insert({query_id, query_ctx});
571
314k
                        return Status::OK();
572
314k
                    }));
573
314k
        }
574
313k
    }
575
466k
    return Status::OK();
576
466k
}
577
578
159
std::string FragmentMgr::dump_pipeline_tasks(int64_t duration) {
579
159
    fmt::memory_buffer debug_string_buffer;
580
159
    size_t i = 0;
581
159
    {
582
159
        fmt::format_to(debug_string_buffer,
583
159
                       "{} pipeline fragment contexts are still running! duration_limit={}\n",
584
159
                       _pipeline_map.num_items(), duration);
585
159
        timespec now;
586
159
        clock_gettime(CLOCK_MONOTONIC, &now);
587
588
159
        _pipeline_map.apply([&](phmap::flat_hash_map<std::pair<TUniqueId, int>,
589
159
                                                     std::shared_ptr<PipelineFragmentContext>>& map)
590
20.3k
                                    -> Status {
591
20.3k
            std::set<TUniqueId> query_id_set;
592
20.3k
            for (auto& it : map) {
593
969
                auto elapsed = it.second->elapsed_time() / 1000000000;
594
969
                if (elapsed < duration) {
595
                    // Only display tasks which has been running for more than {duration} seconds.
596
963
                    continue;
597
963
                }
598
6
                if (!query_id_set.contains(it.first.first)) {
599
3
                    query_id_set.insert(it.first.first);
600
3
                    fmt::format_to(
601
3
                            debug_string_buffer, "QueryId: {}, global_runtime_filter_mgr: {}\n",
602
3
                            print_id(it.first.first),
603
3
                            it.second->get_query_ctx()->runtime_filter_mgr()->debug_string());
604
605
3
                    if (it.second->get_query_ctx()->get_merge_controller_handler()) {
606
1
                        fmt::format_to(debug_string_buffer, "{}\n",
607
1
                                       it.second->get_query_ctx()
608
1
                                               ->get_merge_controller_handler()
609
1
                                               ->debug_string());
610
1
                    }
611
3
                }
612
613
6
                auto timeout_second = it.second->timeout_second();
614
6
                fmt::format_to(
615
6
                        debug_string_buffer,
616
6
                        "No.{} (elapse_second={}s, query_timeout_second={}s, is_timeout={}): {}\n",
617
6
                        i, elapsed, timeout_second, it.second->is_timeout(now),
618
6
                        it.second->debug_string());
619
6
                i++;
620
6
            }
621
20.3k
            return Status::OK();
622
20.3k
        });
623
159
    }
624
159
    return fmt::to_string(debug_string_buffer);
625
159
}
626
627
0
std::string FragmentMgr::dump_pipeline_tasks(TUniqueId& query_id) {
628
0
    if (auto q_ctx = get_query_ctx(query_id)) {
629
0
        return q_ctx->print_all_pipeline_context();
630
0
    } else {
631
0
        return fmt::format(
632
0
                "Dump pipeline tasks failed: Query context (query id = {}) not found. \n",
633
0
                print_id(query_id));
634
0
    }
635
0
}
636
637
Status FragmentMgr::exec_plan_fragment(const TPipelineFragmentParams& params,
638
                                       QuerySource query_source, const FinishCallback& cb,
639
                                       const TPipelineFragmentParamsList& parent,
640
467k
                                       std::shared_ptr<bool> is_prepare_success) {
641
467k
    VLOG_ROW << "Query: " << print_id(params.query_id) << " exec_plan_fragment params is "
642
4
             << apache::thrift::ThriftDebugString(params).c_str();
643
    // sometimes TPipelineFragmentParams debug string is too long and glog
644
    // will truncate the log line, so print query options seperately for debuggin purpose
645
467k
    VLOG_ROW << "Query: " << print_id(params.query_id) << "query options is "
646
542
             << apache::thrift::ThriftDebugString(params.query_options).c_str();
647
648
467k
    std::shared_ptr<QueryContext> query_ctx;
649
467k
    RETURN_IF_ERROR(_get_or_create_query_ctx(params, parent, query_source, query_ctx));
650
467k
    SCOPED_ATTACH_TASK(query_ctx.get()->resource_ctx());
651
    // Set single_backend_query before prepare() so that pipeline local states
652
    // (e.g. StreamingAggLocalState) can read the correct value in their constructors.
653
467k
    query_ctx->set_single_backend_query(params.__isset.query_options &&
654
467k
                                        params.query_options.__isset.single_backend_query &&
655
467k
                                        params.query_options.single_backend_query);
656
467k
    int64_t duration_ns = 0;
657
467k
    std::shared_ptr<PipelineFragmentContext> context = std::make_shared<PipelineFragmentContext>(
658
467k
            query_ctx->query_id(), params, query_ctx, _exec_env, cb);
659
467k
    {
660
467k
        SCOPED_RAW_TIMER(&duration_ns);
661
467k
        Status prepare_st = Status::OK();
662
467k
        ASSIGN_STATUS_IF_CATCH_EXCEPTION(prepare_st = context->prepare(_thread_pool.get()),
663
467k
                                         prepare_st);
664
467k
        DBUG_EXECUTE_IF("FragmentMgr.exec_plan_fragment.prepare_failed", {
665
467k
            prepare_st = Status::Aborted("FragmentMgr.exec_plan_fragment.prepare_failed");
666
467k
        });
667
467k
        if (!prepare_st.ok()) {
668
1.21k
            query_ctx->cancel(prepare_st, params.fragment_id);
669
1.21k
            return prepare_st;
670
1.21k
        }
671
467k
    }
672
465k
    g_fragmentmgr_prepare_latency << (duration_ns / 1000);
673
674
465k
    DBUG_EXECUTE_IF("FragmentMgr.exec_plan_fragment.failed",
675
465k
                    { return Status::Aborted("FragmentMgr.exec_plan_fragment.failed"); });
676
465k
    {
677
465k
        int64_t now = duration_cast<std::chrono::milliseconds>(
678
465k
                              std::chrono::system_clock::now().time_since_epoch())
679
465k
                              .count();
680
465k
        g_fragment_executing_count << 1;
681
465k
        g_fragment_last_active_time.set_value(now);
682
683
        // (query_id, fragment_id) is executed only on one BE, locks _pipeline_map.
684
465k
        auto res = _pipeline_map.find({params.query_id, params.fragment_id});
685
465k
        if (res != nullptr) {
686
0
            return Status::InternalError(
687
0
                    "exec_plan_fragment query_id({}) input duplicated fragment_id({})",
688
0
                    print_id(params.query_id), params.fragment_id);
689
0
        }
690
465k
        _pipeline_map.insert({params.query_id, params.fragment_id}, context);
691
465k
    }
692
693
    // Save params for recursive CTE child fragments so we can recreate the PFC later.
694
    // For recursive CTE, the child fragment needs to be destroyed and rebuilt between rounds,
695
    // so we save the original params here and use them in rerun_fragment(rebuild).
696
465k
    if (params.__isset.need_notify_close && params.need_notify_close) {
697
223
        std::lock_guard<std::mutex> lk(_rerunnable_params_lock);
698
223
        _rerunnable_params_map[{params.query_id, params.fragment_id}] = {
699
223
                .deregister_runtime_filter_ids = {},
700
223
                .params = params,
701
223
                .parent = parent,
702
223
                .finish_callback = cb,
703
223
                .query_ctx = query_ctx};
704
223
    }
705
706
466k
    if (!params.__isset.need_wait_execution_trigger || !params.need_wait_execution_trigger) {
707
185k
        query_ctx->set_ready_to_execute_only();
708
185k
    }
709
710
465k
    query_ctx->set_pipeline_context(params.fragment_id, context);
711
712
465k
    RETURN_IF_ERROR(context->submit());
713
465k
    if (is_prepare_success != nullptr) {
714
5.34k
        *is_prepare_success = true;
715
5.34k
    }
716
465k
    return Status::OK();
717
465k
}
718
719
180k
void FragmentMgr::cancel_query(const TUniqueId query_id, const Status reason) {
720
180k
    std::shared_ptr<QueryContext> query_ctx = nullptr;
721
180k
    {
722
180k
        if (auto q_ctx = get_query_ctx(query_id)) {
723
144k
            query_ctx = q_ctx;
724
144k
        } else {
725
36.3k
            LOG(WARNING) << "Query " << print_id(query_id)
726
36.3k
                         << " does not exists, failed to cancel it";
727
36.3k
            return;
728
36.3k
        }
729
180k
    }
730
144k
    SCOPED_ATTACH_TASK(query_ctx->resource_ctx());
731
144k
    query_ctx->cancel(reason);
732
144k
    remove_query_context(query_id);
733
    // Clean up id_file_map in IdManager if exists
734
144k
    if (ExecEnv::GetInstance()->get_id_manager()->get_id_file_map(query_id)) {
735
1.09k
        ExecEnv::GetInstance()->get_id_manager()->remove_id_file_map(query_id);
736
1.09k
    }
737
144k
    LOG(INFO) << "Query " << print_id(query_id)
738
144k
              << " is cancelled and removed. Reason: " << reason.to_string();
739
144k
}
740
741
18
void FragmentMgr::cancel_worker() {
742
18
    LOG(INFO) << "FragmentMgr cancel worker start working.";
743
744
18
    timespec check_invalid_query_last_timestamp;
745
18
    clock_gettime(CLOCK_MONOTONIC, &check_invalid_query_last_timestamp);
746
747
12.7k
    do {
748
12.7k
        std::vector<TUniqueId> queries_lost_coordinator;
749
12.7k
        std::vector<TUniqueId> queries_timeout;
750
12.7k
        std::vector<TUniqueId> queries_pipeline_task_leak;
751
        // Fe process uuid -> set<QueryId>
752
12.7k
        std::map<int64_t, std::unordered_set<TUniqueId>> running_queries_on_all_fes;
753
12.7k
        const std::map<TNetworkAddress, FrontendInfo>& running_fes =
754
12.7k
                ExecEnv::GetInstance()->get_running_frontends();
755
756
12.7k
        timespec now;
757
12.7k
        clock_gettime(CLOCK_MONOTONIC, &now);
758
759
12.7k
        if (config::enable_pipeline_task_leakage_detect &&
760
12.7k
            now.tv_sec - check_invalid_query_last_timestamp.tv_sec >
761
0
                    config::pipeline_task_leakage_detect_period_secs) {
762
0
            check_invalid_query_last_timestamp = now;
763
0
            running_queries_on_all_fes = _get_all_running_queries_from_fe();
764
12.7k
        } else {
765
12.7k
            running_queries_on_all_fes.clear();
766
12.7k
        }
767
768
12.7k
        std::vector<std::shared_ptr<PipelineFragmentContext>> ctx;
769
12.7k
        _pipeline_map.apply(
770
12.7k
                [&](phmap::flat_hash_map<std::pair<TUniqueId, int>,
771
1.63M
                                         std::shared_ptr<PipelineFragmentContext>>& map) -> Status {
772
1.63M
                    ctx.reserve(ctx.size() + map.size());
773
1.63M
                    for (auto& pipeline_itr : map) {
774
61.3k
                        ctx.push_back(pipeline_itr.second);
775
61.3k
                    }
776
1.63M
                    return Status::OK();
777
1.63M
                });
778
61.3k
        for (auto& c : ctx) {
779
61.3k
            c->clear_finished_tasks();
780
61.3k
        }
781
782
12.7k
        std::unordered_map<std::shared_ptr<PBackendService_Stub>, BrpcItem> brpc_stub_with_queries;
783
12.7k
        _collect_timeout_queries_and_brpc_items(queries_timeout, brpc_stub_with_queries, now);
784
785
        // We use a very conservative cancel strategy.
786
        // 0. If there are no running frontends, do not cancel any queries.
787
        // 1. If query's process uuid is zero, do not cancel
788
        // 2. If same process uuid, do not cancel
789
        // 3. If fe has zero process uuid, do not cancel
790
12.7k
        if (running_fes.empty() && _query_ctx_map.num_items() != 0) {
791
1
            LOG_EVERY_N(WARNING, 10)
792
1
                    << "Could not find any running frontends, maybe we are upgrading or "
793
1
                       "starting? "
794
1
                    << "We will not cancel any outdated queries in this situation.";
795
12.7k
        } else {
796
12.7k
            _collect_invalid_queries(queries_lost_coordinator, queries_pipeline_task_leak,
797
12.7k
                                     running_queries_on_all_fes, running_fes,
798
12.7k
                                     check_invalid_query_last_timestamp);
799
12.7k
        }
800
801
12.7k
        if (config::enable_brpc_connection_check) {
802
3.63k
            for (auto it : brpc_stub_with_queries) {
803
1.83k
                if (!it.first) {
804
0
                    LOG(WARNING) << "brpc stub is nullptr, skip it.";
805
0
                    continue;
806
0
                }
807
1.83k
                _check_brpc_available(it.first, it.second);
808
1.83k
            }
809
3.63k
        }
810
811
12.7k
        if (!queries_lost_coordinator.empty()) {
812
10
            LOG(INFO) << "There are " << queries_lost_coordinator.size()
813
10
                      << " queries need to be cancelled, coordinator dead or restarted.";
814
10
        }
815
816
12.7k
        for (const auto& qid : queries_timeout) {
817
1
            cancel_query(qid,
818
1
                         Status::Error<ErrorCode::TIMEOUT>(
819
1
                                 "FragmentMgr cancel worker going to cancel timeout instance "));
820
1
        }
821
822
12.7k
        for (const auto& qid : queries_pipeline_task_leak) {
823
            // Cancel the query, and maybe try to report debug info to fe so that we can
824
            // collect debug info by sql or http api instead of search log.
825
0
            cancel_query(qid, Status::Error<ErrorCode::ILLEGAL_STATE>(
826
0
                                      "Potential pipeline task leakage"));
827
0
        }
828
829
12.7k
        for (const auto& qid : queries_lost_coordinator) {
830
10
            cancel_query(qid, Status::Error<ErrorCode::CANCELLED>(
831
10
                                      "Source frontend is not running or restarted"));
832
10
        }
833
834
12.7k
    } while (!_stop_background_threads_latch.wait_for(
835
12.7k
            std::chrono::seconds(config::fragment_mgr_cancel_worker_interval_seconds)));
836
18
    LOG(INFO) << "FragmentMgr cancel worker is going to exit.";
837
18
}
838
839
void FragmentMgr::_collect_timeout_queries_and_brpc_items(
840
        std::vector<TUniqueId>& queries_timeout,
841
        std::unordered_map<std::shared_ptr<PBackendService_Stub>, BrpcItem>& brpc_stub_with_queries,
842
12.7k
        timespec now) {
843
12.7k
    std::vector<std::shared_ptr<QueryContext>> contexts;
844
12.7k
    _query_ctx_map.apply(
845
1.63M
            [&](phmap::flat_hash_map<TUniqueId, std::weak_ptr<QueryContext>>& map) -> Status {
846
1.68M
                for (auto it = map.begin(); it != map.end();) {
847
46.7k
                    if (auto q_ctx = it->second.lock()) {
848
46.7k
                        contexts.push_back(q_ctx);
849
46.7k
                        if (q_ctx->is_timeout(now)) {
850
1
                            LOG_WARNING("Query {} is timeout", print_id(it->first));
851
1
                            queries_timeout.push_back(it->first);
852
46.7k
                        } else if (config::enable_brpc_connection_check) {
853
35.5k
                            auto brpc_stubs = q_ctx->get_using_brpc_stubs();
854
35.5k
                            for (auto& item : brpc_stubs) {
855
6.96k
                                if (!brpc_stub_with_queries.contains(item.second)) {
856
1.83k
                                    brpc_stub_with_queries.emplace(item.second,
857
1.83k
                                                                   BrpcItem {item.first, {q_ctx}});
858
5.12k
                                } else {
859
5.12k
                                    brpc_stub_with_queries[item.second].queries.emplace_back(q_ctx);
860
5.12k
                                }
861
6.96k
                            }
862
35.5k
                        }
863
46.7k
                        ++it;
864
46.7k
                    } else {
865
25
                        it = map.erase(it);
866
25
                    }
867
46.7k
                }
868
1.63M
                return Status::OK();
869
1.63M
            });
870
12.7k
}
871
872
void FragmentMgr::_collect_invalid_queries(
873
        std::vector<TUniqueId>& queries_lost_coordinator,
874
        std::vector<TUniqueId>& queries_pipeline_task_leak,
875
        const std::map<int64_t, std::unordered_set<TUniqueId>>& running_queries_on_all_fes,
876
        const std::map<TNetworkAddress, FrontendInfo>& running_fes,
877
12.7k
        timespec check_invalid_query_last_timestamp) {
878
12.7k
    std::vector<std::shared_ptr<QueryContext>> q_contexts;
879
12.7k
    _query_ctx_map.apply([&](phmap::flat_hash_map<TUniqueId, std::weak_ptr<QueryContext>>& map)
880
1.63M
                                 -> Status {
881
1.63M
        for (const auto& it : map) {
882
46.8k
            if (auto q_ctx = it.second.lock()) {
883
                // Cross-cluster query: coordinator FE is not in local `running_fes`,
884
                // we should not cancel it based on local coordinator liveness.
885
46.7k
                if (q_ctx->get_query_source() == QuerySource::EXTERNAL_FRONTEND) {
886
1
                    continue;
887
1
                }
888
46.7k
                q_contexts.push_back(q_ctx);
889
46.7k
                const int64_t fe_process_uuid = q_ctx->get_fe_process_uuid();
890
891
46.7k
                if (fe_process_uuid == 0) {
892
                    // zero means this query is from a older version fe or
893
                    // this fe is starting
894
267
                    continue;
895
267
                }
896
897
                // If the query is not running on the any frontends, cancel it.
898
46.5k
                if (auto itr = running_queries_on_all_fes.find(fe_process_uuid);
899
46.5k
                    itr != running_queries_on_all_fes.end()) {
900
                    // Query not found on this frontend, and the query arrives before the last check
901
0
                    if (itr->second.find(it.first) == itr->second.end() &&
902
                        // tv_nsec represents the number of nanoseconds that have elapsed since the time point stored in tv_sec.
903
                        // tv_sec is enough, we do not need to check tv_nsec.
904
0
                        q_ctx->get_query_arrival_timestamp().tv_sec <
905
0
                                check_invalid_query_last_timestamp.tv_sec &&
906
0
                        q_ctx->get_query_source() == QuerySource::INTERNAL_FRONTEND) {
907
0
                        queries_pipeline_task_leak.push_back(q_ctx->query_id());
908
0
                        LOG_INFO(
909
0
                                "Query {}, type {} is not found on any frontends, "
910
0
                                "maybe it "
911
0
                                "is leaked.",
912
0
                                print_id(q_ctx->query_id()), toString(q_ctx->get_query_source()));
913
0
                        continue;
914
0
                    }
915
0
                }
916
917
46.5k
                auto itr = running_fes.find(q_ctx->coord_addr);
918
46.5k
                if (itr != running_fes.end()) {
919
46.5k
                    if (fe_process_uuid == itr->second.info.process_uuid ||
920
46.5k
                        itr->second.info.process_uuid == 0) {
921
46.5k
                        continue;
922
46.5k
                    } else {
923
0
                        LOG_WARNING(
924
0
                                "Coordinator of query {} restarted, going to cancel "
925
0
                                "it.",
926
0
                                print_id(q_ctx->query_id()));
927
0
                    }
928
46.5k
                } else {
929
                    // In some rear cases, the rpc port of follower is not updated in time,
930
                    // then the port of this follower will be zero, but acutally it is still running,
931
                    // and be has already received the query from follower.
932
                    // So we need to check if host is in running_fes.
933
0
                    bool fe_host_is_standing = std::any_of(
934
0
                            running_fes.begin(), running_fes.end(), [&q_ctx](const auto& fe) {
935
0
                                return fe.first.hostname == q_ctx->coord_addr.hostname &&
936
0
                                       fe.first.port == 0;
937
0
                            });
938
0
                    if (fe_host_is_standing) {
939
0
                        LOG_WARNING(
940
0
                                "Coordinator {}:{} is not found, but its host is still "
941
0
                                "running with an unstable brpc port, not going to "
942
0
                                "cancel "
943
0
                                "it.",
944
0
                                q_ctx->coord_addr.hostname, q_ctx->coord_addr.port,
945
0
                                print_id(q_ctx->query_id()));
946
0
                        continue;
947
0
                    } else {
948
0
                        LOG_WARNING(
949
0
                                "Could not find target coordinator {}:{} of query {}, "
950
0
                                "going to "
951
0
                                "cancel it.",
952
0
                                q_ctx->coord_addr.hostname, q_ctx->coord_addr.port,
953
0
                                print_id(q_ctx->query_id()));
954
0
                    }
955
0
                }
956
46.5k
            }
957
            // Coordinator of this query has already dead or query context has been released.
958
10
            queries_lost_coordinator.push_back(it.first);
959
10
        }
960
1.63M
        return Status::OK();
961
1.63M
    });
962
12.7k
}
963
964
void FragmentMgr::_check_brpc_available(const std::shared_ptr<PBackendService_Stub>& brpc_stub,
965
1.83k
                                        const BrpcItem& brpc_item) {
966
1.83k
    const std::string message = "hello doris!";
967
1.83k
    std::string error_message;
968
1.83k
    int32_t failed_count = 0;
969
1.83k
    const int64_t check_timeout_ms =
970
1.83k
            std::max<int64_t>(100, config::brpc_connection_check_timeout_ms);
971
972
1.83k
    while (true) {
973
1.83k
        PHandShakeRequest request;
974
1.83k
        request.set_hello(message);
975
1.83k
        PHandShakeResponse response;
976
1.83k
        brpc::Controller cntl;
977
1.83k
        cntl.set_timeout_ms(check_timeout_ms);
978
1.83k
        cntl.set_max_retry(10);
979
1.83k
        brpc_stub->hand_shake(&cntl, &request, &response, nullptr);
980
981
1.83k
        if (cntl.Failed()) {
982
0
            error_message = cntl.ErrorText();
983
0
            LOG(WARNING) << "brpc stub: " << brpc_item.network_address.hostname << ":"
984
0
                         << brpc_item.network_address.port << " check failed: " << error_message;
985
1.83k
        } else if (response.has_status() && response.status().status_code() == 0) {
986
1.83k
            break;
987
1.83k
        } else {
988
0
            error_message = response.DebugString();
989
0
            LOG(WARNING) << "brpc stub: " << brpc_item.network_address.hostname << ":"
990
0
                         << brpc_item.network_address.port << " check failed: " << error_message;
991
0
        }
992
0
        failed_count++;
993
0
        if (failed_count == 2) {
994
0
            for (const auto& query_wptr : brpc_item.queries) {
995
0
                auto query = query_wptr.lock();
996
0
                if (query && !query->is_cancelled()) {
997
0
                    query->cancel(Status::InternalError("brpc(dest: {}:{}) check failed: {}",
998
0
                                                        brpc_item.network_address.hostname,
999
0
                                                        brpc_item.network_address.port,
1000
0
                                                        error_message));
1001
0
                }
1002
0
            }
1003
1004
0
            LOG(WARNING) << "remove brpc stub from cache: " << brpc_item.network_address.hostname
1005
0
                         << ":" << brpc_item.network_address.port << ", error: " << error_message;
1006
0
            ExecEnv::GetInstance()->brpc_internal_client_cache()->erase(
1007
0
                    brpc_item.network_address.hostname, brpc_item.network_address.port);
1008
0
            break;
1009
0
        }
1010
0
    }
1011
1.83k
}
1012
1013
0
void FragmentMgr::debug(std::stringstream& ss) {}
1014
/*
1015
 * 1. resolve opaqued_query_plan to thrift structure
1016
 * 2. build TPipelineFragmentParams
1017
 */
1018
Status FragmentMgr::exec_external_plan_fragment(const TScanOpenParams& params,
1019
                                                const TQueryPlanInfo& t_query_plan_info,
1020
                                                const TUniqueId& query_id,
1021
                                                const TUniqueId& fragment_instance_id,
1022
3
                                                std::vector<TScanColumnDesc>* selected_columns) {
1023
    // set up desc tbl
1024
3
    DescriptorTbl* desc_tbl = nullptr;
1025
3
    ObjectPool obj_pool;
1026
3
    Status st = DescriptorTbl::create(&obj_pool, t_query_plan_info.desc_tbl, &desc_tbl);
1027
3
    if (!st.ok()) {
1028
0
        LOG(WARNING) << "open context error: extract DescriptorTbl failure";
1029
0
        std::stringstream msg;
1030
0
        msg << " create DescriptorTbl error, should not be modified after returned Doris FE "
1031
0
               "processed";
1032
0
        return Status::InvalidArgument(msg.str());
1033
0
    }
1034
3
    TupleDescriptor* tuple_desc = desc_tbl->get_tuple_descriptor(0);
1035
3
    if (tuple_desc == nullptr) {
1036
0
        LOG(WARNING) << "open context error: extract TupleDescriptor failure";
1037
0
        std::stringstream msg;
1038
0
        msg << " get  TupleDescriptor error, should not be modified after returned Doris FE "
1039
0
               "processed";
1040
0
        return Status::InvalidArgument(msg.str());
1041
0
    }
1042
    // process selected columns form slots
1043
50
    for (const SlotDescriptor* slot : tuple_desc->slots()) {
1044
50
        TScanColumnDesc col;
1045
50
        col.__set_name(slot->col_name());
1046
50
        col.__set_type(to_thrift(slot->type()->get_primitive_type()));
1047
50
        selected_columns->emplace_back(std::move(col));
1048
50
    }
1049
1050
3
    VLOG_QUERY << "BackendService execute open()  TQueryPlanInfo: "
1051
0
               << apache::thrift::ThriftDebugString(t_query_plan_info);
1052
    // assign the param used to execute PlanFragment
1053
3
    TPipelineFragmentParams exec_fragment_params;
1054
3
    exec_fragment_params.protocol_version = (PaloInternalServiceVersion::type)0;
1055
3
    exec_fragment_params.__set_is_simplified_param(false);
1056
3
    exec_fragment_params.__set_fragment(t_query_plan_info.plan_fragment);
1057
3
    exec_fragment_params.__set_desc_tbl(t_query_plan_info.desc_tbl);
1058
1059
    // assign the param used for executing of PlanFragment-self
1060
3
    TPipelineInstanceParams fragment_exec_params;
1061
3
    exec_fragment_params.query_id = query_id;
1062
3
    fragment_exec_params.fragment_instance_id = fragment_instance_id;
1063
3
    exec_fragment_params.coord.hostname = "external";
1064
3
    std::map<::doris::TPlanNodeId, std::vector<TScanRangeParams>> per_node_scan_ranges;
1065
3
    std::vector<TScanRangeParams> scan_ranges;
1066
3
    std::vector<int64_t> tablet_ids = params.tablet_ids;
1067
3
    TNetworkAddress address;
1068
3
    address.hostname = BackendOptions::get_localhost();
1069
3
    address.port = doris::config::be_port;
1070
3
    std::map<int64_t, TTabletVersionInfo> tablet_info = t_query_plan_info.tablet_info;
1071
3
    for (auto tablet_id : params.tablet_ids) {
1072
3
        TPaloScanRange scan_range;
1073
3
        scan_range.db_name = params.database;
1074
3
        scan_range.table_name = params.table;
1075
3
        auto iter = tablet_info.find(tablet_id);
1076
3
        if (iter != tablet_info.end()) {
1077
3
            TTabletVersionInfo info = iter->second;
1078
3
            scan_range.tablet_id = tablet_id;
1079
3
            scan_range.version = std::to_string(info.version);
1080
            // Useless but it is required field in TPaloScanRange
1081
3
            scan_range.version_hash = "0";
1082
3
            scan_range.schema_hash = std::to_string(info.schema_hash);
1083
3
            scan_range.hosts.push_back(address);
1084
3
        } else {
1085
0
            std::stringstream msg;
1086
0
            msg << "tablet_id: " << tablet_id << " not found";
1087
0
            LOG(WARNING) << "tablet_id [ " << tablet_id << " ] not found";
1088
0
            return Status::NotFound(msg.str());
1089
0
        }
1090
3
        TScanRange doris_scan_range;
1091
3
        doris_scan_range.__set_palo_scan_range(scan_range);
1092
3
        TScanRangeParams scan_range_params;
1093
3
        scan_range_params.scan_range = doris_scan_range;
1094
3
        scan_ranges.push_back(scan_range_params);
1095
3
    }
1096
3
    per_node_scan_ranges.insert(std::make_pair((::doris::TPlanNodeId)0, scan_ranges));
1097
3
    fragment_exec_params.per_node_scan_ranges = per_node_scan_ranges;
1098
3
    exec_fragment_params.local_params.push_back(fragment_exec_params);
1099
3
    TQueryOptions query_options;
1100
3
    query_options.batch_size = params.batch_size;
1101
3
    query_options.execution_timeout = params.execution_timeout;
1102
3
    query_options.mem_limit = params.mem_limit;
1103
3
    query_options.__isset.mem_limit = params.__isset.mem_limit;
1104
3
    query_options.query_type = TQueryType::EXTERNAL;
1105
3
    query_options.be_exec_version = BeExecVersionManager::get_newest_version();
1106
3
    exec_fragment_params.__set_query_options(query_options);
1107
3
    VLOG_ROW << "external exec_plan_fragment params is "
1108
0
             << apache::thrift::ThriftDebugString(exec_fragment_params).c_str();
1109
1110
3
    TPipelineFragmentParamsList mocked;
1111
3
    return exec_plan_fragment(exec_fragment_params, QuerySource::EXTERNAL_CONNECTOR, mocked);
1112
3
}
1113
1114
Status FragmentMgr::apply_filterv2(const PPublishFilterRequestV2* request,
1115
1.90k
                                   butil::IOBufAsZeroCopyInputStream* attach_data) {
1116
1.90k
    UniqueId queryid = request->query_id();
1117
1.90k
    TUniqueId query_id;
1118
1.90k
    query_id.__set_hi(queryid.hi);
1119
1.90k
    query_id.__set_lo(queryid.lo);
1120
1.90k
    if (auto q_ctx = get_query_ctx(query_id)) {
1121
1.89k
        SCOPED_ATTACH_TASK(q_ctx.get());
1122
1.89k
        RuntimeFilterMgr* runtime_filter_mgr = q_ctx->runtime_filter_mgr();
1123
1.89k
        DCHECK(runtime_filter_mgr != nullptr);
1124
1125
        // 1. get the target filters
1126
1.89k
        std::vector<std::shared_ptr<RuntimeFilterConsumer>> filters =
1127
1.89k
                runtime_filter_mgr->get_consume_filters(request->filter_id());
1128
1129
        // 2. create the filter wrapper to replace or ignore/disable the target filters
1130
1.89k
        if (!filters.empty()) {
1131
            // Discard stale-stage requests from old recursive CTE rounds.
1132
1.89k
            if (filters[0]->stage() != request->stage()) {
1133
0
                return Status::OK();
1134
0
            }
1135
1.89k
            RETURN_IF_ERROR(filters[0]->assign(*request, attach_data));
1136
5.13k
            std::ranges::for_each(filters, [&](auto& filter) { filter->signal(filters[0].get()); });
1137
1.89k
        }
1138
1.89k
    }
1139
1.90k
    return Status::OK();
1140
1.90k
}
1141
1142
64
Status FragmentMgr::send_filter_size(const PSendFilterSizeRequest* request) {
1143
64
    UniqueId queryid = request->query_id();
1144
64
    TUniqueId query_id;
1145
64
    query_id.__set_hi(queryid.hi);
1146
64
    query_id.__set_lo(queryid.lo);
1147
1148
64
    if (config::enable_debug_points &&
1149
64
        DebugPoints::instance()->is_enable("FragmentMgr::send_filter_size.return_eof")) {
1150
0
        return Status::EndOfFile("inject FragmentMgr::send_filter_size.return_eof");
1151
0
    }
1152
1153
64
    if (auto q_ctx = get_query_ctx(query_id)) {
1154
64
        return q_ctx->get_merge_controller_handler()->send_filter_size(q_ctx, request);
1155
64
    } else {
1156
0
        return Status::EndOfFile(
1157
0
                "Send filter size failed: Query context (query-id: {}) not found, maybe "
1158
0
                "finished",
1159
0
                queryid.to_string());
1160
0
    }
1161
64
}
1162
1163
64
Status FragmentMgr::sync_filter_size(const PSyncFilterSizeRequest* request) {
1164
64
    UniqueId queryid = request->query_id();
1165
64
    TUniqueId query_id;
1166
64
    query_id.__set_hi(queryid.hi);
1167
64
    query_id.__set_lo(queryid.lo);
1168
64
    if (auto q_ctx = get_query_ctx(query_id)) {
1169
64
        try {
1170
64
            return q_ctx->runtime_filter_mgr()->sync_filter_size(request);
1171
64
        } catch (const Exception& e) {
1172
0
            return Status::InternalError(
1173
0
                    "Sync filter size failed: Query context (query-id: {}) error: {}",
1174
0
                    queryid.to_string(), e.what());
1175
0
        }
1176
64
    } else {
1177
0
        return Status::EndOfFile(
1178
0
                "Sync filter size failed: Query context (query-id: {}) already finished",
1179
0
                queryid.to_string());
1180
0
    }
1181
64
}
1182
1183
Status FragmentMgr::merge_filter(const PMergeFilterRequest* request,
1184
2.72k
                                 butil::IOBufAsZeroCopyInputStream* attach_data) {
1185
2.72k
    UniqueId queryid = request->query_id();
1186
1187
2.72k
    TUniqueId query_id;
1188
2.72k
    query_id.__set_hi(queryid.hi);
1189
2.72k
    query_id.__set_lo(queryid.lo);
1190
2.72k
    if (auto q_ctx = get_query_ctx(query_id)) {
1191
2.71k
        SCOPED_ATTACH_TASK(q_ctx.get());
1192
2.71k
        if (!q_ctx->get_merge_controller_handler()) {
1193
0
            return Status::InternalError("Merge filter failed: Merge controller handler is null");
1194
0
        }
1195
2.71k
        return q_ctx->get_merge_controller_handler()->merge(q_ctx, request, attach_data);
1196
2.71k
    } else {
1197
9
        return Status::EndOfFile(
1198
9
                "Merge filter size failed: Query context (query-id: {}) already finished",
1199
9
                queryid.to_string());
1200
9
    }
1201
2.72k
}
1202
1203
void FragmentMgr::get_runtime_query_info(
1204
25.3k
        std::vector<std::weak_ptr<ResourceContext>>* _resource_ctx_list) {
1205
25.3k
    std::vector<std::shared_ptr<QueryContext>> contexts;
1206
25.3k
    _query_ctx_map.apply(
1207
3.24M
            [&](phmap::flat_hash_map<TUniqueId, std::weak_ptr<QueryContext>>& map) -> Status {
1208
3.33M
                for (auto iter = map.begin(); iter != map.end();) {
1209
94.2k
                    if (auto q_ctx = iter->second.lock()) {
1210
94.2k
                        _resource_ctx_list->push_back(q_ctx->resource_ctx());
1211
94.2k
                        contexts.push_back(q_ctx);
1212
94.2k
                        iter++;
1213
94.2k
                    } else {
1214
46
                        iter = map.erase(iter);
1215
46
                    }
1216
94.2k
                }
1217
3.24M
                return Status::OK();
1218
3.24M
            });
1219
25.3k
}
1220
1221
Status FragmentMgr::get_realtime_exec_status(const TUniqueId& query_id,
1222
0
                                             TReportExecStatusParams* exec_status) {
1223
0
    if (exec_status == nullptr) {
1224
0
        return Status::InvalidArgument("exes_status is nullptr");
1225
0
    }
1226
1227
0
    std::shared_ptr<QueryContext> query_context = get_query_ctx(query_id);
1228
0
    if (query_context == nullptr) {
1229
0
        return Status::NotFound("Query {} not found or released", print_id(query_id));
1230
0
    }
1231
1232
0
    *exec_status = query_context->get_realtime_exec_status();
1233
1234
0
    return Status::OK();
1235
0
}
1236
1237
1
Status FragmentMgr::get_query_statistics(const TUniqueId& query_id, TQueryStatistics* query_stats) {
1238
1
    if (query_stats == nullptr) {
1239
0
        return Status::InvalidArgument("query_stats is nullptr");
1240
0
    }
1241
1242
1
    return ExecEnv::GetInstance()->runtime_query_statistics_mgr()->get_query_statistics(
1243
1
            print_id(query_id), query_stats);
1244
1
}
1245
1246
Status FragmentMgr::transmit_rec_cte_block(
1247
        const TUniqueId& query_id, const TUniqueId& instance_id, int node_id,
1248
4.22k
        const google::protobuf::RepeatedPtrField<doris::PBlock>& pblocks, bool eos) {
1249
4.22k
    if (auto q_ctx = get_query_ctx(query_id)) {
1250
4.22k
        SCOPED_ATTACH_TASK(q_ctx.get());
1251
4.22k
        return q_ctx->send_block_to_cte_scan(instance_id, node_id, pblocks, eos);
1252
4.22k
    } else {
1253
0
        return Status::EndOfFile(
1254
0
                "Transmit rec cte block failed: Query context (query-id: {}) not found, maybe "
1255
0
                "finished",
1256
0
                print_id(query_id));
1257
0
    }
1258
4.22k
}
1259
1260
// Orchestrates the recursive CTE fragment lifecycle through 4 phases:
1261
//
1262
// wait_for_destroy: collect deregister RF IDs, store brpc closure, trigger old PFC close
1263
// rebuild: increment stage, deregister old RFs, create+prepare new PFC from saved params
1264
// submit: submit the new PFC's pipeline tasks for execution
1265
// final_close: send final report, detach saved params, trigger the last PFC close
1266
//
1267
// During wait_for_destroy, the brpc ClosureGuard is stored in the PFC so the RPC response is
1268
// deferred until the PFC is fully destroyed. This gives the caller (RecCTESourceOperatorX) a
1269
// synchronization point to know when the old PFC has finished all its tasks.
1270
Status FragmentMgr::rerun_fragment(const std::shared_ptr<brpc::ClosureGuard>& guard,
1271
                                   const TUniqueId& query_id, int fragment_id,
1272
2
                                   PRerunFragmentParams_Opcode stage) {
1273
2
    if (stage == PRerunFragmentParams::WAIT_FOR_DESTROY ||
1274
2
        stage == PRerunFragmentParams::FINAL_CLOSE) {
1275
2
        auto fragment_ctx = _pipeline_map.find({query_id, fragment_id});
1276
2
        if (!fragment_ctx) {
1277
0
            return Status::NotFound("Fragment context (query-id: {}, fragment-id: {}) not found",
1278
0
                                    print_id(query_id), fragment_id);
1279
0
        }
1280
1281
2
        if (stage == PRerunFragmentParams::WAIT_FOR_DESTROY) {
1282
0
            std::unique_lock<std::mutex> lk(_rerunnable_params_lock);
1283
0
            auto it = _rerunnable_params_map.find({query_id, fragment_id});
1284
0
            if (it == _rerunnable_params_map.end()) {
1285
0
                lk.unlock();
1286
0
                auto st = fragment_ctx->listen_wait_close(guard, true);
1287
0
                if (!st.ok()) {
1288
0
                    LOG(WARNING) << fmt::format(
1289
0
                            "wait_for_destroy fragment context (query-id: {}, fragment-id: "
1290
0
                            "{}) failed: {}",
1291
0
                            print_id(query_id), fragment_id, st.to_string());
1292
0
                }
1293
0
                return Status::NotFound(
1294
0
                        "Rerunnable params (query-id: {}, fragment-id: {}) not found",
1295
0
                        print_id(query_id), fragment_id);
1296
0
            }
1297
1298
0
            it->second.deregister_runtime_filter_ids.merge(
1299
0
                    fragment_ctx->get_deregister_runtime_filter());
1300
0
        }
1301
1302
2
        auto* query_ctx = fragment_ctx->get_query_ctx();
1303
2
        SCOPED_ATTACH_TASK(query_ctx);
1304
2
        RETURN_IF_ERROR(
1305
2
                fragment_ctx->listen_wait_close(guard, stage == PRerunFragmentParams::FINAL_CLOSE));
1306
1307
2
        if (stage == PRerunFragmentParams::FINAL_CLOSE) {
1308
            // Detach the entry under the lock and destroy it afterward. This keeps the invariant
1309
            // that RerunableFragmentInfo, which owns QueryContext, is never destroyed while holding
1310
            // _rerunnable_params_lock.
1311
2
            decltype(_rerunnable_params_map)::node_type final_close_info;
1312
2
            {
1313
2
                std::lock_guard<std::mutex> lk(_rerunnable_params_lock);
1314
2
                final_close_info = _rerunnable_params_map.extract({query_id, fragment_id});
1315
2
            }
1316
2
        }
1317
2
        fragment_ctx->notify_close();
1318
2
        return Status::OK();
1319
2
    } else if (stage == PRerunFragmentParams::REBUILD) {
1320
0
        auto q_ctx = get_query_ctx(query_id);
1321
0
        if (!q_ctx) {
1322
0
            return Status::NotFound(
1323
0
                    "rerun_fragment: Query context (query-id: {}) not found, maybe finished",
1324
0
                    print_id(query_id));
1325
0
        }
1326
0
        SCOPED_ATTACH_TASK(q_ctx.get());
1327
0
        RerunableFragmentInfo info;
1328
0
        {
1329
0
            std::lock_guard<std::mutex> lk(_rerunnable_params_lock);
1330
0
            auto it = _rerunnable_params_map.find({query_id, fragment_id});
1331
0
            if (it == _rerunnable_params_map.end()) {
1332
0
                return Status::NotFound("rebuild (query-id: {}, fragment-id: {}) not found",
1333
0
                                        print_id(query_id), fragment_id);
1334
0
            }
1335
0
            it->second.stage++;
1336
            // Deregister old runtime filters so new ones can be registered in the new PFC.
1337
0
            for (int32_t filter_id : it->second.deregister_runtime_filter_ids) {
1338
0
                q_ctx->runtime_filter_mgr()->remove_filter(filter_id);
1339
0
            }
1340
0
            info = it->second;
1341
0
        }
1342
1343
0
        auto context = std::make_shared<PipelineFragmentContext>(
1344
0
                q_ctx->query_id(), info.params, q_ctx, _exec_env, info.finish_callback);
1345
        // Propagate the recursion stage so that runtime filters created by this PFC
1346
        // carry the correct stage number.
1347
0
        context->set_rec_cte_stage(info.stage);
1348
1349
0
        Status prepare_st = Status::OK();
1350
0
        ASSIGN_STATUS_IF_CATCH_EXCEPTION(prepare_st = context->prepare(_thread_pool.get()),
1351
0
                                         prepare_st);
1352
0
        if (!prepare_st.ok()) {
1353
0
            q_ctx->cancel(prepare_st, info.params.fragment_id);
1354
0
            return prepare_st;
1355
0
        }
1356
1357
        // Insert new PFC into _pipeline_map (old one was removed)
1358
0
        _pipeline_map.insert({info.params.query_id, info.params.fragment_id}, context);
1359
1360
        // Update QueryContext mapping (must support overwrite)
1361
0
        q_ctx->set_pipeline_context(info.params.fragment_id, context);
1362
0
        return Status::OK();
1363
1364
0
    } else if (stage == PRerunFragmentParams::SUBMIT) {
1365
0
        auto fragment_ctx = _pipeline_map.find({query_id, fragment_id});
1366
0
        if (!fragment_ctx) {
1367
0
            return Status::NotFound("Fragment context (query-id: {}, fragment-id: {}) not found",
1368
0
                                    print_id(query_id), fragment_id);
1369
0
        }
1370
0
        return fragment_ctx->submit();
1371
0
    } else {
1372
0
        return Status::InvalidArgument("Unknown rerun fragment opcode: {}", stage);
1373
0
    }
1374
2
}
1375
1376
Status FragmentMgr::reset_global_rf(const TUniqueId& query_id,
1377
2.04k
                                    const google::protobuf::RepeatedField<int32_t>& filter_ids) {
1378
2.04k
    if (auto q_ctx = get_query_ctx(query_id)) {
1379
2.04k
        SCOPED_ATTACH_TASK(q_ctx.get());
1380
2.04k
        return q_ctx->reset_global_rf(filter_ids);
1381
2.04k
    } else {
1382
0
        return Status::NotFound(
1383
0
                "reset_fragment: Query context (query-id: {}) not found, maybe finished",
1384
0
                print_id(query_id));
1385
0
    }
1386
2.04k
}
1387
1388
} // namespace doris