mrhhsg commented on code in PR #68032:
URL: https://github.com/apache/doris/pull/68032#discussion_r4296005934


##########
be/src/io/fs/s3_file_writer.h:
##########
@@ -21,6 +21,7 @@
 
 #include <chrono>
 #include <cstddef>
+#include <functional>

Review Comment:
   Follow-up in e42e09d2298: `S3FileWriter` gets one addition, an opt-in 
`abort()`. A failed or discarded spill part leaves the parts of its multipart 
upload behind; deleting the spill prefix does not remove them (they are not 
listed as objects), so they stay billed until the bucket lifecycle rule aborts 
the upload. Before, the spill writer aborted it itself, rebuilding the bucket, 
key and upload id from the writer's getters and calling the object storage 
client directly. `abort()` keeps that inside `S3FileWriter`: it skips the 
buffers not uploaded yet, waits for the running uploads and aborts the 
multipart upload if one was started. It is only allowed on an open writer or 
one whose `close()` failed. Destroying a writer still never aborts and no 
existing caller changes, since an upload may be completed elsewhere (the S3 
committer completes them on FE); only the spill writer calls it.
   



##########
be/src/exec/spill/spill_file_writer.cpp:
##########
@@ -90,42 +146,208 @@ Status SpillFileWriter::_close_current_part(const 
std::shared_ptr<SpillFile>& sp
     _part_meta.append((const char*)&_part_max_sub_block_size, 
sizeof(_part_max_sub_block_size));
     _part_meta.append((const char*)&_part_written_blocks, 
sizeof(_part_written_blocks));
 
-    {
+    int64_t meta_size = _part_meta.size();
+    // The footer must always be written so that the part can be closed; 
account it
+    // without checking the capacity limit.
+    Status status = _data_dir->try_reserve(meta_size, /*force=*/true);
+    if (status.ok()) {
         SCOPED_TIMER(_write_file_timer);
-        RETURN_IF_ERROR(_file_writer->append(_part_meta));
+        status = _append_to_part(Slice(_part_meta));
+        if (!status.ok()) {
+            _data_dir->release(meta_size);
+        }
     }
 
-    int64_t meta_size = _part_meta.size();
-    _part_written_bytes += meta_size;
-    COUNTER_UPDATE(_write_file_total_size, meta_size);
+    if (status.ok()) {
+        _part_written_bytes += meta_size;
+        COUNTER_UPDATE(_write_file_total_size, meta_size);
+        if (_resource_ctx) {
+            if (_data_dir->is_remote()) {
+                
_resource_ctx->io_context()->update_spill_write_bytes_to_remote_storage(meta_size);
+            } else {
+                
_resource_ctx->io_context()->update_spill_write_bytes_to_local_storage(meta_size);
+            }
+        }
+        if (_write_file_current_size) {
+            COUNTER_UPDATE(_write_file_current_size, meta_size);
+        }
+        
ExecEnv::GetInstance()->spill_file_mgr()->update_spill_write_bytes(meta_size);
+        // Incrementally update SpillFile's accounting so gc() can always
+        // decrement the correct amount, even if close() is never called.
+        if (spill_file) {
+            spill_file->update_written_bytes(meta_size);
+        }
+    }
+
+    // Close synchronously. Spilling already runs on a thread that may block 
on every append
+    // (the pipeline execution thread running the revoke task, or the async 
result-writer
+    // thread for Iceberg), and with the default part size the wait at a part 
boundary (the
+    // tail of the uploads plus one CompleteMultipartUpload) is negligible 
against the part
+    // itself, so overlapping it with the next part is not worth an async 
close pipeline.
+    std::unique_ptr<io::FileWriter> writer = std::move(_file_writer);
+    MultipartUploadId upload = _multipart_upload_id(writer.get());
+    if (status.ok()) {
+        MonotonicStopWatch close_watch;
+        close_watch.start();
+        status = writer->close();

Review Comment:
   Correction to the reply above: since e42e09d2298, `s3_file_writer.{h,cpp}` 
differ from master by one addition, an opt-in `abort()` used by spill to give 
up a failed or discarded part (see the reply on the "no need to modify s3 file 
writer" thread). It does not change this thread's conclusion: 
`check_after_upload()` is called only from `_complete()` and `_put_object()` 
inside `_close_impl()`, and `abort()` calls neither (it skips the queued 
buffers, waits for the running uploads and sends AbortMultipartUpload; 
`close()` is refused afterwards), so spill reaches `check_after_upload()` 
exactly as every other S3 upload on master does.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to