feat(gax): add ResumableUploadResultRetryAlgorithm - #14225
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the ResumableUploadResultRetryAlgorithm class and its corresponding unit tests to handle retries for resumable uploads based on the Unified Resumable Upload Protocol specification. The feedback identifies an issue where non-ApiException throwables (such as IOException) would still be retried even when the RetryingContext explicitly overrides and restricts the retryable codes. To ensure consistency, the algorithm should return false for non-ApiExceptions when custom retryable codes are provided in the context, and a corresponding test assertion should be added to verify this behavior.
| if (context.getRetryableCodes() != null) { | ||
| if (previousThrowable instanceof ApiException) { | ||
| return context | ||
| .getRetryableCodes() | ||
| .contains(((ApiException) previousThrowable).getStatusCode().getCode()); | ||
| } | ||
| } |
There was a problem hiding this comment.
When context.getRetryableCodes() is not null, it indicates that the caller has explicitly overridden the retryable codes for this call (for example, by setting an empty set to disable retries).
In the current implementation, if previousThrowable is not an instance of ApiException (such as an IOException), the code falls through to shouldRetry(previousThrowable, previousResponse), which returns true for IOException. This means IOExceptions would still be retried even if retries were explicitly disabled or customized via the context.
To ensure consistency with other retry algorithms in GAX (like ApiExceptionRetryAlgorithm), we should return false for any non-ApiException when context.getRetryableCodes() is configured.
| if (context.getRetryableCodes() != null) { | |
| if (previousThrowable instanceof ApiException) { | |
| return context | |
| .getRetryableCodes() | |
| .contains(((ApiException) previousThrowable).getStatusCode().getCode()); | |
| } | |
| } | |
| if (context.getRetryableCodes() != null) { | |
| if (previousThrowable instanceof ApiException) { | |
| return context | |
| .getRetryableCodes() | |
| .contains(((ApiException) previousThrowable).getStatusCode().getCode()); | |
| } | |
| return false; | |
| } |
| // Default algorithm retries UNAVAILABLE, but context with empty codes forbids it | ||
| assertThat(algorithm.shouldRetry(contextWithEmptyCodes, unavailable, null)).isFalse(); | ||
|
|
There was a problem hiding this comment.
Add an assertion to verify that IOException is not retried when the context overrides the retryable codes (e.g., with an empty set).
| // Default algorithm retries UNAVAILABLE, but context with empty codes forbids it | |
| assertThat(algorithm.shouldRetry(contextWithEmptyCodes, unavailable, null)).isFalse(); | |
| // Default algorithm retries UNAVAILABLE, but context with empty codes forbids it | |
| assertThat(algorithm.shouldRetry(contextWithEmptyCodes, unavailable, null)).isFalse(); | |
| // IOException should also not be retried when retryable codes are overridden by the context | |
| IOException ioException = new IOException("connection reset"); | |
| assertThat(algorithm.shouldRetry(contextWithEmptyCodes, ioException, null)).isFalse(); |
e73249a to
5407505
Compare
b8f7fc8 to
4a9482c
Compare
5407505 to
b706b52
Compare
4a9482c to
28c4867
Compare
b706b52 to
3d41927
Compare
28c4867 to
19504ac
Compare
|
|


Work in progress - not ready for review