Skip to content

fix(io): attempt every S3 delete and report how many failed - #966

Open
plusplusjiajia wants to merge 4 commits into
apache:mainfrom
plusplusjiajia:fix-s3-delete-files-attempt-all
Open

plusplusjiajia wants to merge 4 commits into
apache:mainfrom
plusplusjiajia:fix-s3-delete-files-attempt-all

Conversation

@plusplusjiajia

@plusplusjiajia plusplusjiajia commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #898 (comment).

ArrowS3FileIO::DeleteFiles returned at the first credential prefix whose delete failed, so files under the remaining prefixes were never attempted. Java's S3FileIO.deleteFiles attempts every batch on a thread pool, logs each failed path, and throws BulkDeletionFailureException with the count.

This matches that:

  • Every file is attempted; each failure is logged, and the call returns Failed to delete N of M files.
  • Deletes run on up to s3.delete.num-threads threads (default: the number of hardware threads), as in Java.

Java also packs keys into DeleteObjects batches. Arrow has no public batch delete for S3, so each thread deletes one file at a time, as Arrow's own DeleteFiles does. ResolvingFileIO stays fail-fast across delegates, as Java's does.

DeleteFilesAttemptsEveryFile puts an allowed file between two denied ones; it fails on main and passes here. This touches the same function as #898, so whichever lands second needs a small rebase.

ArrowS3FileIO::DeleteFiles grouped locations by credential prefix and
returned at the first group that failed, so files in later groups were
never attempted. Java's S3FileIO attempts every batch, logs each failed
path and reports the failure count.

Delete each file through its delegate, log each failure, and return
"Failed to delete N of M files" at the end. This sends the same requests
as before, since Arrow's S3 DeleteFiles also deletes one file at a time.
ResolvingFileIO stays fail-fast across delegates, as it is in Java.
size_t failed = 0;
for (const auto& file_location : file_locations) {
locations_by_io[&FileIOForPath(file_location)].push_back(file_location);
if (auto status = FileIOForPath(file_location).DeleteFile(file_location);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use a thread pool to delete files concurrently? Sequential S3 deletes could be slow for large batches. Java's S3FileIO does that, so maybe we should pursue that too, but not a requirement for this PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhjwpku Thanks! Done: deletes now run on up to s3.delete.num-threads threads (default: hardware threads), as in Java. Java also batches keys into DeleteObjects; Arrow has no public batch delete for S3, so each thread deletes one file at a time.

Java's S3FileIO runs its deletes on an executor sized by
s3.delete.num-threads, which defaults to the number of processors.
DeleteFiles now does the same, with the calling thread taking part.

Java also packs keys into DeleteObjects batches. Arrow has no public
batch delete for S3, so each thread still deletes one file at a time,
as Arrow's own DeleteFiles does.
std::jthread needs -fexperimental-library with libc++ 18 and 19, which
the Clang 18+ requirement covers, so use std::async futures instead;
they also wait for their thread if an exception unwinds.

Helper threads now bind the caller's logger, as logger.h prescribes for
thread pools. Without it their warnings went to the global logger while
the returned error only carries the failure count.
wait() leaves an exception stored in a helper's future, so a delete that
threw was neither counted nor reported, and DeleteFiles could return
success. get() rethrows it on the calling thread, as the sequential loop
did.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants