-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Resume partial download from S3 on connection drop #46589
Resume partial download from S3 on connection drop #46589
Conversation
Today if the connection to S3 times out or drops after starting to download an object then the SDK does not attempt to recover or resume the download, causing the restore of the whole shard to fail and retry. This commit allows Elasticsearch to detect such a mid-stream failure and to resume the download from where it failed.
Pinging @elastic/es-distributed |
The retry behaviour is perhaps not ideal here - we only fail completely after I'm also not sure about trying to handle the case where a retrying |
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.
LGTM, thanks @DaveCTurner good stuff :)
...ins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RetryingInputStream.java
Outdated
Show resolved
Hide resolved
...ins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RetryingInputStream.java
Outdated
Show resolved
Hide resolved
...ins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RetryingInputStream.java
Outdated
Show resolved
Hide resolved
I think this is fine. We're not using these streams in any tricky-async way anyway so if we run into an |
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.
This looks very nice, thanks @DaveCTurner. I left a comment on ensuring that a closed S3 stream will prevent more reads.
public void testReadNonexistentBlobThrowsNoSuchFileException() { | ||
final BlobContainer blobContainer = createBlobContainer(between(1, 5), null, null, null); | ||
final Exception exception = expectThrows(NoSuchFileException.class, () -> blobContainer.readBlob("read_nonexistent_blob")); | ||
assertThat(exception.getMessage().toLowerCase(Locale.ROOT), |
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.
nit: can fit on the same line
...pository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java
Outdated
Show resolved
Hide resolved
...pository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java
Outdated
Show resolved
Hide resolved
...pository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java
Outdated
Show resolved
Hide resolved
...ins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RetryingInputStream.java
Show resolved
Hide resolved
Thanks @tlrx and @original-brownbear. I thought about this overnight and decided it'd be safer to limit the number of retries per blob rather than per I also think it might be useful to collect the exceptions that result in retries and add them as suppressed exceptions in case we hit the limit and fail the download. WDYT? |
I agree
I also thought about it when reviewing the PR. I think we can add them as suppressed exceptions but maybe just limit their numbers in case the max retries is set to a large value? |
Yes, a bound is a good idea. I added suppressed exceptions in 22f5703. |
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.
LGTM, thanks David!
/** | ||
* Wrapper around an S3 object that will retry the {@link GetObjectRequest} if the download fails part-way through, resuming from where | ||
* the failure occurred. This should be handled by the SDK but it isn't today. This should be revisited in the future (e.g. before removing | ||
* the {@link Version#V_7_0_0} version constant) and removed when the SDK handles retries itself. |
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.
+1, otherwise we'll adding retries over retries
I'm not sure sure about this. This has the strange side effect that once again larger blobs are more likely to fail downloading which is exactly what motivated this in the first place. What do you think is the safety issue here? But if you're still worried after that argument, could we make the retry count proportional to the size of the blob (could make it proportional to the blob's size divided by the upload chunk size we used for it ... it may have changed since the snapshot was taken but it's a good enough estimate IMO)? |
I am thinking of something like a badly-configured security device that drops connections it believes to have downloaded an unreasonable amount of data like, say, 1MB. In the presence of such a device I don't think we should push on through and download each object in 1MB chunks - this would result in a rather surprising bill at the end of the month! By default each blob has bounded size (1GB) which bounds the probability of it failing outright. I think the default of 3 retries for each chunk is ample. |
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.
I am thinking of something like a badly-configured security device that drops connections it believes to have downloaded an unreasonable amount of data like, say, 1MB. In the presence of such a device I don't think we should push on through and download each object in 1MB chunks - this would result in a rather surprising bill at the end of the month!
Assuming we want to protect people from this LGTM :)
In practice this fixes our issues anyway I think, given the low rate of failure we were observing. Sorry for the noise :)
} | ||
|
||
@Override | ||
public synchronized void reset() { |
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.
no need to make this synchronized
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.
Thanks, not sure where that came from. Fixed in 3f8c20e.
|
||
/** | ||
* Wrapper around an S3 object that will retry the {@link GetObjectRequest} if the download fails part-way through, resuming from where | ||
* the failure occurred. This should be handled by the SDK but it isn't today. This should be revisited in the future (e.g. before removing |
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.
can you link to the corresponding open AWS SDK issue? i.e. aws/aws-sdk-java#856
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.
Yes, done in 3f8c20e. I am not convinced that that's the whole issue, because the problem we were chasing was to do with S3 actively closing the connection rather than a network timeout, but there doesn't seem to be an issue for that.
Today if the connection to S3 times out or drops after starting to download an object then the SDK does not attempt to recover or resume the download, causing the restore of the whole shard to fail and retry. This commit allows Elasticsearch to detect such a mid-stream failure and to resume the download from where it failed.
Exactly as elastic#46589 (and kept as close to it as possible code wise so we can dry things up in a follow-up potentially) but for GCS. Closes elastic#52319
* Add Blob Download Retries to GCS Repository Exactly as elastic#46589 (and kept as close to it as possible code wise so we can dry things up in a follow-up potentially) but for GCS. Closes elastic#52319
* Add Blob Download Retries to GCS Repository Exactly as elastic#46589 (and kept as close to it as possible code wise so we can dry things up in a follow-up potentially) but for GCS. Closes elastic#52319
Today if the connection to S3 times out or drops after starting to download an
object then the SDK does not attempt to recover or resume the download, causing
the restore of the whole shard to fail and retry. This commit allows
Elasticsearch to detect such a mid-stream failure and to resume the download
from where it failed.