This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new d8d8152183b [branch-4.1](cherry-pick) cancel message is printed too
many times (#67548)
d8d8152183b is described below
commit d8d8152183b81caa4e94aa79908f7b62bde15b92
Author: yiguolei <[email protected]>
AuthorDate: Mon Sep 7 09:15:36 2026 +0800
[branch-4.1](cherry-pick) cancel message is printed too many times (#67548)
### What problem does this PR solve?
Issue Number: close #xxx
pick #56822 #67344
Problem Summary:
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [ ] Regression test
- [ ] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [ ] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] No.
- [ ] Yes. <!-- Add document PR link here. eg:
https://github.com/apache/doris-website/pull/1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->
---------
Co-authored-by: Gabriel <[email protected]>
---
be/src/exec/pipeline/dependency.cpp | 4 +--
be/src/exec/pipeline/pipeline_fragment_context.cpp | 15 ++++-----
be/src/exec/pipeline/pipeline_fragment_context.h | 10 ++++--
be/src/exec/pipeline/pipeline_task.cpp | 2 +-
be/src/exec/pipeline/pipeline_task.h | 4 +--
be/src/exec/pipeline/task_scheduler.cpp | 10 ++++--
be/src/runtime/fragment_mgr.cpp | 4 +--
be/src/runtime/query_context.cpp | 36 ++++++++++------------
be/src/runtime/query_context.h | 5 +--
.../workload_management/query_task_controller.cpp | 4 +--
.../workload_management/query_task_controller.h | 3 +-
11 files changed, 52 insertions(+), 45 deletions(-)
diff --git a/be/src/exec/pipeline/dependency.cpp
b/be/src/exec/pipeline/dependency.cpp
index 014f3182183..c8f9d3d3ba2 100644
--- a/be/src/exec/pipeline/dependency.cpp
+++ b/be/src/exec/pipeline/dependency.cpp
@@ -79,7 +79,7 @@ void Dependency::set_ready() {
for (auto task : local_block_task) {
if (auto t = task.lock()) {
std::unique_lock<std::mutex> lc(_task_lock);
- THROW_IF_ERROR(t->wake_up(this));
+ THROW_IF_ERROR(t->wake_up(this, lc));
}
}
}
@@ -90,7 +90,7 @@ Dependency*
Dependency::is_blocked_by(std::shared_ptr<PipelineTask> task) {
if (!ready && task) {
_add_block_task(task);
start_watcher();
- THROW_IF_ERROR(task->blocked(this));
+ THROW_IF_ERROR(task->blocked(this, lc));
}
return ready ? nullptr : this;
}
diff --git a/be/src/exec/pipeline/pipeline_fragment_context.cpp
b/be/src/exec/pipeline/pipeline_fragment_context.cpp
index 4193927e395..06b617b8339 100644
--- a/be/src/exec/pipeline/pipeline_fragment_context.cpp
+++ b/be/src/exec/pipeline/pipeline_fragment_context.cpp
@@ -203,10 +203,10 @@ bool PipelineFragmentContext::notify_close() {
return all_closed;
}
-// Must not add lock in this method. Because it will call query ctx cancel. And
-// QueryCtx cancel will call fragment ctx cancel. And Also Fragment ctx's
running
-// Method like exchange sink buffer will call query ctx cancel. If we add lock
here
-// There maybe dead lock.
+// QueryContext is the sole cancellation entry point and invokes this method
to apply the accepted
+// cancellation to this fragment. Do not call QueryContext::cancel() from
here: doing so would make
+// cancellation bidirectional and allow every task closed with the query error
to repeat the whole
+// fragment's timeout diagnostics and dependency-unblocking work.
void PipelineFragmentContext::cancel(const Status reason) {
LOG_INFO("PipelineFragmentContext::cancel")
.tag("query_id", print_id(_query_id))
@@ -240,7 +240,6 @@ void PipelineFragmentContext::cancel(const Status reason) {
_query_ctx->set_first_error_msg(first_error_msg);
}
- _query_ctx->cancel(reason, _fragment_id);
if (!reason.is<ErrorCode::LIMIT_REACH>() &&
!reason.is<ErrorCode::FINISHED>()) {
for (auto& id : _fragment_instance_ids) {
LOG(WARNING) << "PipelineFragmentContext cancel instance: " <<
print_id(id);
@@ -1826,7 +1825,7 @@ Status PipelineFragmentContext::submit() {
DBUG_EXECUTE_IF("PipelineFragmentContext.submit.failed",
{ st =
Status::Aborted("PipelineFragmentContext.submit.failed"); });
if (!st) {
- cancel(Status::InternalError("submit context to executor
fail"));
+ _query_ctx->cancel(Status::InternalError("submit context to
executor fail"));
std::lock_guard<std::mutex> l(_task_mutex);
_total_tasks = submit_tasks;
break;
@@ -2287,7 +2286,9 @@ Status PipelineFragmentContext::send_report(bool done) {
.runtime_state = _runtime_state.get(),
.load_error_url = load_eror_url,
.first_error_msg = first_error_msg,
- .cancel_fn = [this](const Status& reason) {
cancel(reason); }};
+ .cancel_fn = [query_ctx = _query_ctx](const
Status& reason) {
+ query_ctx->cancel(reason);
+ }};
auto ctx =
std::dynamic_pointer_cast<PipelineFragmentContext>(shared_from_this());
return _exec_env->fragment_mgr()->get_thread_pool()->submit_func([this,
req, ctx]() {
SCOPED_ATTACH_TASK(ctx->get_query_ctx()->query_mem_tracker());
diff --git a/be/src/exec/pipeline/pipeline_fragment_context.h
b/be/src/exec/pipeline/pipeline_fragment_context.h
index f8a1bfea229..f5cb2fc22f9 100644
--- a/be/src/exec/pipeline/pipeline_fragment_context.h
+++ b/be/src/exec/pipeline/pipeline_fragment_context.h
@@ -81,8 +81,6 @@ public:
void set_is_report_success(bool is_report_success) { _is_report_success =
is_report_success; }
- void cancel(const Status reason);
-
bool notify_close();
TUniqueId get_query_id() const { return _query_id; }
@@ -149,6 +147,14 @@ public:
}
private:
+ // QueryContext is the sole entry point for query cancellation. Keep
fragment cancellation
+ // private so callers cannot bypass QueryContext's first-error-wins guard
and repeatedly run
+ // expensive fragment-local cleanup (for example, timeout diagnostics and
task unblocking).
+ // QueryContext::cancel() calls this method only to propagate the accepted
query cancellation
+ // to each fragment; this method must not call QueryContext::cancel() back.
+ friend void QueryContext::cancel(Status new_status);
+ void cancel(const Status reason);
+
void _coordinator_callback(const ReportStatusRequest& req);
std::string _to_http_path(const std::string& file_name) const;
diff --git a/be/src/exec/pipeline/pipeline_task.cpp
b/be/src/exec/pipeline/pipeline_task.cpp
index c898024c915..b364eaee3a7 100644
--- a/be/src/exec/pipeline/pipeline_task.cpp
+++ b/be/src/exec/pipeline/pipeline_task.cpp
@@ -1055,7 +1055,7 @@ Status PipelineTask::revoke_memory(const
std::shared_ptr<SpillContext>& spill_co
return Status::OK();
}
-Status PipelineTask::wake_up(Dependency* dep) {
+Status PipelineTask::wake_up(Dependency* dep, std::unique_lock<std::mutex>& /*
dep_lock */) {
// call by dependency
DCHECK_EQ(_blocked_dep, dep) << "dep : " << dep->debug_string(0) << "task:
" << debug_string();
_blocked_dep = nullptr;
diff --git a/be/src/exec/pipeline/pipeline_task.h
b/be/src/exec/pipeline/pipeline_task.h
index 68d2195e559..5b5da9dca83 100644
--- a/be/src/exec/pipeline/pipeline_task.h
+++ b/be/src/exec/pipeline/pipeline_task.h
@@ -119,7 +119,7 @@ public:
return _op_shared_states[id].get();
}
- Status wake_up(Dependency* dep);
+ Status wake_up(Dependency* dep, std::unique_lock<std::mutex>& /* dep_lock
*/);
DataSinkOperatorPtr sink() const { return _sink; }
@@ -173,7 +173,7 @@ public:
[[nodiscard]] size_t get_revocable_size() const;
[[nodiscard]] Status revoke_memory(const std::shared_ptr<SpillContext>&
spill_context);
- Status blocked(Dependency* dependency) {
+ Status blocked(Dependency* dependency, std::unique_lock<std::mutex>& /*
dep_lock */) {
DCHECK_EQ(_blocked_dep, nullptr) << "task: " << debug_string();
_blocked_dep = dependency;
return _state_transition(PipelineTask::State::BLOCKED);
diff --git a/be/src/exec/pipeline/task_scheduler.cpp
b/be/src/exec/pipeline/task_scheduler.cpp
index 1a052fc1ffa..3a1274defcb 100644
--- a/be/src/exec/pipeline/task_scheduler.cpp
+++ b/be/src/exec/pipeline/task_scheduler.cpp
@@ -81,17 +81,21 @@ void close_task(PipelineTask* task, Status exec_status,
PipelineFragmentContext*
// task finished.
SCOPED_ATTACH_TASK(task->runtime_state());
if (!exec_status.ok()) {
- ctx->cancel(exec_status);
+ // Always enter cancellation through QueryContext. The status passed
while closing a task
+ // may be the query's existing cancellation status rather than a new
task failure; the
+ // QueryContext first-error-wins guard makes that case a no-op and
prevents repeated
+ // fragment-local cancellation work.
+ ctx->get_query_ctx()->cancel(exec_status);
LOG(WARNING) << fmt::format("Pipeline task failed. query_id: {}
reason: {}",
print_id(ctx->get_query_id()),
exec_status.to_string());
}
Status status = task->close(exec_status);
if (!status.ok()) {
- ctx->cancel(status);
+ ctx->get_query_ctx()->cancel(status);
}
status = task->finalize();
if (!status.ok()) {
- ctx->cancel(status);
+ ctx->get_query_ctx()->cancel(status);
}
}
diff --git a/be/src/runtime/fragment_mgr.cpp b/be/src/runtime/fragment_mgr.cpp
index c8efc4b8ed5..55aad40baac 100644
--- a/be/src/runtime/fragment_mgr.cpp
+++ b/be/src/runtime/fragment_mgr.cpp
@@ -655,7 +655,7 @@ Status FragmentMgr::exec_plan_fragment(const
TPipelineFragmentParams& params,
prepare_st =
Status::Aborted("FragmentMgr.exec_plan_fragment.prepare_failed");
});
if (!prepare_st.ok()) {
- query_ctx->cancel(prepare_st, params.fragment_id);
+ query_ctx->cancel(prepare_st);
return prepare_st;
}
}
@@ -1322,7 +1322,7 @@ Status FragmentMgr::rerun_fragment(const
std::shared_ptr<brpc::ClosureGuard>& gu
ASSIGN_STATUS_IF_CATCH_EXCEPTION(prepare_st =
context->prepare(_thread_pool.get()),
prepare_st);
if (!prepare_st.ok()) {
- q_ctx->cancel(prepare_st, info.params.fragment_id);
+ q_ctx->cancel(prepare_st);
return prepare_st;
}
diff --git a/be/src/runtime/query_context.cpp b/be/src/runtime/query_context.cpp
index aaab0ad698c..c5e9b277093 100644
--- a/be/src/runtime/query_context.cpp
+++ b/be/src/runtime/query_context.cpp
@@ -315,7 +315,7 @@ void QueryContext::set_memory_sufficient(bool sufficient) {
}
}
-void QueryContext::cancel(Status new_status, int fragment_id) {
+void QueryContext::cancel(Status new_status) {
if (!_exec_status.update(new_status)) {
return;
}
@@ -353,7 +353,21 @@ void QueryContext::cancel(Status new_status, int
fragment_id) {
}
set_ready_to_execute(new_status);
- cancel_all_pipeline_context(new_status, fragment_id);
+
+ // Copy the fragment contexts under the map lock, then cancel them after
releasing it. Fragment
+ // cancellation may take task-level locks and must not run while holding
the query map lock.
+ std::vector<std::weak_ptr<PipelineFragmentContext>> ctx_to_cancel;
+ {
+ std::lock_guard<std::mutex> lock(_pipeline_map_write_lock);
+ for (auto& entry : _fragment_id_to_pipeline_ctx) {
+ ctx_to_cancel.push_back(entry.second);
+ }
+ }
+ for (auto& f_context : ctx_to_cancel) {
+ if (auto pipeline_ctx = f_context.lock()) {
+ pipeline_ctx->cancel(new_status);
+ }
+ }
}
void QueryContext::set_load_error_url(std::string error_url) {
@@ -376,24 +390,6 @@ std::string QueryContext::get_first_error_msg() {
return _first_error_msg;
}
-void QueryContext::cancel_all_pipeline_context(const Status& reason, int
fragment_id) {
- std::vector<std::weak_ptr<PipelineFragmentContext>> ctx_to_cancel;
- {
- std::lock_guard<std::mutex> lock(_pipeline_map_write_lock);
- for (auto& [f_id, f_context] : _fragment_id_to_pipeline_ctx) {
- if (fragment_id == f_id) {
- continue;
- }
- ctx_to_cancel.push_back(f_context);
- }
- }
- for (auto& f_context : ctx_to_cancel) {
- if (auto pipeline_ctx = f_context.lock()) {
- pipeline_ctx->cancel(reason);
- }
- }
-}
-
std::string QueryContext::print_all_pipeline_context() {
std::vector<std::weak_ptr<PipelineFragmentContext>> ctx_to_print;
fmt::memory_buffer debug_string_buffer;
diff --git a/be/src/runtime/query_context.h b/be/src/runtime/query_context.h
index e0368def54e..de6b416cf14 100644
--- a/be/src/runtime/query_context.h
+++ b/be/src/runtime/query_context.h
@@ -135,11 +135,12 @@ public:
[[nodiscard]] bool is_cancelled() const { return !_exec_status.ok(); }
- void cancel_all_pipeline_context(const Status& reason, int fragment_id =
-1);
std::string print_all_pipeline_context();
void set_pipeline_context(const int fragment_id,
std::shared_ptr<PipelineFragmentContext>
pip_ctx);
- void cancel(Status new_status, int fragment_id = -1);
+ // The sole entry point for query cancellation. Only the first error is
accepted; it is then
+ // propagated to every PipelineFragmentContext for fragment-local cleanup.
+ void cancel(Status new_status);
[[nodiscard]] Status exec_status() { return _exec_status.status(); }
diff --git a/be/src/runtime/workload_management/query_task_controller.cpp
b/be/src/runtime/workload_management/query_task_controller.cpp
index de6b3e8a24f..8708429b2bd 100644
--- a/be/src/runtime/workload_management/query_task_controller.cpp
+++ b/be/src/runtime/workload_management/query_task_controller.cpp
@@ -39,12 +39,12 @@ bool QueryTaskController::is_cancelled() const {
return query_ctx->is_cancelled();
}
-bool QueryTaskController::cancel_impl(const Status& reason, int fragment_id) {
+bool QueryTaskController::cancel_impl(const Status& reason) {
auto query_ctx = query_ctx_.lock();
if (query_ctx == nullptr) {
return false;
}
- query_ctx->cancel(reason, fragment_id);
+ query_ctx->cancel(reason);
return true;
}
diff --git a/be/src/runtime/workload_management/query_task_controller.h
b/be/src/runtime/workload_management/query_task_controller.h
index 854a2c05cb6..7fe354681b8 100644
--- a/be/src/runtime/workload_management/query_task_controller.h
+++ b/be/src/runtime/workload_management/query_task_controller.h
@@ -35,8 +35,7 @@ public:
~QueryTaskController() override = default;
bool is_cancelled() const override;
- bool cancel_impl(const Status& reason, int fragment_id);
- bool cancel_impl(const Status& reason) override { return
cancel_impl(reason, -1); }
+ bool cancel_impl(const Status& reason) override;
bool is_pure_load_task() const override;
int32_t get_slot_count() const override;
void disable_reserve_memory() override;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]