Skip to content

Commit f793bb1

Browse files
sevevmergify[bot]
authored andcommitted
[BugFix] Fix scan-teardown use-after-free on MorselQueueFactory (#76259)
Signed-off-by: sevev <qiangzh95@gmail.com> (cherry picked from commit c613e0f) # Conflicts: # be/src/exec/runtime/fragment_context.h # be/test/exec/pipeline_fragment_context_exec_runtime_test.cpp
1 parent 088cbee commit f793bb1

2 files changed

Lines changed: 471 additions & 0 deletions

File tree

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
// Copyright 2021-present StarRocks, Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <atomic>
18+
#include <functional>
19+
#include <map>
20+
#include <memory>
21+
#include <memory_resource>
22+
#include <unordered_map>
23+
#include <utility>
24+
#include <vector>
25+
26+
#include "base/hash/hash_std.hpp"
27+
#include "base/uid_util.h"
28+
#include "compute_env/pipeline/driver_limiter.h"
29+
#include "compute_env/pipeline/pipeline_timer_context.h"
30+
#include "compute_env/query/fragment_runtime_state.h"
31+
#include "compute_env/query_cache/cache_param.h"
32+
#include "compute_env/workgroup/work_group_fwd.h"
33+
#include "exec/runtime/adaptive/adaptive_dop_param.h"
34+
#include "exec/runtime/group_execution/execution_group_fwd.h"
35+
#include "exec_primitive/exec_node.h"
36+
#include "exec_primitive/pipeline/pipeline_fwd.h"
37+
#include "exec_primitive/pipeline/primitives/execution_group_lifecycle.h"
38+
#include "exec_primitive/pipeline/primitives/fragment_lifecycle.h"
39+
#include "exec_primitive/pipeline/runtime_filter_hub.h"
40+
#include "exec_primitive/pipeline/scan/morsel_queue_factory_base.h"
41+
#include "gen_cpp/FrontendService.h"
42+
#include "gen_cpp/HeartbeatService.h"
43+
#include "gen_cpp/InternalService_types.h"
44+
#include "gen_cpp/PlanNodes_types.h"
45+
#include "gen_cpp/QueryPlanExtra_types.h"
46+
#include "gen_cpp/Types_types.h"
47+
#include "runtime/runtime_state.h"
48+
#include "storage_primitive/predicate_tree_params.h"
49+
50+
namespace starrocks {
51+
52+
class FragmentAttachment;
53+
class FragmentDictState;
54+
class RuntimeFilterPort;
55+
56+
namespace pipeline {
57+
58+
class PassThroughChunkBufferGuard;
59+
60+
using RuntimeFilterPort = starrocks::RuntimeFilterPort;
61+
using PerDriverScanRangesMap = std::map<int32_t, std::vector<TScanRangeParams>>;
62+
63+
class FragmentContext : public ExecutionGroupLifecycle {
64+
friend class FragmentContextManager;
65+
66+
public:
67+
FragmentContext();
68+
~FragmentContext();
69+
70+
// Fragment-level shared MemPool — delegates to RuntimeState which owns it.
71+
MemPool* fragment_mem_pool() { return _runtime_state ? _runtime_state->fragment_mem_pool() : nullptr; }
72+
73+
// PMR memory resource — delegates to RuntimeState which owns it.
74+
std::pmr::memory_resource* mem_resource() { return _runtime_state ? _runtime_state->mem_resource() : nullptr; }
75+
76+
const TUniqueId& query_id() const { return _query_id; }
77+
void set_query_id(const TUniqueId& query_id) { _query_id = query_id; }
78+
const TUniqueId& fragment_instance_id() const { return _fragment_runtime_state.fragment_instance_id(); }
79+
void set_fragment_instance_id(const TUniqueId& fragment_instance_id) {
80+
_fragment_runtime_state.set_fragment_instance_id(fragment_instance_id);
81+
}
82+
FragmentRuntimeState& fragment_runtime_state() { return _fragment_runtime_state; }
83+
const FragmentRuntimeState& fragment_runtime_state() const { return _fragment_runtime_state; }
84+
void set_fe_addr(const TNetworkAddress& fe_addr) { _fragment_runtime_state.set_fe_addr(fe_addr); }
85+
const TNetworkAddress& fe_addr() const { return _fragment_runtime_state.fe_addr(); }
86+
FragmentFuture finish_future() { return _finish_promise.get_future(); }
87+
RuntimeState* runtime_state() const { return _runtime_state.get(); }
88+
std::shared_ptr<RuntimeState> runtime_state_ptr() { return _runtime_state; }
89+
void set_runtime_state(std::shared_ptr<RuntimeState>&& runtime_state) { _runtime_state = std::move(runtime_state); }
90+
void attach_to_runtime_state(RuntimeState* state);
91+
FragmentDictState* dict_state() const { return _fragment_dict_state.get(); }
92+
ExecNode*& plan() { return _plan; }
93+
94+
void move_tplan(TPlan& tplan);
95+
const TPlan& tplan() const { return _tplan; }
96+
void set_data_sink(std::unique_ptr<DataSink> data_sink);
97+
98+
size_t total_dop() const;
99+
100+
bool all_execution_groups_finished() const { return _num_finished_execution_groups == _execution_groups.size(); }
101+
void count_down_execution_group(size_t val = 1);
102+
void on_execution_group_finished() override { count_down_execution_group(); }
103+
void set_fragment_lifecycle(FragmentLifecycleWeakPtr lifecycle) { _fragment_lifecycle = std::move(lifecycle); }
104+
105+
bool need_report_exec_state();
106+
void report_exec_state_if_necessary();
107+
108+
void set_final_status(const Status& status);
109+
110+
Status final_status() const { return _fragment_runtime_state.final_status(); }
111+
112+
void cancel(const Status& status);
113+
114+
void finish() { cancel(Status::OK()); }
115+
116+
MorselQueueFactoryMap& morsel_queue_factories() { return _morsel_queue_factories; }
117+
118+
void set_pipelines(ExecutionGroups&& exec_groups, Pipelines&& pipelines);
119+
120+
Status prepare_all_pipelines();
121+
122+
void iterate_drivers(const std::function<void(const DriverPtr&)>& call);
123+
124+
void clear_all_drivers();
125+
void close_all_execution_groups();
126+
127+
RuntimeFilterHub* runtime_filter_hub() { return _fragment_runtime_state.runtime_filter_hub(); }
128+
const RuntimeFilterHub* runtime_filter_hub() const { return _fragment_runtime_state.runtime_filter_hub(); }
129+
130+
RuntimeFilterPort* runtime_filter_port() { return _runtime_state->runtime_filter_port(); }
131+
132+
void prepare_pass_through_chunk_buffer();
133+
void destroy_pass_through_chunk_buffer();
134+
135+
void set_driver_token(DriverLimiter::TokenPtr driver_token) { _driver_token = std::move(driver_token); }
136+
Status set_pipeline_timer(PipelineTimer* pipeline_timer, std::shared_ptr<PipelineTimerTask> timeout_task);
137+
void clear_pipeline_timer();
138+
PipelineTimerContextPtr pipeline_timer_context() const { return _pipeline_timer_context; }
139+
140+
query_cache::CacheParam& cache_param() { return _cache_param; }
141+
142+
void set_enable_cache(bool flag) { _fragment_runtime_state.set_enable_cache(flag); }
143+
144+
bool enable_cache() const { return _fragment_runtime_state.enable_cache(); }
145+
146+
void set_fragment_attachments(std::vector<std::unique_ptr<FragmentAttachment>>&& attachments);
147+
148+
void set_enable_adaptive_dop(bool val) { _fragment_runtime_state.set_enable_adaptive_dop(val); }
149+
bool enable_adaptive_dop() const { return _fragment_runtime_state.enable_adaptive_dop(); }
150+
AdaptiveDopParam& adaptive_dop_param() { return _adaptive_dop_param; }
151+
152+
const PredicateTreeParams& pred_tree_params() const { return _fragment_runtime_state.pred_tree_params(); }
153+
void set_pred_tree_params(const PredicateTreeParams& params) {
154+
_fragment_runtime_state.set_pred_tree_params(params);
155+
}
156+
157+
size_t next_driver_id() { return _next_driver_id++; }
158+
159+
void set_workgroup(workgroup::WorkGroupPtr wg) { _fragment_runtime_state.set_workgroup(std::move(wg)); }
160+
const workgroup::WorkGroupPtr& workgroup() const { return _fragment_runtime_state.workgroup(); }
161+
bool enable_resource_group() const { return workgroup() != nullptr; }
162+
TQueryType::type query_type() const;
163+
164+
size_t expired_log_count() const { return _expired_log_count; }
165+
166+
void set_expired_log_count(size_t val) { _expired_log_count = val; }
167+
168+
void iterate_pipeline(const std::function<void(Pipeline*)>& call);
169+
Status iterate_pipeline(const std::function<Status(Pipeline*)>& call);
170+
171+
Status prepare_active_drivers();
172+
Status submit_active_drivers(DriverExecutor* executor);
173+
174+
bool enable_group_execution() const { return _enable_group_execution; }
175+
void set_enable_group_execution(bool enable_group_execution) { _enable_group_execution = enable_group_execution; }
176+
177+
void set_report_when_finish(bool report) { _report_when_finish = report; }
178+
179+
// acquire runtime filter from cache
180+
void acquire_runtime_filters();
181+
182+
bool enable_event_scheduler() const { return event_scheduler() != nullptr; }
183+
EventScheduler* event_scheduler() const { return _event_scheduler.get(); }
184+
void init_event_scheduler();
185+
186+
private:
187+
void _close_fragment_attachments();
188+
189+
bool _enable_group_execution = false;
190+
// Id of this query
191+
TUniqueId _query_id;
192+
// Id of this instance
193+
FragmentRuntimeState _fragment_runtime_state;
194+
195+
// Hold tplan data datasink from delivery request to create driver lazily
196+
// after delivery request has been finished.
197+
TPlan _tplan;
198+
std::unique_ptr<DataSink> _data_sink;
199+
200+
// promise used to determine whether fragment finished its execution
201+
FragmentPromise _finish_promise;
202+
203+
// never adjust the order of _runtime_state, _plan, _pipelines and _drivers, since
204+
// _plan depends on _runtime_state and _drivers depends on _runtime_state.
205+
std::shared_ptr<RuntimeState> _runtime_state = nullptr;
206+
std::unique_ptr<FragmentDictState> _fragment_dict_state;
207+
ExecNode* _plan = nullptr; // lives in _runtime_state->obj_pool()
208+
size_t _next_driver_id = 0;
209+
// MorselQueueFactory must outlive the pipelines/operators declared below: a
210+
// scan operator's ConnectorChunkSource::close() is invoked from ~ScanOperator
211+
// during _pipelines teardown and calls back into the MorselQueueFactory via
212+
// ScanOperatorFactory::morsel_queue_factory(). Declaring _morsel_queue_factories
213+
// before _pipelines makes it destroyed after them, avoiding a use-after-free on
214+
// non-EOF (cancel/error/limit) teardown paths.
215+
MorselQueueFactoryMap _morsel_queue_factories;
216+
// Must outlive PipelineDriver observers owned by _pipelines.
217+
std::unique_ptr<EventScheduler> _event_scheduler;
218+
Pipelines _pipelines;
219+
ExecutionGroups _execution_groups;
220+
std::atomic<size_t> _num_finished_execution_groups = 0;
221+
FragmentLifecycleWeakPtr _fragment_lifecycle;
222+
223+
PipelineTimerContextPtr _pipeline_timer_context = nullptr;
224+
std::shared_ptr<PipelineTimerTask> _timeout_task = nullptr;
225+
std::shared_ptr<PipelineTimerTask> _report_state_task = nullptr;
226+
227+
DriverLimiter::TokenPtr _driver_token = nullptr;
228+
229+
std::unique_ptr<PassThroughChunkBufferGuard> _pass_through_chunk_buffer_guard;
230+
231+
query_cache::CacheParam _cache_param;
232+
std::vector<std::unique_ptr<FragmentAttachment>> _fragment_attachments;
233+
234+
AdaptiveDopParam _adaptive_dop_param;
235+
236+
size_t _expired_log_count = 0;
237+
238+
bool _report_when_finish{};
239+
};
240+
} // namespace pipeline
241+
} // namespace starrocks

0 commit comments

Comments
 (0)