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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 835e867a848 branch-4.1: [fix](s3) handle S3 file writer async task 
submission failures #64779 (#65160)
835e867a848 is described below

commit 835e867a8480abd3fdd013c98a588b2cdc60ef33
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Jul 3 20:36:25 2026 +0800

    branch-4.1: [fix](s3) handle S3 file writer async task submission failures 
#64779 (#65160)
    
    Cherry-picked from #64779
    
    Co-authored-by: hui lai <[email protected]>
---
 be/src/io/fs/s3_file_writer.cpp                    | 50 ++++++++++---
 be/src/io/fs/s3_file_writer.h                      |  1 +
 .../s3/test_s3_file_writer_submit_error.groovy     | 84 ++++++++++++++++++++++
 3 files changed, 127 insertions(+), 8 deletions(-)

diff --git a/be/src/io/fs/s3_file_writer.cpp b/be/src/io/fs/s3_file_writer.cpp
index d543c09328d..ec8e54849f6 100644
--- a/be/src/io/fs/s3_file_writer.cpp
+++ b/be/src/io/fs/s3_file_writer.cpp
@@ -153,13 +153,30 @@ Status S3FileWriter::close(bool non_block) {
         _async_close_pack = std::make_unique<AsyncCloseStatusPack>();
         _async_close_pack->future = _async_close_pack->promise.get_future();
         s3_file_writer_async_close_queuing << 1;
-        return 
ExecEnv::GetInstance()->non_block_close_thread_pool()->submit_func([&]() {
+        Status submit_status = Status::OK();
+        DBUG_EXECUTE_IF("S3FileWriter.close.submit_async_close.inject_error", {
+            submit_status = 
Status::IOError("S3FileWriter.close.submit_async_close.inject_error");
+        });
+        if (submit_status.ok()) {
+            submit_status =
+                    
ExecEnv::GetInstance()->non_block_close_thread_pool()->submit_func([&]() {
+                        s3_file_writer_async_close_queuing << -1;
+                        s3_file_writer_async_close_processing << 1;
+                        _st = _close_impl();
+                        _async_close_pack->promise.set_value(_st);
+                        s3_file_writer_async_close_processing << -1;
+                    });
+        }
+        if (!submit_status.ok()) {
             s3_file_writer_async_close_queuing << -1;
-            s3_file_writer_async_close_processing << 1;
+            LOG(WARNING) << "failed to submit async close for "
+                         << _obj_storage_path_opts.path.native()
+                         << ", fallback to sync close, status=" << 
submit_status;
             _st = _close_impl();
             _async_close_pack->promise.set_value(_st);
-            s3_file_writer_async_close_processing << -1;
-        });
+            return _st;
+        }
+        return Status::OK();
     }
     _st = _close_impl();
     _state = State::CLOSED;
@@ -250,6 +267,20 @@ Status S3FileWriter::_build_upload_buffer() {
     return Status::OK();
 }
 
+Status S3FileWriter::_submit_upload_buffer(const std::shared_ptr<FileBuffer>& 
buf) {
+    _countdown_event.add_count();
+    DBUG_EXECUTE_IF("S3FileWriter.submit_upload_buffer.inject_error", {
+        auto st = 
Status::IOError("S3FileWriter.submit_upload_buffer.inject_error");
+        _complete_part_task_callback(st);
+        return st;
+    });
+    auto st = FileBuffer::submit(buf);
+    if (!st.ok()) [[unlikely]] {
+        _complete_part_task_callback(st);
+    }
+    return st;
+}
+
 Status S3FileWriter::_close_impl() {
     VLOG_DEBUG << "S3FileWriter::close, path: " << 
_obj_storage_path_opts.path.native();
 
@@ -276,9 +307,12 @@ Status S3FileWriter::_close_impl() {
     }
 
     if (_pending_buf != nullptr) { // there is remaining data in buffer need 
to be uploaded
-        _countdown_event.add_count();
-        RETURN_IF_ERROR(FileBuffer::submit(std::move(_pending_buf)));
+        auto st = _submit_upload_buffer(_pending_buf);
         _pending_buf = nullptr;
+        if (!st.ok()) {
+            _wait_until_finish("pending buffer submit failed");
+            return st;
+        }
     }
 
     RETURN_IF_ERROR(_complete());
@@ -330,9 +364,9 @@ Status S3FileWriter::appendv(const Slice* data, size_t 
data_cnt) {
                     RETURN_IF_ERROR(_create_multi_upload_request());
                 }
                 _cur_part_num++;
-                _countdown_event.add_count();
-                RETURN_IF_ERROR(FileBuffer::submit(std::move(_pending_buf)));
+                auto st = _submit_upload_buffer(_pending_buf);
                 _pending_buf = nullptr;
+                RETURN_IF_ERROR(st);
             }
             _bytes_appended += data_size_to_append;
         }
diff --git a/be/src/io/fs/s3_file_writer.h b/be/src/io/fs/s3_file_writer.h
index f31a9edef2c..5a8075e03cf 100644
--- a/be/src/io/fs/s3_file_writer.h
+++ b/be/src/io/fs/s3_file_writer.h
@@ -85,6 +85,7 @@ private:
     void _upload_one_part(int part_num, UploadFileBuffer& buf);
     bool _complete_part_task_callback(Status s);
     Status _build_upload_buffer();
+    Status _submit_upload_buffer(const std::shared_ptr<FileBuffer>& buf);
     void _record_close_latency();
 
     ObjectStoragePathOptions _obj_storage_path_opts;
diff --git 
a/regression-test/suites/cloud_p0/s3/test_s3_file_writer_submit_error.groovy 
b/regression-test/suites/cloud_p0/s3/test_s3_file_writer_submit_error.groovy
new file mode 100644
index 00000000000..17518c968b5
--- /dev/null
+++ b/regression-test/suites/cloud_p0/s3/test_s3_file_writer_submit_error.groovy
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_s3_file_writer_submit_error", "p0, nonConcurrent") {
+    if (!isCloudMode()) {
+        return
+    }
+
+    def submitUploadBufferErrorPoint = 
"S3FileWriter.submit_upload_buffer.inject_error"
+    def asyncCloseSubmitErrorPoint = 
"S3FileWriter.close.submit_async_close.inject_error"
+    def debugPoints = [submitUploadBufferErrorPoint, 
asyncCloseSubmitErrorPoint]
+
+    def disableDebugPoints = {
+        debugPoints.each { point ->
+            GetDebugPoint().disableDebugPointForAllBEs(point)
+        }
+    }
+
+    def insertAndCheck = { String label, boolean expectSuccess ->
+        def insertSql = "INSERT INTO test_s3_file_writer_submit_error " +
+                "VALUES (1, '${label}'), (2, '${label}')"
+        if (expectSuccess) {
+            sql insertSql
+        } else {
+            test {
+                sql insertSql
+                exception "S3FileWriter.submit_upload_buffer.inject_error"
+            }
+        }
+    }
+
+    def runWithDebugPoint = { String label, boolean expectSuccess, String 
point ->
+        GetDebugPoint().enableDebugPointForAllBEs(point)
+        try {
+            insertAndCheck(label, expectSuccess)
+        } finally {
+            GetDebugPoint().disableDebugPointForAllBEs(point)
+        }
+    }
+
+    sql """ DROP TABLE IF EXISTS test_s3_file_writer_submit_error """
+    sql """
+        CREATE TABLE IF NOT EXISTS test_s3_file_writer_submit_error (
+            `k1` int NULL,
+            `v1` varchar(32) NULL
+        ) ENGINE=OLAP
+        DUPLICATE KEY(`k1`)
+        DISTRIBUTED BY HASH(`k1`) BUCKETS 1
+        PROPERTIES (
+            "replication_allocation" = "tag.location.default: 1"
+        )
+    """
+
+    setBeConfigTemporary([
+        "enable_file_cache_adaptive_write": "false",
+        "enable_packed_file": "true",
+        "small_file_threshold_bytes": "1"
+    ]) {
+        try {
+            sql """ SET disable_file_cache = true """
+            disableDebugPoints()
+
+            runWithDebugPoint("submit_err", false, 
submitUploadBufferErrorPoint)
+            runWithDebugPoint("close_submit_err", true, 
asyncCloseSubmitErrorPoint)
+        } finally {
+            sql """ SET disable_file_cache = false """
+            disableDebugPoints()
+        }
+    }
+}


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

Reply via email to