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
Don't retry streaming requests with blocks after data has been recieved #1617
Don't retry streaming requests with blocks after data has been recieved #1617
Conversation
This is not yet ready for review, there is a complication with |
07f1f5e
to
d099329
Compare
Ready for review. I changed the code to truncate the response target only in case of This PR also makes the assumption that the response target will always respond to |
Note that this addresses the feature request discussed in #1535 (that is, it will be fully addressed once |
d099329
to
9c746d0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good, just need some context to finalize.
Additionally, administrivia, can you confirm: "By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license." |
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. |
9c746d0
to
90c5d9b
Compare
@janko Is this PR still desirable? If so I will do a review and get this in. |
Yes, I think it would still be useful to have that capability. Personally I haven't had experience with downloading large S3 objects, so I don't know how often these network errors might happen. But considering that the SDK has default retries in normal cases, it seems like it does happen. I ideally wanted to later combine it with Range requests, so that we don't need to download content that's already "written". The desired logic would be similar to what google-api-client does. But I couldn't figure out how to make it work with client-side encryption. I guess I will try to find a way to skip that behaviour when client-side encryption is used. Anyway, that would be in a separate PR. |
Thanks for the info. The code looks good to me. I would gladly merge it in. There is however a broken test:
Could you update this PR to fix that? |
I attempted this but I hit a road block as well. It seems the chunk size is 64 while the response body is 48. This could be from cipher padding?
|
Thanks a lot for working on this 😃 Yes, the client-side encryption is definitely difficult to handle, I spent a lot of time trying to figure it out before. I think I would skip the retry mechanism when client-side encryption is used, if that's possible to detect. |
No problem. I wanted to waste my afternoon debugging this! (just kidding of course...) I found that it can certainly be skipped with:
We can do this as a last resort but where's the fun in that? If the chunk size is 64, but the response body size is 48, what are the missing 16 bytes? I feel like we're closer now. Edit: have to set this aside for now.. if you're feeling inspired, please take a look again! |
I've been looking into fixes for #2311 and this seems related. I've updated this PR with a fix for the S3 encryption client. The issue is that the underlying body that the IODecrypter wraps is NOT reset between retries (The IODecrypter is recreated with the previous StringIO on the new request). There are a few additional issues I think that I've tried to solve:
Additionally - this change will not work for the |
There are a few issues with specs from the merge with master, but I think we can resolve those fairly easily (mostly the changes need to be applied into the adaptive retry logic as well). However, there are still 2 main issues I mentioned above. Specifically:
@janko - Given these I think we have a few options and I wanted to get your take.
At this point I'd lean towards #2 - I'd prefer to reduce the complexity of the response_target and retry code for generic streaming operations and do provide the best support for retries of interrupted downloads in the get_object operations. What do you think? |
@alextwoods Thank you for picking this up, option 2️⃣ sounds good to me 👍 |
resets of IO setting of IO on error
I've created #2326 as a feature request for adding the S3 plugin for retry with range. I've updated this PR to keep a few things that will be useful for that request + fix #2311. If you're able to pitch in on the get_object specific retry w/ range (#2326 ) that would be great, otherwise, I'm hoping to have some time next week to pick it up. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good however merge from master looks to be wrong
@janko - I've created a draft PR for adding retry support using the range header for S3 get_objects: #2343 - Since get_objects is the only streaming operation that supports range its limited to that operation. Additionally because of the way we do retries, the code gets a bit messy. Lots of special cases. |
When downloading S3 objects with a block, aws-sdk-s3 internally creates a
BlockIO
to be the writable response target.Normally aws-sdk-s3 retries any failed downloads in case of network errors, but not for streaming downloads. This is because
BlockIO
is not truncatable, unlike a response target likeStringIO
orTempfile
, and aws-sdk-s3 needs it to be because it retries the whole download from the start.In Shrine and tus-ruby-server I'm using aws-sdk-s3's streaming download feature, mainly for streaming the S3 object through a web application. It would be great if these downloads were to be retried in case of network errors in this case as well, especially for tus-ruby-server where the S3 objects will typically be very large.
This PR adds this functionality. It does so by remembering how many bytes of the content was "written" to the response target so far; when the request is retried, part of the response body that has already been written is simply skipped, until it reaches the part it hasn't written yet.
After this PR I plan to add support for range requests, so that the requests are retried from the last byte offset. But for start I wanted to add this because I didn't know whether all S3 endpoints support range requests, so I thought we would still need this as a safety net.