Repository: StarRocks/starrocks
Pull request #58753: [BugFix] Fix possible duplicate finishInstance calls
Author: login: LiShuMing
Labels: name: documentation
description: Improvements or additions to documentation
name: 3.3-merged
name: 3.4-merged
name: 3.5-merged
Created: 2025-05-07T11:26:55Z | Merged: 2025-05-09T10:29:41Z

## Why I'm doing:

I found a bug which one instance may call `finishInstance` multi times which may cause `profileDoneSignal`'s count down incorrect:
```
❯ grep c0ae89be-283f-11f0-9ec2-0050569a5b3f fe.log.20250504-5
2025-05-04 00:57:52.926+08:00 INFO (starrocks-mysql-nio-pool-16578|16034887) [QueryRuntimeProfile.attachInstances():227] Attach instances, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b, instance_size: 8, instance_ids: [c0ae89be-283f-11f0-9ec2-0050569a5b3d, c0ae89be-283f-11f0-9ec2-0050569a5b3e, c0ae89be-283f-11f0-9ec2-0050569a5b3f, c0ae89be-283f-11f0-9ec2-0050569a5b40, c0ae89be-283f-11f0-9ec2-0050569a5b41, c0ae89be-283f-11f0-9ec2-0050569a5b42, c0ae89be-283f-11f0-9ec2-0050569a5b43, c0ae89be-283f-11f0-9ec2-0050569a5b3c]
2025-05-04 00:58:44.524+08:00 INFO (thrift-server-pool-7638300|16089094) [QueryRuntimeProfile.updateLoadInformation():417] Update load information, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b
2025-05-04 00:58:44.528+08:00 INFO (thrift-server-pool-7638300|16089094) [QueryRuntimeProfile.finishInstance():252] Finish instances, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, count: 1
2025-05-04 00:58:44.530+08:00 INFO (thrift-server-pool-7637970|16088233) [QueryRuntimeProfile.updateLoadInformation():417] Update load information, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b
2025-05-04 00:58:44.530+08:00 INFO (thrift-server-pool-7637970|16088233) [QueryRuntimeProfile.finishInstance():252] Finish instances, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, count: 0
2025-05-04 00:58:44.535+08:00 INFO (thrift-server-pool-7638370|16089454) [QueryRuntimeProfile.updateLoadInformation():417] Update load information, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b
2025-05-04 00:58:44.535+08:00 INFO (thrift-server-pool-7638370|16089454) [QueryRuntimeProfile.finishInstance():252] Finish instances, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, count: 0
2025-05-04 00:58:44.537+08:00 INFO (thrift-server-pool-7638239|16088912) [QueryRuntimeProfile.updateLoadInformation():417] Update load information, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b
2025-05-04 00:58:44.537+08:00 INFO (thrift-server-pool-7638239|16088912) [DefaultCoordinator.lambda$updateFragmentExecStatus$10():1023] update load information successfully, tablet commit info size: 5, instanceId: c0ae89be-283f-11f0-9ec2-0050569a5b3f, query id: c0ae89be-283f-11f0-9ec2-0050569a5b3b
2025-05-04 00:58:44.537+08:00 INFO (thrift-server-pool-7638239|16088912) [QueryRuntimeProfile.finishInstance():252] Finish instances, query_id: c0ae89be-283f-11f0-9ec2-0050569a5b3b, instance_id: c0ae89be-283f-11f0-9ec2-0050569a5b3f, count: 0

```

And `updateExecStatus` is not safe: when `params.isDone()` is `false` which means it's a runtime profile report, it may also call `finishInstance` in `updateFragmentExecStatus`:
```
    /**
     * Update the execution state and profile from the report RPC.
     *
     * @param params The report RPC request.
     * @return true if the state is updated. Otherwise, return false.
     */
    public synchronized boolean updateExecStatus(TReportExecStatusParams params) {
        switch (state) {
            case CREATED:
            case FINISHED: // duplicate packet
            case FAILED:
                return false;
            case DEPLOYING:
            case EXECUTING:
            case CANCELLING:
            default:
                if (params.isDone()) {
                    if (params.getStatus() == null || params.getStatus().getStatus_code() == TStatusCode.OK) {
                        transitionState(State.FINISHED);
                    } else {
                        transitionState(State.FAILED);
                    }
                }
                return true;
        }
    }
```

## What I'm doing:
1. Fix `updateExecStatus` to distinguish requests with  `isDone=false` and `isDone=true`;
2. Split `updateFragmentExecStatus` into `updateRuntimeProfile` and `updateFinishInstance` to separate `isDone=false` and `isDone=true`' request.
3. Add `report_exec_state_failed_status` failpoint to mock rpc fails.
4. Change `report_exec_status`'s retry time to 1 since the caller(report_exec_state) has already retried 3 times, we don't need to retry again;
5. Add `report_exec_rpc_request_retry_num` to control report rpc retry number;
6.   For DML jobs, finishInstance is ensured to be called only after instance finished and should not be called repeatedly. Otherwise, it will cause commit with wrong commit info.


Fixes 
## What type of PR is this:

- [x] BugFix
- [ ] Feature
- [ ] 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
  - [x] 3.5
  - [x] 3.4
  - [x] 3.3
  - [ ] 3.2
  - [ ] 3.1


Diff:
diff --git a/be/src/common/config.h b/be/src/common/config.h
index 0f730d3acdc5b1..e44c0581297c18 100644
--- a/be/src/common/config.h
+++ b/be/src/common/config.h
@@ -1186,6 +1186,11 @@ CONF_mInt64(mem_limited_chunk_queue_block_size, "8388608");
 
 CONF_Int32(internal_service_query_rpc_thread_num, "-1");
 CONF_Int32(internal_service_datacache_rpc_thread_num, "-1");
+// The retry times of rpc request to report exec rpc request to FE. The default value is 10,
+// which means that the rpc request will be retried 10 times if it fails if it's fragment instatnce finish rpc.
+// Report exec rpc request is important for load job, if one fragment instance finish report failed,
+// the load job will be hang until timeout.
+CONF_mInt32(report_exec_rpc_request_retry_num, "10");
 
 /*
  * When compile with ENABLE_STATUS_FAILED, every use of RETURN_INJECT has probability of 1/cardinality_of_inject
diff --git a/be/src/exec/pipeline/exec_state_reporter.cpp b/be/src/exec/pipeline/exec_state_reporter.cpp
index 1a7c4e11e47af3..3c5abc76d48336 100644
--- a/be/src/exec/pipeline/exec_state_reporter.cpp
+++ b/be/src/exec/pipeline/exec_state_reporter.cpp
@@ -153,9 +153,11 @@ Status ExecStateReporter::report_exec_status(const TReportExecStatusParams& para
     TReportExecStatusResult res;
     Status rpc_status;
 
+    // since the caller(report_exec_state) has already retried {@code config::report_exec_rpc_request_retry_num} times,
+    // no need to retry again.
     rpc_status = ThriftRpcHelper::rpc<FrontendServiceClient>(
             fe_addr, [&res, &params](FrontendServiceConnection& client) { client->reportExecStatus(res, params); },
-            config::thrift_rpc_timeout_ms);
+            config::thrift_rpc_timeout_ms, 1);
 
     if (rpc_status.ok()) {
         rpc_status = Status(res.status);
diff --git a/be/src/exec/pipeline/pipeline_driver_executor.cpp b/be/src/exec/pipeline/pipeline_driver_executor.cpp
index 9e8f3508a2451c..faa0cb503c52bb 100644
--- a/be/src/exec/pipeline/pipeline_driver_executor.cpp
+++ b/be/src/exec/pipeline/pipeline_driver_executor.cpp
@@ -31,6 +31,7 @@
 namespace starrocks::pipeline {
 
 DEFINE_FAIL_POINT(operator_return_failed_status);
+DEFINE_FAIL_POINT(report_exec_state_failed_status);
 
 GlobalDriverExecutor::GlobalDriverExecutor(const std::string& name, std::unique_ptr<ThreadPool> thread_pool,
                                            bool enable_resource_group, const CpuUtil::CpuIds& cpuids,
@@ -359,15 +360,24 @@ void GlobalDriverExecutor::report_exec_state(QueryContext* query_ctx, FragmentCo
 
     auto report_task = [params, exec_env, fe_addr, fragment_id]() {
         int retry_times = 0;
-        while (retry_times++ < 3) {
+        int max_retry_times = config::report_exec_rpc_request_retry_num;
+        while (retry_times++ < max_retry_times) {
             auto status = ExecStateReporter::report_exec_status(*params, exec_env, fe_addr);
+
+            FAIL_POINT_TRIGGER_EXECUTE(report_exec_state_failed_status, {
+                if (status.ok()) {
+                    status = Status::InternalError("injected failed status");
+                }
+            });
+
             if (!status.ok()) {
                 if (status.is_not_found()) {
                     VLOG(1) << "[Driver] Fail to report exec state due to query not found: fragment_instance_id="
                             << print_id(fragment_id);
                 } else {
                     LOG(WARNING) << "[Driver] Fail to report exec state: fragment_instance_id=" << print_id(fragment_id)
-                                 << ", status: " << status.to_string() << ", retry_times=" << retry_times;
+                                 << ", status: " << status.to_string() << ", retry_times=" << retry_times
+                                 << ", max_retry_times=" << max_retry_times;
                     // if it is done exec state report, we should retry
                     if (params->__isset.done && params->done) {
                         continue;
diff --git a/be/src/runtime/fragment_mgr.cpp b/be/src/runtime/fragment_mgr.cpp
index 412f6f24bf1c94..54628f5f910353 100644
--- a/be/src/runtime/fragment_mgr.cpp
+++ b/be/src/runtime/fragment_mgr.cpp
@@ -721,7 +721,7 @@ void FragmentMgr::report_fragments(const std::vector<TUniqueId>& non_pipeline_ne
                     config::thrift_rpc_timeout_ms);
 
             if (!rpc_status.ok()) {
-                LOG(WARNING) << "thrift rpc error:" << rpc_status;
+                LOG(WARNING) << "batch report exec status rpc error:" << rpc_status;
                 continue;
             }
 
diff --git a/be/src/runtime/profile_report_worker.cpp b/be/src/runtime/profile_report_worker.cpp
index f2dd2aaa6aef74..80f386daacc096 100644
--- a/be/src/runtime/profile_report_worker.cpp
+++ b/be/src/runtime/profile_report_worker.cpp
@@ -110,15 +110,16 @@ void ProfileReportWorker::_start_report_profile() {
 void ProfileReportWorker::execute() {
     LOG(INFO) << "ProfileReportWorker start working.";
 
-    int32_t interval = config::profile_report_interval;
-
     while (!_stop.load(std::memory_order_consume)) {
         _start_report_profile();
 
+        // interval can be changed by config dynamically
+        int32_t interval = config::profile_report_interval;
         if (interval <= 0) {
             LOG(WARNING) << "profile_report_interval config is illegal: " << interval << ", force set to 1";
             interval = 1;
         }
+
         nap_sleep(interval, [this] { return _stop.load(std::memory_order_consume); });
     }
     LOG(INFO) << "ProfileReportWorker going to exit.";
diff --git a/be/src/util/thrift_rpc_helper.cpp b/be/src/util/thrift_rpc_helper.cpp
index 4257295e54ed31..c82264cfa7f833 100644
--- a/be/src/util/thrift_rpc_helper.cpp
+++ b/be/src/util/thrift_rpc_helper.cpp
@@ -115,12 +115,14 @@ Status ThriftRpcHelper::rpc(const std::string& ip, const int32_t port,
         if (status.ok()) {
             return Status::OK();
         }
-        LOG(WARNING) << status;
+        LOG(WARNING) << "rpc failed: " << status << ", retry times: " << i << "/" << retry_times
+                     << ", address=" << address << ", timeout_ms=" << timeout_ms;
         SleepFor(MonoDelta::FromMilliseconds(config::thrift_client_retry_interval_ms));
         // reopen failure will disable this connection to prevent it from being used again.
         auto st = client.reopen(timeout_ms);
         if (!st.ok()) {
-            LOG(WARNING) << "client reopen failed. address=" << address << ", status=" << st.message();
+            LOG(WARNING) << "rpc client reopen failed. address=" << address << ", status=" << st.message()
+                         << ", retry times: " << i << "/" << retry_times;
             break;
         }
     } while (i++ < retry_times);
diff --git a/docs/en/administration/management/BE_configuration.md b/docs/en/administration/management/BE_configuration.md
index 85fe8c91942a0f..19ba94bb4bd0e7 100644
--- a/docs/en/administration/management/BE_configuration.md
+++ b/docs/en/administration/management/BE_configuration.md
@@ -5258,3 +5258,12 @@ When this value is set to less than `0`, the system uses the product of its abso
 - Is mutable: No
 - Description: The maximum length of input values for bitmap functions.
 - Introduced in: -
+
+##### report_exec_rpc_request_retry_num
+
+- Default: 10
+- Type: Int
+- Unit: -
+- Is mutable: Yes
+- Description: The retry times of rpc request to report exec rpc request to FE. The default value is 10, which means that the rpc request will be retried 10 times if it fails only if it's fragment instatnce finish rpc. Report exec rpc request is important for load job, if one fragment instance finish report failed, the load job will be hang until timeout.
+- Introduced in: -
\ No newline at end of file
diff --git a/docs/zh/administration/management/BE_configuration.md b/docs/zh/administration/management/BE_configuration.md
index 0c22e97753ae2e..2c1202d096e0e2 100644
--- a/docs/zh/administration/management/BE_configuration.md
+++ b/docs/zh/administration/management/BE_configuration.md
@@ -5250,3 +5250,12 @@ curl http://<BE_IP>:<BE_HTTP_PORT>/varz
 - 是否动态：否
 - 描述：bitmap 函数输入值的最大长度。
 - 引入版本：-
+
+##### report_exec_rpc_request_retry_num
+
+- 默认值：10
+- 类型: Int
+- 单位：-
+- 是否动态：是
+- 描述：用于向 FE 汇报执行状态的 RPC 请求的重试次数。默认值为 10，意味着如果该 RPC 请求失败（仅限于 fragment instance 的 finish RPC），将最多重试 10 次。该请求对于导入任务（load job）非常重要，如果某个 fragment instance 的完成状态报告失败，整个导入任务将会一直挂起，直到超时。
+-引入版本：-
\ No newline at end of file
diff --git a/fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java b/fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java
index e2ad5db6f8dd83..3d4c7524f84ef9 100644
--- a/fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java
+++ b/fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java
@@ -1094,63 +1094,31 @@ public void updateFragmentExecStatus(TReportExecStatusParams params) {
         // The exec status would affect query schedule, so it must be updated no matter what exceptions happen.
         // Otherwise, the query might hang until timeout
         if (!execState.updateExecStatus(params)) {
+            LOG.info("duplicate report fragment exec status, query id: {}, instance id: {}, backend id: {}, " +
+                            "status: {}, exec state: {}",
+                    DebugUtil.printId(jobSpec.getQueryId()),
+                    DebugUtil.printId(params.getFragment_instance_id()),
+                    params.getBackend_id(), params.status, execState.getState());
             return;
         }
 
-        String instanceId = DebugUtil.printId(params.getFragment_instance_id());
+        final String instanceId = DebugUtil.printId(params.getFragment_instance_id());
+        final boolean isDone = params.isDone();
         // Create a CompletableFuture chain for handling updates
-        CompletableFuture<Void> future = CompletableFuture.completedFuture(null)
+        final CompletableFuture<Void> future = CompletableFuture.completedFuture(null)
                 .thenRun(() -> {
                     try {
-                        queryProfile.updateProfile(execState, params);
-                        execState.updateRunningProfile(params);
+                        updateRuntimeProfile(params, execState, instanceId);
                     } catch (Throwable e) {
-                        LOG.warn("update profile failed {}", instanceId, e);
+                        LOG.warn("update runtime profile failed {}", instanceId, e);
                     }
                 })
                 .thenRun(() -> {
-                    try {
-                        lock();
-                        queryProfile.updateLoadChannelProfile(params);
-                    } catch (Throwable e) {
-                        LOG.warn("update load channel profile failed {}", instanceId, e);
-                    } finally {
-                        unlock();
-                    }
-                })
-                .thenRun(() -> {
-                    Status status = new Status(params.status);
-                    if (!(returnedAllResults && status.isCancelled()) && !status.ok()) {
-                        ConnectContext ctx = connectContext;
-                        if (ctx != null) {
-                            ctx.setErrorCodeOnce(status.getErrorCodeString());
-                        }
-                        LOG.warn("exec state report failed status={}, query_id={}, instance_id={}, backend_id={}",
-                                status, DebugUtil.printId(jobSpec.getQueryId()),
-                                DebugUtil.printId(params.getFragment_instance_id()),
-                                params.getBackend_id());
-                        updateStatus(status, params.getFragment_instance_id());
-                    }
-                })
-                .thenRun(() -> {
-                    if (execState.isFinished()) {
-                        try {
-                            lock();
-                            queryProfile.updateLoadInformation(execState, params);
-                        } catch (Throwable e) {
-                            LOG.warn("update load information failed {}", instanceId, e);
-                        } finally {
-                            unlock();
-                        }
+                    // update load info if it's a isDone rpc
+                    if (isDone && execState.isFinished()) {
+                        updateFinishInstance(params, execState, instanceId);
                     }
                 })
-                .thenRun(() -> {
-                    // NOTE: it's critical for query execution, and must be put after the profile update
-                    if (execState.isFinished()) {
-                        queryProfile.finishInstance(params.getFragment_instance_id());
-                    }
-                })
-                .thenRun(() -> updateJobProgress(params))
                 .handle((result, ex) -> {
                     // all block are independent, continue the execution no matter what exception happen
                     if (ex != null) {
@@ -1167,6 +1135,77 @@ public void updateFragmentExecStatus(TReportExecStatusParams params) {
         }
     }
 
+    /**
+     * Update runtime profile and load profile no matter the input params is a finish or runtime profile
+     */
+    private void updateRuntimeProfile(TReportExecStatusParams params,
+                                      FragmentInstanceExecState execState,
+                                      String instanceId) {
+        // update profile
+        try {
+            queryProfile.updateProfile(execState, params);
+            execState.updateRunningProfile(params);
+        } catch (Throwable e) {
+            LOG.warn("update profile failed {}", instanceId, e);
+        }
+
+        // update load profile
+        try {
+            lock();
+            queryProfile.updateLoadChannelProfile(params);
+        } catch (Throwable e) {
+            LOG.warn("update load channel profile failed {}", instanceId, e);
+        } finally {
+            unlock();
+        }
+
+        // update job progress
+        try {
+            updateJobProgress(params);
+        } catch (Throwable e) {
+            LOG.warn("update job progress failed {}", instanceId, e);
+        }
+
+        // update status
+        Status status = new Status(params.status);
+        if (!(returnedAllResults && status.isCancelled()) && !status.ok()) {
+            ConnectContext ctx = connectContext;
+            if (ctx != null) {
+                ctx.setErrorCodeOnce(status.getErrorCodeString());
+            }
+            LOG.warn("exec state report failed status={}, query_id={}, instance_id={}, backend_id={}",
+                    status, DebugUtil.printId(jobSpec.getQueryId()),
+                    DebugUtil.printId(params.getFragment_instance_id()),
+                    params.getBackend_id());
+            updateStatus(status, params.getFragment_instance_id());
+        }
+    }
+
+    /**
+     * Update the instance only if the instance is finished.
+     */
+    private void updateFinishInstance(TReportExecStatusParams params,
+                                      FragmentInstanceExecState execState,
+                                      String instanceId) {
+        // For DML jobs, finishInstance is ensured to be called only after instance finished and should not be
+        // called repeatedly. Otherwise, it will cause commit with wrong commit info.
+        if (jobSpec.isLoadType() && execState.getState().isFinished() && queryProfile.isFinished()) {
+            throw new RuntimeException(String.format("updateFinishInstance called after fragment is finished:%s, query_id:%s",
+                    instanceId, DebugUtil.printId(params.getQuery_id())));
+        }
+        try {
+            lock();
+            queryProfile.updateLoadInformation(execState, params);
+        } catch (Throwable e) {
+            LOG.warn("update load information failed {}", instanceId, e);
+        } finally {
+            unlock();
+        }
+
+        // NOTE: it's critical for query execution, and must be put after the profile update
+        queryProfile.finishInstance(params.getFragment_instance_id());
+    }
+
     @Override
     public synchronized void updateAuditStatistics(TReportAuditStatisticsParams params) {
         PQueryStatistics newAuditStatistics = AuditStatisticsUtil.toProtobuf(params.audit_statistics);
diff --git a/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/QueryRuntimeProfile.java b/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/QueryRuntimeProfile.java
index 117e81f4d47114..a8bbe4f0a40bb7 100644
--- a/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/QueryRuntimeProfile.java
+++ b/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/QueryRuntimeProfile.java
@@ -129,14 +129,14 @@ public class QueryRuntimeProfile {
     // ------------------------------------------------------------------------------------
     // Fields for export.
     // ------------------------------------------------------------------------------------
-    private final List<String> exportFiles = Lists.newArrayList();
-    private final List<TTabletCommitInfo> commitInfos = Lists.newArrayList();
-    private final List<TTabletFailInfo> failInfos = Lists.newArrayList();
+    private final List<String> exportFiles = Lists.newCopyOnWriteArrayList();
+    private final List<TTabletCommitInfo> commitInfos = Lists.newCopyOnWriteArrayList();
+    private final List<TTabletFailInfo> failInfos = Lists.newCopyOnWriteArrayList();
 
     // ------------------------------------------------------------------------------------
     // Fields for external table sink
     // ------------------------------------------------------------------------------------
-    private final List<TSinkCommitInfo> sinkCommitInfos = Lists.newArrayList();
+    private final List<TSinkCommitInfo> sinkCommitInfos = Lists.newCopyOnWriteArrayList();
 
     // Fields for datacache
     private final DataCacheSelectMetrics dataCacheSelectMetrics = new DataCacheSelectMetrics();
diff --git a/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/dag/FragmentInstanceExecState.java b/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/dag/FragmentInstanceExecState.java
index cfb4501e9e65e9..166a4a64fce406 100644
--- a/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/dag/FragmentInstanceExecState.java
+++ b/fe/fe-core/src/main/java/com/starrocks/qe/scheduler/dag/FragmentInstanceExecState.java
@@ -75,7 +75,7 @@
 public class FragmentInstanceExecState {
     private static final Logger LOG = LogManager.getLogger(FragmentInstanceExecState.class);
 
-    private State state = State.CREATED;
+    private volatile State state = State.CREATED;
 
     private final JobSpec jobSpec;
     private final PlanFragmentId fragmentId;
@@ -479,6 +479,10 @@ private synchronized void transitionState(State from, State to) {
         }
     }
 
+    public State getState() {
+        return state;
+    }
+
     public enum State {
         CREATED,
         DEPLOYING,
@@ -495,6 +499,10 @@ public boolean hasBeenDeployed() {
         public boolean isTerminal() {
             return this == FINISHED || this == FAILED;
         }
+
+        public boolean isFinished() {
+            return this == FINISHED;
+        }
     }
 
     public FragmentInstance getFragmentInstance() {
diff --git a/fe/fe-core/src/test/java/com/starrocks/qe/scheduler/JoinTest.java b/fe/fe-core/src/test/java/com/starrocks/qe/scheduler/JoinTest.java
index c085336b4b1381..a77469403eda37 100644
--- a/fe/fe-core/src/test/java/com/starrocks/qe/scheduler/JoinTest.java
+++ b/fe/fe-core/src/test/java/com/starrocks/qe/scheduler/JoinTest.java
@@ -41,6 +41,9 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
@@ -221,4 +224,40 @@ public void testJoinFinishSuccessfully() throws Exception {
                 .collect(Collectors.toMap(Map.Entry::getKey, entry -> String.valueOf(entry.getValue())));
         Assert.assertEquals(stringLoadCounters, scheduler.getLoadCounters());
     }
+
+    @Test
+    public void testUpdateExecStatusConcurrently() throws Exception {
+        String sql = "insert into lineitem select a.* from lineitem a join lineitem b on a.L_ORDERKEY=b.L_ORDERKEY";
+        DefaultCoordinator scheduler = startScheduling(sql);
+        List<Integer> executionIndexes = scheduler.getExecutionDAG().getExecutions().stream()
+                .map(FragmentInstanceExecState::getIndexInJob)
+                .collect(Collectors.toList());
+        int fragmentInstanceCount = executionIndexes.size();
+        // threads for each fragment instance to mock duplicated updateFragmentExecStatus and some of them are not EOS.
+        final int threadCountPerInstance = 10;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCountPerInstance * fragmentInstanceCount);
+        List<Future<?>> futures = new ArrayList<>();
+        for (int i = 0; i < fragmentInstanceCount * threadCountPerInstance; i++) {
+            final int indexInJob = i % fragmentInstanceCount;
+            final boolean isDone = i % 2 == 0;
+            futures.add(executor.submit(() -> {
+                TReportExecStatusParams request = new TReportExecStatusParams(FrontendServiceVersion.V1);
+                request.setBackend_num(indexInJob);
+                request.setDone(isDone);
+                request.setStatus(new TStatus(TStatusCode.OK));
+                scheduler.updateFragmentExecStatus(request);
+            }));
+        }
+        // ensure finishInstance will not be called duplicate
+        for (Future<?> f : futures) {
+            f.get();
+        }
+        executor.shutdown();
+        for (int i = 0; i < fragmentInstanceCount; i++) {
+            int indexInJob = executionIndexes.get(i);
+            FragmentInstanceExecState execState = scheduler.getExecutionDAG().getExecution(indexInJob);
+            System.out.println(execState.getState());
+            Assert.assertTrue(execState.getState() == FragmentInstanceExecState.State.FINISHED);
+        }
+    }
 }
diff --git a/test/sql/test_insert_overwrite/R/test_insert_with_profile b/test/sql/test_insert_overwrite/R/test_insert_with_profile
new file mode 100644
index 00000000000000..3de8a11dc7fac6
--- /dev/null
+++ b/test/sql/test_insert_overwrite/R/test_insert_with_profile
@@ -0,0 +1,56 @@
+-- name: test_insert_with_profile
+create table t1(k int) 
+distributed by hash(k) buckets 96;
+-- result:
+-- !result
+create table t2(k int) 
+distributed by hash(k) buckets 96;
+-- result:
+-- !result
+set enable_profile=true;
+-- result:
+-- !result
+set big_query_profile_threshold='1s';
+-- result:
+-- !result
+set runtime_profile_report_interval=1;
+-- result:
+-- !result
+set pipeline_dop=96;
+-- result:
+-- !result
+insert into t1 select * from TABLE(generate_series(0, 10));
+-- result:
+-- !result
+select count(*) from t1;
+-- result:
+11
+-- !result
+admin enable failpoint 'report_exec_state_failed_status';
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+-- result:
+-- !result
+select count(*) from t2;
+-- result:
+7
+-- !result
\ No newline at end of file
diff --git a/test/sql/test_insert_overwrite/T/test_insert_with_profile b/test/sql/test_insert_overwrite/T/test_insert_with_profile
new file mode 100644
index 00000000000000..bd5e38c9d0817e
--- /dev/null
+++ b/test/sql/test_insert_overwrite/T/test_insert_with_profile
@@ -0,0 +1,26 @@
+-- name: test_insert_with_profile
+create table t1(k int) 
+distributed by hash(k) buckets 96;
+
+create table t2(k int) 
+distributed by hash(k) buckets 96;
+
+set enable_profile=true;
+set big_query_profile_threshold='1s';
+set runtime_profile_report_interval=1;
+set pipeline_dop=96;
+
+insert into t1 select * from TABLE(generate_series(0, 10));
+select count(*) from t1;
+
+admin enable failpoint 'report_exec_state_failed_status';
+
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+insert into t2 select a.k from t1 a join t1 b on a.k = b.k where a.k = 0 and b.k = 0;
+
+select count(*) from t2;
\ No newline at end of file


Reviews:
user: login: starrocks-cr[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-07T11:27:46Z
user: login: starrocks-cr[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-07T11:27:49Z
user: login: kevincai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T12:36:22Z
user: login: kevincai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T12:37:42Z
user: login: wyb
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T07:44:34Z
user: login: LiShuMing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T09:01:20Z
user: login: xiangguangyxg
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T10:00:30Z
user: login: kevincai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T13:40:21Z
user: login: kevincai
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T02:48:03Z
user: login: xiangguangyxg
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T07:58:02Z
user: login: kevincai
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T08:00:39Z
user: login: wyb
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T08:01:44Z
user: login: kangkaisen
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-09T09:25:35Z
user: login: stdpain
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T10:29:39Z

Review comments:
diff_hunk: @@ -479,6 +486,10 @@ private synchronized void transitionState(State from, State to) {
         }
     }
 
+    public State getState() {
+        return state;
+    }
+
     public enum State {
         CREATED,
         DEPLOYING,
path: fe/fe-core/src/main/java/com/starrocks/qe/scheduler/dag/FragmentInstanceExecState.java
user: login: starrocks-cr[bot]
body: The most risky bug in this code is:
Incorrect usage of `params.done` as a field/property, which may cause compilation errors.

You can modify the code like this:
```java
            default: {
                // if it's a runtime profile, return empty directly.
                if (!params.isDone()) {  // Assuming isDone() is the correct method to use here
                    return Optional.empty();
                }

                if (params.getStatus() == null || params.getStatus().getStatus_code() == TStatusCode.OK) {
                    transitionState(State.FINISHED);
                } else {
                    transitionState(State.FAILED);
                }
                return Optional.of(true);
            }
```
created_at: 2025-05-07T11:27:46Z
author_association: NONE
diff_hunk: @@ -1167,6 +1138,71 @@ public void updateFragmentExecStatus(TReportExecStatusParams params) {
         }
     }
 
+    /**
+     * Update runtime profile and load profile no matter the input params is a finish or runtime profile
+     */
+    private void updateRuntimeProfile(TReportExecStatusParams params,
+                                      FragmentInstanceExecState execState,
+                                      String instanceId) {
+        // update profile
+        try {
+            queryProfile.updateProfile(execState, params);
+            execState.updateRunningProfile(params);
+        } catch (Throwable e) {
+            LOG.warn("update profile failed {}", instanceId, e);
+        }
+
+        // update load profile
+        try {
+            lock();
+            queryProfile.updateLoadChannelProfile(params);
+        } catch (Throwable e) {
+            LOG.warn("update load channel profile failed {}", instanceId, e);
+        } finally {
+            unlock();
+        }
+
+        // update job progress
+        try {
+            updateJobProgress(params);
+        } catch (Throwable e) {
+            LOG.warn("update job progress failed {}", instanceId, e);
+        }
+
+        // update status
+        Status status = new Status(params.status);
+        if (!(returnedAllResults && status.isCancelled()) && !status.ok()) {
+            ConnectContext ctx = connectContext;
+            if (ctx != null) {
+                ctx.setErrorCodeOnce(status.getErrorCodeString());
+            }
+            LOG.warn("exec state report failed status={}, query_id={}, instance_id={}, backend_id={}",
+                    status, DebugUtil.printId(jobSpec.getQueryId()),
+                    DebugUtil.printId(params.getFragment_instance_id()),
+                    params.getBackend_id());
+            updateStatus(status, params.getFragment_instance_id());
+        }
+    }
+
+    /**
+     * Update the instance only if the instance is finished.
+     */
+    private void updateFinishInstance(TReportExecStatusParams params,
+                                      FragmentInstanceExecState execState,
+                                      String instanceId) {
+        try {
+            lock();
+            queryProfile.updateLoadInformation(execState, params);
+        } catch (Throwable e) {
+            LOG.warn("update load information failed {}", instanceId, e);
+        } finally {
+            unlock();
+        }
+
+        // NOTE: it's critical for query execution, and must be put after the profile update
+        queryProfile.finishInstance(params.getFragment_instance_id());
+    }
+
     @Override
     public synchronized void updateAuditStatistics(TReportAuditStatisticsParams params) {
         PQueryStatistics newAuditStatistics = AuditStatisticsUtil.toProtobuf(params.audit_statistics);
path: fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java
user: login: starrocks-cr[bot]
body: The most risky bug in this code is:
The `updateJobProgress` method is called inside `updateRuntimeProfile`, but the CompletableFuture chain seems to lack proper handling when exceptions occur, potentially missing `updateJobProgress`.

You can modify the code like this:
```java
final CompletableFuture<Void> future = CompletableFuture.completedFuture(null)
    .thenRun(() -> {
        try {
            updateRuntimeProfile(params, execState, instanceId);
        } catch (Throwable e) {
            LOG.warn("update runtime profile failed {}", instanceId, e);
        }
    })
    .handle((result, ex) -> {
        // Ensure updateJobProgress is called even if previous stages throw an exception
        if (ex != null) {
            LOG.warn("error occurred during CompletableFuture chain", ex);
        }
        try {
            updateJobProgress(params);
        } catch (Throwable jobProgressEx) {
            LOG.warn("update job progress failed {}", instanceId, jobProgressEx);
        }
        return null;
    });
```
This change ensures `updateJobProgress` is always attempted even in the presence of exceptions from earlier operations.
created_at: 2025-05-07T11:27:49Z
author_association: NONE
diff_hunk: @@ -1093,64 +1094,34 @@ public void updateFragmentExecStatus(TReportExecStatusParams params) {
         // NOTE:
         // The exec status would affect query schedule, so it must be updated no matter what exceptions happen.
         // Otherwise, the query might hang until timeout
-        if (!execState.updateExecStatus(params)) {
+        final Optional<Boolean> updateExecStatus = execState.updateExecStatus(params);
path: fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java
user: login: kevincai
body: wondering if a lock associated with the execState can be leveraged here so that the execStatus update for the same instance id can be only executed in sequence in this interface, which will be greatly reduce the complexity and lower the learning curve of this code snippet.
created_at: 2025-05-07T12:36:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -246,8 +246,13 @@ public void attachExecutionProfiles(Collection<FragmentInstanceExecState> execut
         profiles.forEach((k, v) -> fragmentProfiles.get(k).addChildren(v));
     }
 
-    public void finishInstance(TUniqueId instanceId) {
+    public synchronized void finishInstance(TUniqueId instanceId) {
path: fe/fe-core/src/main/java/com/starrocks/qe/scheduler/QueryRuntimeProfile.java
user: login: kevincai
body: this might not necessary since the `markedCountDown` is already synchronized. The only usage of this `synchronized` is to ensure the RuntimeException is correctly determined.
created_at: 2025-05-07T12:37:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -246,8 +246,13 @@ public void attachExecutionProfiles(Collection<FragmentInstanceExecState> execut
         profiles.forEach((k, v) -> fragmentProfiles.get(k).addChildren(v));
     }
 
-    public void finishInstance(TUniqueId instanceId) {
+    public synchronized void finishInstance(TUniqueId instanceId) {
path: fe/fe-core/src/main/java/com/starrocks/qe/scheduler/QueryRuntimeProfile.java
user: login: LiShuMing
body: fixed
created_at: 2025-05-08T09:01:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1186,6 +1186,11 @@ CONF_mInt64(mem_limited_chunk_queue_block_size, "8388608");
 
 CONF_Int32(internal_service_query_rpc_thread_num, "-1");
 CONF_Int32(internal_service_datacache_rpc_thread_num, "-1");
+// The retry times of rpc request to report exec rpc request to FE. The default value is 10,
+// which means that the rpc request will be retried 10 times if it fails.
+// Report exec rpc request is important for load job, if one instance report failed,
+// the load job will be hang until timeout.
+CONF_mInt32(report_exec_rpc_request_retry_num, "10");
path: be/src/common/config.h
user: login: kevincai
body: need to update the doc as well.
created_at: 2025-05-08T13:07:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,9 +153,10 @@ Status ExecStateReporter::report_exec_status(const TReportExecStatusParams& para
     TReportExecStatusResult res;
     Status rpc_status;
 
+    // since the caller(report_exec_state) has already retried 3 times, we don't need to retry again.
path: be/src/exec/pipeline/exec_state_reporter.cpp
user: login: kevincai
body: this comment is not accurate.
created_at: 2025-05-08T13:07:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -359,15 +360,23 @@ void GlobalDriverExecutor::report_exec_state(QueryContext* query_ctx, FragmentCo
 
     auto report_task = [params, exec_env, fe_addr, fragment_id]() {
         int retry_times = 0;
-        while (retry_times++ < 3) {
+        while (retry_times++ < config::report_exec_rpc_request_retry_num) {
path: be/src/exec/pipeline/pipeline_driver_executor.cpp
user: login: kevincai
body: need to capture the configvar into a local variable in case the configvar is changed in the middle of the retry.

Also a simple backoff strategy should be adopted here, to avoid in quick fails and retry too soon. (e.g. intermittent network failure)
created_at: 2025-05-08T13:08:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1094,63 +1094,31 @@ public void updateFragmentExecStatus(TReportExecStatusParams params) {
         // The exec status would affect query schedule, so it must be updated no matter what exceptions happen.
         // Otherwise, the query might hang until timeout
         if (!execState.updateExecStatus(params)) {
+            LOG.warn("duplicate report fragment exec status, query id: {}, instance id: {}, backend id: {}, " +
path: fe/fe-core/src/main/java/com/starrocks/qe/DefaultCoordinator.java
user: login: kevincai
body: may convert to info level, this is not a serious problem that should be drawn attention.
created_at: 2025-05-08T13:33:16Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-09T04:43:34Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=StarRocks_starrocks&pullRequest=58753) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [8 New issues](https://sonarcloud.io/project/issues?id=StarRocks_starrocks&pullRequest=58753&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=StarRocks_starrocks&pullRequest=58753&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=StarRocks_starrocks&pullRequest=58753&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=StarRocks_starrocks&pullRequest=58753&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=StarRocks_starrocks&pullRequest=58753&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=StarRocks_starrocks&pullRequest=58753)


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T07:33:11Z
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-09T07:35:32Z
body: #### [FE Incremental Coverage Report]

:white_check_mark: **pass** : 43 / 53 (81.13%)




#### file detail

|   |path|covered_line|new_line|coverage|not_covered_line_detail|
|----|----|----|----|----|----|
|:large_blue_circle:|com/starrocks/qe/DefaultCoordinator.java|36|46|78.26%|[1148, 1149, 1156, 1157, 1165, 1166, 1193, 1194, 1199, 1200]|
|:large_blue_circle:|com/starrocks/qe/scheduler/dag/FragmentInstanceExecState.java|3|3|100.00%|[]|
|:large_blue_circle:|com/starrocks/qe/scheduler/QueryRuntimeProfile.java|4|4|100.00%|[]|




author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-09T07:48:41Z
body: #### [BE Incremental Coverage Report]

:white_check_mark: **pass** : 11 / 12 (91.67%)




#### file detail

|   |path|covered_line|new_line|coverage|not_covered_line_detail|
|----|----|----|----|----|----|
|:large_blue_circle:|be/src/runtime/fragment_mgr.cpp|0|1|00.00%|[724]|
|:large_blue_circle:|be/src/exec/pipeline/pipeline_driver_executor.cpp|5|5|100.00%|[]|
|:large_blue_circle:|be/src/exec/pipeline/exec_state_reporter.cpp|1|1|100.00%|[]|
|:large_blue_circle:|be/src/util/thrift_rpc_helper.cpp|4|4|100.00%|[]|
|:large_blue_circle:|be/src/runtime/profile_report_worker.cpp|1|1|100.00%|[]|




author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-09T10:29:50Z
body: @Mergifyio backport branch-3.4
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-09T10:29:50Z
body: @Mergifyio backport branch-3.5
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-09T10:29:53Z
body: @Mergifyio backport branch-3.3
author_association: CONTRIBUTOR
user: login: mergify[bot]
created_at: 2025-05-09T10:30:18Z
body: > backport branch-3.4

#### ✅ Backports have been created

<details>

* [#58833 [BugFix] Fix possible duplicate finishInstance calls (backport #58753)](https://github.com/StarRocks/starrocks/pull/58833) has been created for branch `branch-3.4` but encountered conflicts

</details>


<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "backport branch-3.4", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR
user: login: mergify[bot]
created_at: 2025-05-09T10:30:19Z
body: > backport branch-3.5

#### ✅ Backports have been created

<details>

* [#58834 [BugFix] Fix possible duplicate finishInstance calls (backport #58753)](https://github.com/StarRocks/starrocks/pull/58834) has been created for branch `branch-3.5` but encountered conflicts

</details>


<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "backport branch-3.5", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR
user: login: mergify[bot]
created_at: 2025-05-09T10:30:21Z
body: > backport branch-3.3

#### ✅ Backports have been created

<details>

* [#58835 [BugFix] Fix possible duplicate finishInstance calls (backport #58753)](https://github.com/StarRocks/starrocks/pull/58835) has been created for branch `branch-3.3` but encountered conflicts

</details>


<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "backport branch-3.3", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR
user: login: LiShuMing
created_at: 2025-05-09T11:57:58Z
body: @mergify backport branch-3.3.14
author_association: CONTRIBUTOR
user: login: mergify[bot]
created_at: 2025-05-09T11:58:27Z
body: > backport branch-3.3.14

#### ✅ Backports have been created

<details>

* [#58837 [BugFix] Fix possible duplicate finishInstance calls (backport #58753)](https://github.com/StarRocks/starrocks/pull/58837) has been created for branch `branch-3.3.14` but encountered conflicts

</details>


<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "backport branch-3.3.14", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR