Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public boolean apply(Throwable e)
// This can happen sometimes when AWS isn't able to obtain the credentials for some service:
// https://github.com/aws/aws-sdk-java/issues/2285
return true;
} else if (e instanceof AmazonS3Exception && ((AmazonS3Exception) e).getStatusCode() == 200 &&
(e.getMessage().contains("InternalError") || e.getMessage().contains("SlowDown"))) {
// This can happen sometimes when AWS returns a 200 response with internal error message
// https://repost.aws/knowledge-center/s3-resolve-200-internalerror
Copy link
Contributor

Choose a reason for hiding this comment

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

This link also suggests SlowDown keyword might need similar behaviour, shall we add that too?

Quoting from the link:

A 200 response with InternalError or SlowDown is similar to a 5xx error. Because Amazon S3 is a distributed system, it's normal to see a small percentage of 200 internal errors. It's a best practice to retry these requests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also check the status code ? Internal error seems to be a bit too loose no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will add both

return true;
} else if (e instanceof InterruptedException) {
Thread.interrupted(); // Clear interrupted state and not retry
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,101 @@ public void testRetryWithSdkClientException() throws Exception
);
Assert.assertEquals(maxRetries, count.get());
}

@Test
public void testRetryWithAmazonS3InternalError() throws Exception
{
final int maxRetries = 3;
final AtomicInteger count = new AtomicInteger();
S3Utils.retryS3Operation(
() -> {
if (count.incrementAndGet() >= maxRetries) {
return "donezo";
} else {
AmazonS3Exception s3Exception = new AmazonS3Exception("We encountered an internal error. Please try again. (Service: Amazon S3; Status Code: 200; Error Code: InternalError; Request ID: some-id)");
s3Exception.setStatusCode(200);
throw s3Exception;
}
},
maxRetries
);
Assert.assertEquals(maxRetries, count.get());
}

@Test
public void testRetryWithAmazonS3SlowDown() throws Exception
{
final int maxRetries = 3;
final AtomicInteger count = new AtomicInteger();
S3Utils.retryS3Operation(
() -> {
if (count.incrementAndGet() >= maxRetries) {
return "success";
} else {
AmazonS3Exception s3Exception = new AmazonS3Exception("Please reduce your request rate. SlowDown");
s3Exception.setStatusCode(200);
throw s3Exception;
}
},
maxRetries
);
Assert.assertEquals(maxRetries, count.get());
}

@Test
public void testNoRetryWithAmazonS3InternalErrorNon200Status()
{
final AtomicInteger count = new AtomicInteger();
Assert.assertThrows(
Exception.class,
() -> S3Utils.retryS3Operation(
() -> {
count.incrementAndGet();
AmazonS3Exception s3Exception = new AmazonS3Exception("InternalError occurred");
s3Exception.setStatusCode(403);
throw s3Exception;
},
3
)
);
Assert.assertEquals(1, count.get());
}

@Test
public void testNoRetryWithAmazonS3SlowDownNon200Status()
{
final AtomicInteger count = new AtomicInteger();
Assert.assertThrows(
Exception.class,
() -> S3Utils.retryS3Operation(
() -> {
count.incrementAndGet();
AmazonS3Exception s3Exception = new AmazonS3Exception("SlowDown message");
s3Exception.setStatusCode(404);
throw s3Exception;
},
3
)
);
Assert.assertEquals(1, count.get());
}

@Test
public void testRetryWithAmazonS3Status200ButDifferentError()
{
final AtomicInteger count = new AtomicInteger();
Assert.assertThrows(
Exception.class,
() -> S3Utils.retryS3Operation(
() -> {
count.incrementAndGet();
AmazonS3Exception s3Exception = new AmazonS3Exception("Some other error message");
s3Exception.setStatusCode(200);
throw s3Exception;
},
3
)
);
Assert.assertEquals(1, count.get());
}
}
Loading