Repository: StarRocks/starrocks
Pull request #58892: [Enhancement] Submit write combined_txn_log/aggregate_tablet_metadata to thread pool to avoid block brpc thread
Author: login: sevev
Created: 2025-05-12T09:13:56Z | Merged: 2025-05-22T00:57:26Z

## Why I'm doing:
The combined_txn_log and aggregated_tablet_metadata are written to remote storage directly in the BRPC threads, which may block the BRPC threads. This PR submits these tasks to a thread pool for execution to avoid blocking BRPC threads.

## What I'm doing:

Fixes #58316

## What type of PR is this:

- [ ] BugFix
- [ ] Feature
- [x] Enhancement
- [ ] Refactor
- [ ] UT
- [ ] Doc
- [ ] Tool

Does this PR entail a change in behavior?

- [ ] Yes, this PR will result in a change in behavior.
- [x] No, this PR will not result in a change in behavior.

If yes, please specify the type of change:

- [ ] Interface/UI changes: syntax, type conversion, expression evaluation, display information
- [ ] Parameter changes: default values, similar parameters but with different default values
- [ ] Policy changes: use new policy to replace old one, functionality automatically enabled
- [ ] Feature removed
- [ ] Miscellaneous: upgrade & downgrade compatibility, etc.

## Checklist:

- [x] I have added test cases for my bug fix or my new feature
- [ ] This pr needs user documentation (for new or modified features or behaviors)
  - [ ] I have added documentation for my new feature or new function
- [ ] This is a backport pr

## Bugfix cherry-pick branch check:
- [x] I have checked the version labels which the pr will be auto-backported to the target branch
  - [ ] 3.5
  - [ ] 3.4
  - [ ] 3.3
  - [ ] 3.2
  - [ ] 3.1


Linked issue #58316: [Feature] Cost optimization for shared-data arch in real-time scenarios
## Feature request

**Is your feature request related to a problem? Please describe.**

We found that the transaction model of StarRocks' current shared-data architecture generates excessive object storage PUT calls in real-time ingestion scenarios, leading to high API costs and negatively impacting import performance.

Therefore, the goal of this design is to minimize the cost of S3 API calls in real-time scenarios as much as possible. Ultimately, we aim to achieve:
1. S3 API costs approaching or even lower than storage costs, giving starrocks a cost-performance advantage 
    compared to competitors:
  > Data freshness is around 30 seconds.
  > Out-of-the-box, which means reducing API costs without any tuning.
 
2. Minimal impact on existing query and ingestion performance.

**Describe the solution you'd like**
1. We reduce API calls by aggregating files as much as possible:
  > We combine data file from per tablet to per BE/CN.
  > We combine TxnLog/Meta file from per tablet to per transaction.

2. Because transactions related to ingestion and compaction represent the highest proportion, our current optimizations target only these two transaction workflows.

**Additional context**
https://docs.google.com/document/d/1CYHwzer_57hbU-hcQ5UcCQshxaG0h1gkvFjyuMldAlw/edit?usp=sharing


Diff:
diff --git a/be/src/http/action/update_config_action.cpp b/be/src/http/action/update_config_action.cpp
index 7d9adeeeda34f6..0587a52c5111f4 100644
--- a/be/src/http/action/update_config_action.cpp
+++ b/be/src/http/action/update_config_action.cpp
@@ -199,9 +199,17 @@ Status UpdateConfigAction::update_config(const std::string& name, const std::str
             return Status::OK();
         });
         _config_callback.emplace("transaction_publish_version_worker_count", [&]() -> Status {
-            auto thread_pool = ExecEnv::GetInstance()->agent_server()->get_thread_pool(TTaskType::PUBLISH_VERSION);
-            return thread_pool->update_max_threads(
+            Status st1 = ExecEnv::GetInstance()
+                                 ->agent_server()
+                                 ->get_thread_pool(TTaskType::PUBLISH_VERSION)
+                                 ->update_max_threads(std::max(MIN_TRANSACTION_PUBLISH_WORKER_COUNT,
+                                                               config::transaction_publish_version_worker_count));
+            Status st2 = ExecEnv::GetInstance()->put_aggregate_metadata_thread_pool()->update_max_threads(
                     std::max(MIN_TRANSACTION_PUBLISH_WORKER_COUNT, config::transaction_publish_version_worker_count));
+            if (!st1.ok() || !st2.ok()) {
+                return Status::InvalidArgument("Failed to update transaction_publish_version_worker_count.");
+            }
+            return st1;
         });
         _config_callback.emplace("transaction_publish_version_thread_pool_num_min", [&]() -> Status {
             auto thread_pool = ExecEnv::GetInstance()->agent_server()->get_thread_pool(TTaskType::PUBLISH_VERSION);
diff --git a/be/src/runtime/exec_env.cpp b/be/src/runtime/exec_env.cpp
index 79057f891ae75e..bcaeea0cf4be9a 100644
--- a/be/src/runtime/exec_env.cpp
+++ b/be/src/runtime/exec_env.cpp
@@ -557,6 +557,17 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    int32_t max_thread_count = config::transaction_publish_version_worker_count;
+    if (max_thread_count <= 0) {
+        max_thread_count = CpuInfo::num_cores();
+    }
+    RETURN_IF_ERROR(ThreadPoolBuilder("put_aggregate_metadata")
+                            .set_min_threads(1)
+                            .set_max_threads(std::max(1, max_thread_count))
+                            .set_max_queue_size(std::numeric_limits<int>::max())
+                            .build(&_put_aggregate_metadata_thread_pool));
+    REGISTER_THREAD_POOL_METRICS(put_aggregate_metadata, _put_aggregate_metadata_thread_pool);
+
 #elif defined(BE_TEST)
     _lake_location_provider = std::make_shared<lake::FixedLocationProvider>(_store_paths.front().path);
     _lake_update_manager =
@@ -564,6 +575,11 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     _lake_tablet_manager =
             new lake::TabletManager(_lake_location_provider, _lake_update_manager, config::lake_metadata_cache_limit);
     _lake_replication_txn_manager = new lake::ReplicationTxnManager(_lake_tablet_manager);
+    RETURN_IF_ERROR(ThreadPoolBuilder("put_aggregate_metadata_pool")
+                            .set_min_threads(1)
+                            .set_max_threads(1)
+                            .set_max_queue_size(std::numeric_limits<int>::max())
+                            .build(&_put_aggregate_metadata_thread_pool));
 #endif
 
     _agent_server = new AgentServer(this, false);
@@ -631,6 +647,10 @@ void ExecEnv::stop() {
         _pipeline_sink_io_pool->shutdown();
     }
 
+    if (_put_aggregate_metadata_thread_pool) {
+        _put_aggregate_metadata_thread_pool->shutdown();
+    }
+
     if (_agent_server) {
         _agent_server->stop();
     }
@@ -757,6 +777,7 @@ void ExecEnv::destroy() {
     SAFE_DELETE(_diagnose_daemon);
     _dictionary_cache_pool.reset();
     _automatic_partition_pool.reset();
+    _put_aggregate_metadata_thread_pool.reset();
     _metrics = nullptr;
 }
 
diff --git a/be/src/runtime/exec_env.h b/be/src/runtime/exec_env.h
index 2353f315072926..a55d1a01ed298e 100644
--- a/be/src/runtime/exec_env.h
+++ b/be/src/runtime/exec_env.h
@@ -350,6 +350,8 @@ class ExecEnv {
 
     ThreadPool* delete_file_thread_pool();
 
+    ThreadPool* put_aggregate_metadata_thread_pool() { return _put_aggregate_metadata_thread_pool.get(); }
+
     void try_release_resource_before_core_dump();
 
     DiagnoseDaemon* diagnose_daemon() const { return _diagnose_daemon; }
@@ -419,6 +421,7 @@ class ExecEnv {
     std::shared_ptr<lake::LocationProvider> _lake_location_provider;
     lake::UpdateManager* _lake_update_manager = nullptr;
     lake::ReplicationTxnManager* _lake_replication_txn_manager = nullptr;
+    std::unique_ptr<ThreadPool> _put_aggregate_metadata_thread_pool = nullptr;
 
     AgentServer* _agent_server = nullptr;
     query_cache::CacheManagerRawPtr _cache_mgr;
diff --git a/be/src/service/service_be/lake_service.cpp b/be/src/service/service_be/lake_service.cpp
index 97d5c69fd4aa5d..2ce9e717c89af3 100644
--- a/be/src/service/service_be/lake_service.cpp
+++ b/be/src/service/service_be/lake_service.cpp
@@ -330,6 +330,28 @@ struct AggregatePublishContext {
             latch->wait();
         }
     }
+
+    void put_aggregate_metadata(ExecEnv* env) {
+        if (!has_failure) {
+            auto thread_pool = env->put_aggregate_metadata_thread_pool();
+            if (UNLIKELY(thread_pool == nullptr)) {
+                publish_status = Status::InternalError("can not find put_aggregate_metadata thread pool");
+            } else {
+                auto latch = BThreadCountDownLatch(1);
+                auto task = std::make_shared<AutoCleanRunnable>(
+                        [&] {
+                            publish_status = env->lake_tablet_manager()->put_aggregate_tablet_metadata(tablet_metas);
+                        },
+                        [&] { latch.count_down(); });
+                Status submit_st = thread_pool->submit(std::move(task));
+                if (!submit_st.ok()) {
+                    LOG(WARNING) << "Fail to submit put_aggregate_tablet_metadata task";
+                    publish_status = submit_st;
+                }
+                latch.wait();
+            }
+        }
+    }
 };
 
 static void aggregate_publish_cb(brpc::Controller* cntl, PublishVersionResponse* resp, AggregatePublishContext* ctx) {
@@ -390,14 +412,12 @@ void LakeServiceImpl::aggregate_publish_version(::google::protobuf::RpcControlle
                               brpc::NewCallback(aggregate_publish_cb, node_cntl, node_resp, &ctx));
     }
 
+    // wait for publish task finish
     ctx.wait();
-    // TODO(zhangqiang)
-    // submit put_aggregate_tablet_metadata to thread pool to avoid block brpc thread.
-    Status final_status =
-            ctx.has_failure
-                    ? ctx.publish_status
-                    : ExecEnv::GetInstance()->lake_tablet_manager()->put_aggregate_tablet_metadata(ctx.tablet_metas);
-    final_status.to_protobuf(response->mutable_status());
+    // write aggregate metadata
+    ctx.put_aggregate_metadata(_env);
+
+    ctx.publish_status.to_protobuf(response->mutable_status());
 }
 
 void LakeServiceImpl::_submit_publish_log_version_task(const int64_t* tablet_ids, size_t tablet_size,
@@ -968,7 +988,26 @@ struct AggregateCompactContext {
         }
     }
 
-    Status write_combined_txn_log() { return starrocks::write_combined_txn_log(combined_txn_log); }
+    void write_combined_txn_log(ExecEnv* env) {
+        if (final_status.ok()) {
+            VLOG(2) << "Write combined txn log. pb=" << combined_txn_log.ShortDebugString();
+            auto thread_pool = env->put_combined_txn_log_thread_pool();
+            if (UNLIKELY(thread_pool == nullptr)) {
+                final_status = Status::InternalError("can not find put_combined_txn_log thread pool");
+            } else {
+                auto latch = BThreadCountDownLatch(1);
+                auto task = std::make_shared<AutoCleanRunnable>(
+                        [&] { final_status = starrocks::write_combined_txn_log(combined_txn_log); },
+                        [&] { latch.count_down(); });
+                Status submit_st = thread_pool->submit(std::move(task));
+                if (!submit_st.ok()) {
+                    LOG(WARNING) << "Fail to submit write combined_txn_log task";
+                    final_status = submit_st;
+                }
+                latch.wait();
+            }
+        }
+    }
 };
 
 static void aggregate_compact_cb(brpc::Controller* cntl, CompactResponse* response,
@@ -1050,10 +1089,7 @@ void LakeServiceImpl::aggregate_compact(::google::protobuf::RpcController* contr
     ac_context.wait();
 
     // write combined txn log
-    // TODO // submit write_combined_txn_log to thread pool to avoid block brpc thread.
-    if (ac_context.final_status.ok()) {
-        ac_context.final_status = ac_context.write_combined_txn_log();
-    }
+    ac_context.write_combined_txn_log(_env);
 
     // fill response
     ac_context.final_status.to_protobuf(response->mutable_status());
diff --git a/be/src/storage/lake/tablet_manager.cpp b/be/src/storage/lake/tablet_manager.cpp
index f4c4d1b85e1170..9ebd7acc35785e 100644
--- a/be/src/storage/lake/tablet_manager.cpp
+++ b/be/src/storage/lake/tablet_manager.cpp
@@ -405,6 +405,10 @@ StatusOr<TabletMetadataPtr> TabletManager::get_tablet_metadata(int64_t tablet_id
                     get_tablet_metadata(tablet_initial_metadata_location(tablet_id), fill_cache, expected_gtid, fs);
         } else {
             tablet_metadata_or = get_single_tablet_metadata(tablet_id, version, fill_cache, expected_gtid, fs);
+            if (tablet_metadata_or.status().is_not_found()) {
+                tablet_metadata_or = get_tablet_metadata(tablet_metadata_location(tablet_id, version), fill_cache,
+                                                         expected_gtid, fs);
+            }
         }
     } else {
         tablet_metadata_or =
@@ -442,6 +446,9 @@ StatusOr<TabletMetadataPtr> TabletManager::get_tablet_metadata(const string& pat
     auto [tablet_id, version] = parse_tablet_metadata_filename(basename(path));
     if (_metacache->lookup_aggregation_partition(tablet_metadata_root_location(tablet_id))) {
         metadata_or = get_single_tablet_metadata(tablet_id, version, fill_cache, expected_gtid, fs);
+        if (metadata_or.status().is_not_found()) {
+            metadata_or = load_tablet_metadata(path, fill_cache, expected_gtid, fs);
+        }
     } else {
         metadata_or = load_tablet_metadata(path, fill_cache, expected_gtid, fs);
         if (metadata_or.status().is_not_found()) {
diff --git a/be/src/util/starrocks_metrics.h b/be/src/util/starrocks_metrics.h
index e951d2cc966d3d..659b189ea358a7 100644
--- a/be/src/util/starrocks_metrics.h
+++ b/be/src/util/starrocks_metrics.h
@@ -368,6 +368,7 @@ class StarRocksMetrics {
     METRICS_DEFINE_THREAD_POOL(pk_index_compaction);
     METRICS_DEFINE_THREAD_POOL(compact_pool);
     METRICS_DEFINE_THREAD_POOL(pindex_load);
+    METRICS_DEFINE_THREAD_POOL(put_aggregate_metadata);
 
     METRIC_DEFINE_UINT_GAUGE(load_rpc_threadpool_size, MetricUnit::NOUNIT);
 
diff --git a/be/test/http/action/update_config_action_test.cpp b/be/test/http/action/update_config_action_test.cpp
index de57fb99e71f88..5438ac3f186585 100644
--- a/be/test/http/action/update_config_action_test.cpp
+++ b/be/test/http/action/update_config_action_test.cpp
@@ -91,4 +91,12 @@ TEST_F(UpdateConfigActionTest, test_update_number_tablet_writer_threads) {
     }
 }
 
+TEST_F(UpdateConfigActionTest, test_update_transaction_publish_version_worker_count) {
+    UpdateConfigAction action(ExecEnv::GetInstance());
+
+    auto st = action.update_config("transaction_publish_version_worker_count", "8");
+    CHECK_OK(st);
+    ASSERT_EQ(8, ExecEnv::GetInstance()->put_aggregate_metadata_thread_pool()->max_threads());
+}
+
 } // namespace starrocks
diff --git a/be/test/service/lake_service_test.cpp b/be/test/service/lake_service_test.cpp
index da5af2f694de16..83cd968ed75f55 100644
--- a/be/test/service/lake_service_test.cpp
+++ b/be/test/service/lake_service_test.cpp
@@ -1047,11 +1047,14 @@ TEST_F(LakeServiceTest, test_aggregate_compact) {
                 TxnLogPB txnlog;
                 txnlog.set_tablet_id(100);
                 txnlog.set_txn_id(100);
+                txnlog.set_partition_id(99);
                 resp->add_txn_logs()->CopyFrom(txnlog);
                 TxnLogPB txnlog2;
                 txnlog2.set_tablet_id(101);
                 txnlog2.set_txn_id(100);
+                txnlog2.set_partition_id(99);
                 resp->add_txn_logs()->CopyFrom(txnlog2);
+                resp->mutable_status()->set_status_code(0);
                 done->Run();
             }));
     // compact success - single cn
@@ -1067,6 +1070,7 @@ TEST_F(LakeServiceTest, test_aggregate_compact) {
         request.add_tablet_ids(_tablet_id);
         request.set_txn_id(txn_id);
         request.set_version(1);
+        request.set_timeout_ms(3000);
         // add request to agg_request
         agg_request.add_requests()->CopyFrom(request);
         agg_request.add_compute_nodes()->CopyFrom(cn);
@@ -1087,6 +1091,7 @@ TEST_F(LakeServiceTest, test_aggregate_compact) {
             request.add_tablet_ids(_tablet_id);
             request.set_txn_id(txn_id);
             request.set_version(1);
+            request.set_timeout_ms(3000);
             // add request to agg_request
             agg_request.add_requests()->CopyFrom(request);
             agg_request.add_compute_nodes()->CopyFrom(cn);
diff --git a/be/test/storage/lake/tablet_manager_test.cpp b/be/test/storage/lake/tablet_manager_test.cpp
index 1c422115e73458..f06e51f3226004 100644
--- a/be/test/storage/lake/tablet_manager_test.cpp
+++ b/be/test/storage/lake/tablet_manager_test.cpp
@@ -635,9 +635,9 @@ TEST_F(LakeTabletManagerTest, put_aggregate_tablet_metadata) {
         metadata2.set_id(2);
         metadata2.set_version(2);
         metadata2.mutable_schema()->CopyFrom(schema_pb1);
-        auto& item1 = (*metadata1.mutable_historical_schemas())[10];
+        auto& item1 = (*metadata2.mutable_historical_schemas())[10];
         item1.CopyFrom(schema_pb1);
-        auto& item2 = (*metadata1.mutable_historical_schemas())[12];
+        auto& item2 = (*metadata2.mutable_historical_schemas())[12];
         item2.CopyFrom(schema_pb3);
     }
 
@@ -650,6 +650,20 @@ TEST_F(LakeTabletManagerTest, put_aggregate_tablet_metadata) {
     TabletMetadataPtr metadata3 = std::move(res).value();
     ASSERT_EQ(metadata3->schema().id(), 10);
     ASSERT_EQ(metadata3->historical_schemas_size(), 2);
+
+    starrocks::TabletMetadata metadata4;
+    {
+        metadata4.set_id(3);
+        metadata4.set_version(3);
+        metadata4.mutable_schema()->CopyFrom(schema_pb1);
+        auto& item1 = (*metadata4.mutable_historical_schemas())[10];
+        item1.CopyFrom(schema_pb1);
+        auto& item2 = (*metadata4.mutable_historical_schemas())[12];
+        item2.CopyFrom(schema_pb3);
+    }
+    EXPECT_OK(_tablet_manager->put_tablet_metadata(metadata4));
+    res = _tablet_manager->get_tablet_metadata(3, 3);
+    ASSERT_TRUE(res.ok());
 }
 
 TEST_F(LakeTabletManagerTest, cache_tablet_metadata) {


Reviews:
user: login: luohaha
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T09:13:48Z
user: login: sevev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-17T00:20:56Z
user: login: luohaha
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-17T02:30:51Z
user: login: luohaha
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-17T02:31:09Z
user: login: kevincai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T06:59:17Z
user: login: kevincai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T07:02:41Z
user: login: kevincai
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T07:46:16Z
user: login: sevev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T08:00:28Z
user: login: kevincai
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T11:18:12Z
user: login: luohaha
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T12:13:20Z
user: login: kevincai
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T00:57:24Z

Review comments:
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
path: be/src/runtime/exec_env.cpp
user: login: luohaha
body: Use `put_aggregate_file_thread_pool` or other name? Because it's used for metadata and txnlog, no only for metadata.
created_at: 2025-05-16T09:13:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
path: be/src/runtime/exec_env.cpp
user: login: sevev
body: > Use `put_aggregate_file_thread_pool` or other name? Because it's used for metadata and txnlog, no only for metadata.

The writing of `combined_txn_log` uses a separate thread pool, which already existed in the previous implementation. Therefore, the thread pools for writing combined_txn_log and aggregate_metadata are distinct.

Furthermore, since these two operations (combined_txn_log writing and aggregate_metadata writing) occur at different stages of the process, maintaining separate thread pools for them may be better.
created_at: 2025-05-17T00:20:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
path: be/src/runtime/exec_env.cpp
user: login: luohaha
body: ok, got it
created_at: 2025-05-17T02:30:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
path: be/src/runtime/exec_env.cpp
user: login: kevincai
body: what does this line for? seems useless.
created_at: 2025-05-20T06:57:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
+    int32_t max_thread_count = config::transaction_publish_version_worker_count;
path: be/src/runtime/exec_env.cpp
user: login: kevincai
body: consider make this configvar dynamic changeable so it will be much easier to update the configvar and take effect without restarting the BE process.
created_at: 2025-05-20T06:58:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
+    int32_t max_thread_count = config::transaction_publish_version_worker_count;
+    if (max_thread_count <= 0) {
+        max_thread_count = CpuInfo::num_cores();
+    }
+    RETURN_IF_ERROR(ThreadPoolBuilder("put_aggregate_metadata_pool")
path: be/src/runtime/exec_env.cpp
user: login: kevincai
body: refer to BUILD_DYNAMIC_TASK_THREAD_POOL in agent_server.cpp, shall be able to register a few metrics related to this thread pool including the tasks, latency, total tasks executed.
created_at: 2025-05-20T07:02:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -789,13 +789,29 @@ Status ExecEnv::init(const std::vector<StorePath>& store_paths, bool as_cn) {
     }
     setenv(staros::starlet::fslib::kFslibCacheDir.c_str(), config::starlet_cache_dir.c_str(), 1);
 
+    std::unique_ptr<ThreadPool> put_aggregate_metadata_thread_pool;
+    int32_t max_thread_count = config::transaction_publish_version_worker_count;
path: be/src/runtime/exec_env.cpp
user: login: sevev
body: > consider make this configvar dynamic changeable so it will be much easier to update the configvar and take effect without restarting the BE process.

done.
created_at: 2025-05-20T08:00:27Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-20T15:57:09Z
body: #### [Java-Extensions Incremental Coverage Report]


:white_check_mark: **pass** : 0 / 0 (0%)




author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-20T15:57:13Z
body: #### [FE Incremental Coverage Report]


:white_check_mark: **pass** : 0 / 0 (0%)




author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-20T16:11:03Z
body: #### [BE Incremental Coverage Report]

:white_check_mark: **pass** : 49 / 60 (81.67%)




#### file detail

|   |path|covered_line|new_line|coverage|not_covered_line_detail|
|----|----|----|----|----|----|
|:large_blue_circle:|be/src/storage/lake/tablet_manager.cpp|1|5|20.00%|[409, 410, 449, 450]|
|:large_blue_circle:|be/src/service/service_be/lake_service.cpp|32|38|84.21%|[338, 348, 349, 996, 1004, 1005]|
|:large_blue_circle:|be/src/http/action/update_config_action.cpp|6|7|85.71%|[210]|
|:large_blue_circle:|be/src/runtime/exec_env.h|1|1|100.00%|[]|
|:large_blue_circle:|be/src/runtime/exec_env.cpp|9|9|100.00%|[]|




author_association: CONTRIBUTOR