Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mechanism to cancel the operation of uploading directory #75

Merged
merged 11 commits into from
Nov 22, 2024

Conversation

ysaito1001
Copy link
Contributor

@ysaito1001 ysaito1001 commented Nov 18, 2024

Issue #, if available:
Resolves a subtask in #65

Description of changes:
This PR introduces a mechanism to cancel the upload of multiple objects within a directory. It distinguishes between aborting and dropping a handle for both UploadHandle and UploadObjectsHandle. Note that this PR does not modify the UploadHandle functionality, except for adding some comments to the struct.

For both of the handles, the basic idea is

  • Dropping a handle abruptly terminates in-progress tasks. This occurs either when the handle is destroyed via RAII or when .drop() is called explicitly. The tasks will be canceled at the points where their futures are waiting, and we don't have much control over the cleanup process, which is left to the underlying async runtime.
  • Aborting a handle (via calling .abort() on it) allows tasks to complete their current work while ignoring any future tasks. The word "complete" has different meanings depending on whether the handle is an UploadHandle or an UploadObjectsHandle.
    • For an UploadHandle, it means that the control flow attempts to abort ongoing read and upload tasks, and it will invoke AbortMultipartUpload in the case of MPU.
    • For an UploadObjectsHandle, it means that each upload task either finishes uploading the current object or invokes .abort() on its respective single upload operation down the line.

For UploadObjectsHandle, this PR introduces a watch channel that signals when the operation enters cancellation mode and needs to be terminated gracefully. There are two ways to trigger cancellation:

  • A user explicitly calls .abort() on the UploadObjectsHandle.
  • A child operation fails while running with the abort policy.

Both cases are covered by newly added integration tests.

Design Discussions:

  • I once considered making the behavior of Drop the same as calling .abort()—for example, having fn drop(&mut self) call .abort(). However, since .drop is a synchronous method and .abort() is asynchronous, this led me to implement separate behaviors for the two.
  • Once the operation enters cancellation mode, .abort() (for both UploadHandle and UploadObjectsHandle) will only return the error that triggered the cancellation and will log any additional errors encountered along the way (e.g., errors from AbortMultipartUpload). We have TODO and I also left TODO to assess this behavior, but for now, the code after this PR "swallows" and logs any new errors encountered during the cancellation process.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@ysaito1001 ysaito1001 marked this pull request as ready for review November 19, 2024 17:12
@ysaito1001 ysaito1001 requested a review from a team as a code owner November 19, 2024 17:12
Copy link
Contributor

@aajtodd aajtodd left a comment

Choose a reason for hiding this comment

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

Overall looks good, couple questions/comments.

aws-s3-transfer-manager/src/error.rs Outdated Show resolved Hide resolved
@ysaito1001 ysaito1001 merged commit c427a5e into main Nov 22, 2024
13 checks passed
@ysaito1001 ysaito1001 deleted the ysaito/cancel-multi-uploads branch November 22, 2024 22:33
Comment on lines +43 to +46
/// In contrast, calling [`Self::abort`] attempts to cancel ongoing tasks more explicitly.
/// It first calls `.abort_all` on the tasks it owns, and then invokes `AbortMultipartUpload`
/// to abort any in-progress multipart uploads. Errors encountered during `AbortMultipartUpload`
/// are logged, but do not affect the overall cancellation flow.
Copy link
Contributor

Choose a reason for hiding this comment

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

trivial: Worth mentioning that the operation might already be complete, and we won't delete anything?

@@ -18,13 +42,61 @@ pub struct UploadObjectsHandle {

impl UploadObjectsHandle {
/// Consume the handle and wait for the upload to complete
///
/// When the `FailedTransferPolicy` is set to [`FailedTransferPolicy::Abort`], this method
/// will return an error if any of the spawned tasks encounter one. The other tasks will
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// will return an error if any of the spawned tasks encounter one. The other tasks will
/// will return the first error if any of the spawned tasks encounter one. The other tasks will

@@ -617,4 +703,35 @@ mod unit {
);
}
}

#[tokio::test]
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should add a test that tries to do MPU but fails and calls abortMultipart instead of completeMultipart.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. As discussed offline, a good place to add that test is probably an integration test file for uploading single object.

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.

3 participants