This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 8134f160240 branch-4.2: [improvement](be) Use thread pool scan
scheduler by default #68610 (#69508)
8134f160240 is described below
commit 8134f1602407c04e3a43bb89a7b91ab09762ab7f
Author: Jerry Hu <[email protected]>
AuthorDate: Sat Oct 24 20:53:15 2026 +0800
branch-4.2: [improvement](be) Use thread pool scan scheduler by default
#68610 (#69508)
## Summary
- Use the existing thread-pool scheduler by default for internal and
external scans. The task executor remains available through the
corresponding explicit configuration options.
- Convert Doris exceptions raised during row-ID V2 fetch RPC processing
on a thread-pool worker into an error status in the RPC response.
## Backport scope
Cherry-picked from #68610 for branch-4.2. This branch does not have the
master-side shared scan LIMIT counter or the associated scanner/context
APIs, so its existing scanner behavior is retained. The backport
includes the scheduler defaults, the row-ID RPC exception boundary, and
focused tests without importing unrelated master-side scan changes.
## Tests
- ASAN BE and FE build: `BUILD_TYPE=ASAN ./build.sh --be --fe -j 32` —
passed.
- Focused BE unit tests covering row-ID fetch, external scan task
submission, workload-group schedulers, scanner context, and scanner
projection — 82 passed.
- `query_p0/limit` regression on an isolated local FE/BE cluster — 18
suites passed.
- `clang-format.sh`, `check-format.sh`, and `git diff --check` — passed.
## Release note
Internal and external scans now use the thread-pool scheduler by
default. Set `enable_task_executor_in_internal_table` or
`enable_task_executor_in_external_table` to `true` to opt back into the
task executor for the respective scan type.
---
be/src/common/config.cpp | 4 +-
be/src/exec/rowid_fetcher.cpp | 7 +++
be/src/exec/rowid_fetcher.h | 4 ++
be/src/runtime/exec_env.h | 1 +
be/src/service/internal_service.cpp | 4 +-
be/test/exec/rowid_fetcher_test.cpp | 73 ++++++++++++++++++++++
.../workload_group/workload_group_manager_test.cpp | 57 +++++++++++++++++
7 files changed, 145 insertions(+), 5 deletions(-)
diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp
index 62476f51bc0..8c1c0f62a01 100644
--- a/be/src/common/config.cpp
+++ b/be/src/common/config.cpp
@@ -330,9 +330,9 @@ DEFINE_Int32(task_executor_max_concurrency_per_task, "-1");
DEFINE_Int32(task_executor_initial_max_concurrency_per_task, "-1");
// Enable task executor in internal table scan.
-DEFINE_Bool(enable_task_executor_in_internal_table, "true");
+DEFINE_Bool(enable_task_executor_in_internal_table, "false");
// Enable task executor in external table scan.
-DEFINE_Bool(enable_task_executor_in_external_table, "true");
+DEFINE_Bool(enable_task_executor_in_external_table, "false");
// number of scanner thread pool size for olap table
// and the min thread num of remote scanner thread pool
diff --git a/be/src/exec/rowid_fetcher.cpp b/be/src/exec/rowid_fetcher.cpp
index 5fc288dd301..32de6d7dfd5 100644
--- a/be/src/exec/rowid_fetcher.cpp
+++ b/be/src/exec/rowid_fetcher.cpp
@@ -559,6 +559,13 @@ Status RowIdStorageReader::read_by_rowids(const
PMultiGetRequest& request,
return Status::OK();
}
+void RowIdStorageReader::read_by_rowids_for_rpc(const PMultiGetRequestV2&
request,
+ PMultiGetResponseV2* response)
{
+ Status st = Status::OK();
+ ASSIGN_STATUS_IF_CATCH_EXCEPTION(st = read_by_rowids(request, response),
st);
+ st.to_protobuf(response->mutable_status());
+}
+
Status RowIdStorageReader::read_by_rowids(const PMultiGetRequestV2& request,
PMultiGetResponseV2* response) {
if (request.request_block_descs_size()) {
diff --git a/be/src/exec/rowid_fetcher.h b/be/src/exec/rowid_fetcher.h
index ca1f8b50690..f4c400f0b07 100644
--- a/be/src/exec/rowid_fetcher.h
+++ b/be/src/exec/rowid_fetcher.h
@@ -127,6 +127,10 @@ public:
static Status read_by_rowids(const PMultiGetRequest& request,
PMultiGetResponse* response);
static Status read_by_rowids(const PMultiGetRequestV2& request,
PMultiGetResponseV2* response);
+ // ThreadPool scan workers have no exception boundary; keep RPC failures
in the response.
+ static void read_by_rowids_for_rpc(const PMultiGetRequestV2& request,
+ PMultiGetResponseV2* response);
+
static bool should_use_file_scanner_v2(const TQueryOptions& query_options,
const TFileScanRangeParams&
scan_params,
const TFileRangeDesc& range);
diff --git a/be/src/runtime/exec_env.h b/be/src/runtime/exec_env.h
index 69160d88f42..97424165056 100644
--- a/be/src/runtime/exec_env.h
+++ b/be/src/runtime/exec_env.h
@@ -339,6 +339,7 @@ public:
void set_cache_manager(CacheManager* cm) { this->_cache_manager = cm; }
void set_process_profile(ProcessProfile* pp) { this->_process_profile =
pp; }
void set_tablet_schema_cache(TabletSchemaCache* c) {
this->_tablet_schema_cache = c; }
+ void set_id_manager(IdManager* id_manager) { this->_id_manager =
id_manager; }
void set_delete_bitmap_agg_cache(DeleteBitmapAggCache* c) {
_delete_bitmap_agg_cache = c; }
void set_tablet_column_object_pool(TabletColumnObjectPool* c) {
this->_tablet_column_object_pool = c;
diff --git a/be/src/service/internal_service.cpp
b/be/src/service/internal_service.cpp
index 3f8719acce7..89d7e0de834 100644
--- a/be/src/service/internal_service.cpp
+++ b/be/src/service/internal_service.cpp
@@ -2335,9 +2335,7 @@ void
PInternalService::multiget_data_v2(google::protobuf::RpcController* control
MonotonicStopWatch watch;
watch.start();
brpc::ClosureGuard closure_guard(done);
- response->mutable_status()->set_status_code(0);
- Status st =
RowIdStorageReader::read_by_rowids(*request, response);
- st.to_protobuf(response->mutable_status());
+ RowIdStorageReader::read_by_rowids_for_rpc(*request,
response);
LOG(INFO) << "multiget_data finished, cost(us):"
<< watch.elapsed_time() / 1000;
return true;
diff --git a/be/test/exec/rowid_fetcher_test.cpp
b/be/test/exec/rowid_fetcher_test.cpp
index 3e8fd3e9517..e4246c4974f 100644
--- a/be/test/exec/rowid_fetcher_test.cpp
+++ b/be/test/exec/rowid_fetcher_test.cpp
@@ -19,17 +19,27 @@
#include <gtest/gtest.h>
+#include <chrono>
+#include <memory>
#include <string>
#include <vector>
#include "common/consts.h"
+#include "common/exception.h"
#include "exec/operator/file_scan_operator.h"
#include "exec/scan/file_scanner_v2.h"
+#include "exec/scan/scanner_scheduler.h"
#include "format_v2/column_mapper.h"
#include "format_v2/table/hive_reader.h"
+#include "gen_cpp/internal_service.pb.h"
#include "runtime/descriptor_helper.h"
#include "runtime/descriptors.h"
+#include "runtime/exec_env.h"
#include "runtime/runtime_state.h"
+#include "storage/id_manager.h"
+#include "storage/olap_common.h"
+#include "util/countdown_latch.h"
+#include "util/defer_op.h"
namespace doris {
@@ -423,4 +433,67 @@ TEST_F(SubmitExternalScanTasksTest,
ThrownExceptionReachesTheCaller) {
EXPECT_NE(result.to_string().find("scanner threw"), std::string::npos);
}
+// multiget_data_v2 runs read_by_rowids() on the remote scan scheduler, which
by default is a
+// ThreadPool whose worker has no exception boundary. A request whose file
mapping exists but
+// whose slot carries a PTypeNode this BE cannot decode makes SlotDescriptor's
constructor throw;
+// the RPC helper must turn that into an error status on the response instead
of letting the
+// exception leave the worker and terminate the process.
+TEST_F(RowIdStorageReaderTest,
RpcHelperReportsThrownExceptionOnThreadPoolPath) {
+ IdManager id_manager;
+ ExecEnv* env = ExecEnv::GetInstance();
+ IdManager* saved_id_manager = env->get_id_manager();
+ env->set_id_manager(&id_manager);
+ Defer restore_id_manager {[&] { env->set_id_manager(saved_id_manager); }};
+
+ PMultiGetRequestV2 request;
+ request.mutable_query_id()->set_hi(68610);
+ request.mutable_query_id()->set_lo(1);
+ request.set_wg_id(1);
+ auto id_file_map = id_manager.add_id_file_map(request.query_id(), 1024);
+ const uint32_t file_id = id_file_map->get_file_mapping_id(
+ std::make_shared<FileMapping>(/*tablet_id=*/1, RowsetId(),
/*segment_id=*/0));
+
+ PRequestBlockDesc* block_desc = request.add_request_block_descs();
+ block_desc->add_file_id(file_id);
+ block_desc->add_row_id(0);
+ PSlotDescriptor* slot = block_desc->add_slots();
+ slot->set_id(0);
+ slot->set_parent(0);
+ slot->set_column_pos(0);
+ slot->set_byte_offset(0);
+ slot->set_null_indicator_byte(0);
+ slot->set_null_indicator_bit(-1);
+ slot->set_col_name("c");
+ slot->set_slot_idx(0);
+ // A type node kind unknown to this BE, as a newer FE could send.
+ slot->mutable_slot_type()->add_types()->set_type(99);
+
+ // The worker captures the response and the latch by reference. Declare
them before the
+ // scheduler so every exit, including a failed wait below, joins the
worker in stop_scheduler
+ // before either captured object is destroyed.
+ PMultiGetResponseV2 response;
+ CountDownLatch finished(1);
+ std::shared_ptr<CgroupCpuCtl> cgroup_cpu_ctl =
std::make_shared<CgroupV2CpuCtl>(1);
+ ThreadPoolSimplifiedScanScheduler scheduler("rowid_rpc_test",
cgroup_cpu_ctl);
+ ASSERT_TRUE(scheduler.start(1, 1, 4, 1).ok());
+ Defer stop_scheduler {[&] { scheduler.stop(); }};
+
+ ASSERT_TRUE(scheduler
+ .submit_scan_task(SimplifiedScanTask(
+ [&]() {
+
RowIdStorageReader::read_by_rowids_for_rpc(
+ request,
&response);
+ finished.count_down();
+ return true;
+ },
+ nullptr, nullptr),
+ "rowid_rpc_test_task")
+ .ok());
+ ASSERT_TRUE(finished.wait_for(std::chrono::seconds(30)));
+
+ EXPECT_NE(response.status().status_code(), 0);
+ ASSERT_EQ(response.status().error_msgs_size(), 1);
+ EXPECT_NE(response.status().error_msgs(0).find("invalid TTypeNodeType"),
std::string::npos);
+}
+
} // namespace doris
diff --git a/be/test/runtime/workload_group/workload_group_manager_test.cpp
b/be/test/runtime/workload_group/workload_group_manager_test.cpp
index 8a10c619bfd..2a61c1319c5 100644
--- a/be/test/runtime/workload_group/workload_group_manager_test.cpp
+++ b/be/test/runtime/workload_group/workload_group_manager_test.cpp
@@ -36,6 +36,7 @@
#include "cpp/sync_point.h"
#include "exec/pipeline/dependency.h"
#include "exec/pipeline/pipeline_tracing.h"
+#include "exec/scan/scanner_scheduler.h"
#include "exec/spill/spill_file_manager.h"
#include "runtime/exec_env.h"
#include "runtime/query_context.h"
@@ -1360,4 +1361,60 @@ TEST_F(WorkloadGroupManagerTest,
AdaptiveFlushRegistrationSurvivesIdChangeAndReu
controller->adjust_once();
}
+TEST_F(WorkloadGroupManagerTest, DefaultScanSchedulersUseThreadPool) {
+ // Check the registered defaults, which do not depend on the be.conf
loaded by the UT runner.
+ const auto& fields = *config::Register::_s_field_map;
+ EXPECT_STREQ("false",
fields.at("enable_task_executor_in_internal_table").defval);
+ EXPECT_STREQ("false",
fields.at("enable_task_executor_in_external_table").defval);
+
+ const auto saved_config = std::make_tuple(
+ config::enable_adaptive_flush_threads,
config::enable_task_executor_in_internal_table,
+ config::enable_task_executor_in_external_table,
config::pipeline_executor_size,
+ config::blocking_pipeline_executor_size,
config::doris_scanner_thread_pool_thread_num,
+ config::doris_max_remote_scanner_thread_pool_thread_num,
+ config::doris_scanner_min_thread_pool_thread_num,
config::min_active_scan_threads,
+ config::min_active_file_scan_threads,
config::flush_thread_num_per_store);
+ Defer restore {[&] {
+ std::tie(config::enable_adaptive_flush_threads,
+ config::enable_task_executor_in_internal_table,
+ config::enable_task_executor_in_external_table,
config::pipeline_executor_size,
+ config::blocking_pipeline_executor_size,
+ config::doris_scanner_thread_pool_thread_num,
+ config::doris_max_remote_scanner_thread_pool_thread_num,
+ config::doris_scanner_min_thread_pool_thread_num,
config::min_active_scan_threads,
+ config::min_active_file_scan_threads,
config::flush_thread_num_per_store) =
+ saved_config;
+ }};
+ config::enable_adaptive_flush_threads = false;
+ config::enable_task_executor_in_internal_table = false;
+ config::enable_task_executor_in_external_table = false;
+ config::pipeline_executor_size = 1;
+ config::blocking_pipeline_executor_size = 1;
+ config::doris_scanner_thread_pool_thread_num = 1;
+ config::doris_max_remote_scanner_thread_pool_thread_num = 1;
+ config::doris_scanner_min_thread_pool_thread_num = 1;
+ config::min_active_scan_threads = 1;
+ config::min_active_file_scan_threads = 1;
+ config::flush_thread_num_per_store = 1;
+
+ ASSERT_TRUE(_wg_manager->create_internal_wg().ok());
+ Defer stop_schedulers {[&] {
+ for (auto& entry : _wg_manager->_workload_groups) {
+ entry.second->try_stop_schedulers();
+ entry.second->destroy_schedulers();
+ }
+ }};
+ ASSERT_EQ(_wg_manager->_workload_groups.size(), 1);
+ auto wg = _wg_manager->_workload_groups.begin()->second;
+
+ TaskScheduler* exec_sched = nullptr;
+ ScannerScheduler* scan_sched = nullptr;
+ ScannerScheduler* remote_scan_sched = nullptr;
+ wg->get_query_scheduler(&exec_sched, &scan_sched, &remote_scan_sched);
+ ASSERT_NE(scan_sched, nullptr);
+ ASSERT_NE(remote_scan_sched, nullptr);
+ EXPECT_NE(dynamic_cast<ThreadPoolSimplifiedScanScheduler*>(scan_sched),
nullptr);
+
EXPECT_NE(dynamic_cast<ThreadPoolSimplifiedScanScheduler*>(remote_scan_sched),
nullptr);
+}
+
} // namespace doris
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]