This is an automated email from the ASF dual-hosted git repository.

sollhui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 4a49d3434b0 [fix](be) Synchronize load error log access (#66250)
4a49d3434b0 is described below

commit 4a49d3434b01338ab36d196bd6f7f0a7d2a26a53
Author: Refrain <[email protected]>
AuthorDate: Tue Aug 18 15:07:08 2026 +0800

    [fix](be) Synchronize load error log access (#66250)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #41320
    
    Load error-log writing and periodic status reporting can concurrently
    access the same `std::ofstream`. The reporting path may close and upload
    the stream while the writer is appending an error row, causing a data
    race and a BE SIGSEGV in the stream write path.
    
    This PR uses `_load_error_log_lock` to serialize stream creation and
    writes and to protect the first-error and path state. A separate
    `_s3_error_log_file_lock` serializes remote upload and URL publication,
    while synchronous S3 I/O runs without holding the load-error writer
    lock.
---
 be/src/runtime/runtime_state.cpp | 87 ++++++++++++++++++++++++++--------------
 be/src/runtime/runtime_state.h   |  5 ++-
 2 files changed, 61 insertions(+), 31 deletions(-)

diff --git a/be/src/runtime/runtime_state.cpp b/be/src/runtime/runtime_state.cpp
index 380bc8f8f72..50740802417 100644
--- a/be/src/runtime/runtime_state.cpp
+++ b/be/src/runtime/runtime_state.cpp
@@ -436,6 +436,21 @@ Status 
RuntimeState::append_error_msg_to_file(std::function<std::string()> line,
     if (query_type() != TQueryType::LOAD) {
         return Status::OK();
     }
+
+    const auto error_limit_status = [this]() -> Status {
+        if (_load_zero_tolerance) {
+            return Status::DataQualityError(
+                    "Encountered unqualified data, stop processing. Please 
check if the source "
+                    "data matches the schema, and consider disabling strict 
mode or increasing "
+                    "max_filter_ratio.");
+        }
+        return Status::OK();
+    };
+    if (_num_print_error_rows.load(std::memory_order_relaxed) > MAX_ERROR_NUM) 
{
+        return error_limit_status();
+    }
+
+    std::lock_guard<std::mutex> l(_load_error_log_lock);
     // If file haven't been opened, open it here
     if (_error_log_file == nullptr) {
         Status status = create_error_log_file();
@@ -452,14 +467,7 @@ Status 
RuntimeState::append_error_msg_to_file(std::function<std::string()> line,
     }
     // If num of printed error row exceeds the limit, don't add error messages 
to error log file any more
     if (_num_print_error_rows.fetch_add(1, std::memory_order_relaxed) > 
MAX_ERROR_NUM) {
-        // if _load_zero_tolerance, return Error to stop the load process 
immediately.
-        if (_load_zero_tolerance) {
-            return Status::DataQualityError(
-                    "Encountered unqualified data, stop processing. Please 
check if the source "
-                    "data matches the schema, and consider disabling strict 
mode or increasing "
-                    "max_filter_ratio.");
-        }
-        return Status::OK();
+        return error_limit_status();
     }
 
     fmt::memory_buffer out;
@@ -481,33 +489,52 @@ Status 
RuntimeState::append_error_msg_to_file(std::function<std::string()> line,
     return Status::OK();
 }
 
+std::string RuntimeState::get_first_error_msg() const {
+    std::lock_guard<std::mutex> l(_load_error_log_lock);
+    return _first_error_msg;
+}
+
 std::string RuntimeState::get_error_log_file_path() {
-    DBUG_EXECUTE_IF("RuntimeState::get_error_log_file_path.block", {
-        if (!_error_log_file_path.empty()) {
-            std::this_thread::sleep_for(std::chrono::seconds(1));
+    std::lock_guard<std::mutex> s3_lock(_s3_error_log_file_lock);
+    std::shared_ptr<io::S3FileSystem> s3_error_fs;
+    std::string local_error_log_file_path;
+    std::string remote_error_log_file_path;
+    {
+        std::lock_guard<std::mutex> load_lock(_load_error_log_lock);
+        DBUG_EXECUTE_IF("RuntimeState::get_error_log_file_path.block", {
+            if (!_error_log_file_path.empty()) {
+                std::this_thread::sleep_for(std::chrono::seconds(1));
+            }
+        });
+        if (!_s3_error_fs || !_error_log_file || !_error_log_file->is_open()) {
+            return _error_log_file_path;
         }
-    });
-    std::lock_guard<std::mutex> l(_s3_error_log_file_lock);
-    if (_s3_error_fs && _error_log_file && _error_log_file->is_open()) {
+
         // close error log file
         _error_log_file->close();
-        std::string error_log_absolute_path =
-                
_exec_env->load_path_mgr()->get_load_error_absolute_path(_error_log_file_path);
-        // upload error log file to s3
-        Status st = _s3_error_fs->upload(error_log_absolute_path, 
_s3_error_log_file_path);
-        if (!st.ok()) {
-            // upload failed and return local error log file path
-            LOG(WARNING) << "Fail to upload error file to s3, 
error_log_file_path="
-                         << _error_log_file_path << ", error=" << st;
-            return _error_log_file_path;
-        }
-        // expiration must be less than a week (in seconds) for presigned url
-        static const unsigned EXPIRATION_SECONDS = 7 * 24 * 60 * 60 - 1;
-        // Use public or private endpoint based on configuration
-        _error_log_file_path =
-                _s3_error_fs->generate_presigned_url(_s3_error_log_file_path, 
EXPIRATION_SECONDS,
-                                                     
config::use_public_endpoint_for_error_log);
+        s3_error_fs = _s3_error_fs;
+        local_error_log_file_path = _error_log_file_path;
+        remote_error_log_file_path = _s3_error_log_file_path;
     }
+
+    std::string error_log_absolute_path =
+            
_exec_env->load_path_mgr()->get_load_error_absolute_path(local_error_log_file_path);
+    // upload error log file to s3
+    Status st = s3_error_fs->upload(error_log_absolute_path, 
remote_error_log_file_path);
+    if (!st.ok()) {
+        // upload failed and return local error log file path
+        LOG(WARNING) << "Fail to upload error file to s3, error_log_file_path="
+                     << local_error_log_file_path << ", error=" << st;
+        return local_error_log_file_path;
+    }
+    // expiration must be less than a week (in seconds) for presigned url
+    static const unsigned EXPIRATION_SECONDS = 7 * 24 * 60 * 60 - 1;
+    // Use public or private endpoint based on configuration
+    auto presigned_url =
+            s3_error_fs->generate_presigned_url(remote_error_log_file_path, 
EXPIRATION_SECONDS,
+                                                
config::use_public_endpoint_for_error_log);
+    std::lock_guard<std::mutex> load_lock(_load_error_log_lock);
+    _error_log_file_path = std::move(presigned_url);
     return _error_log_file_path;
 }
 
diff --git a/be/src/runtime/runtime_state.h b/be/src/runtime/runtime_state.h
index a3cfc5e4cad..bd9b849ba7a 100644
--- a/be/src/runtime/runtime_state.h
+++ b/be/src/runtime/runtime_state.h
@@ -340,7 +340,7 @@ public:
 
     std::string get_error_log_file_path();
 
-    std::string get_first_error_msg() const { return _first_error_msg; }
+    std::string get_first_error_msg() const;
 
     // append error msg and error line to file when loading data.
     // is_summary is true, means we are going to write the summary line
@@ -1029,6 +1029,9 @@ private:
     std::shared_ptr<io::S3FileSystem> _s3_error_fs;
     // error file path on s3, 
${bucket}/${prefix}/error_log/${label}_${fragment_instance_id}
     std::string _s3_error_log_file_path;
+    // Protects the load error log stream, paths, and first error message.
+    mutable std::mutex _load_error_log_lock;
+    // Serializes S3 upload and presigned URL publication.
     std::mutex _s3_error_log_file_lock;
 
     // used for encoding the global lazy materialize


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to