I have a service that calls a 3rd party rate-limited API. The API follows the common pattern of returning a 429
response code w/ a Retry-After
header containing the amount of time to wait before making another API call when the allowed rate has been exceeded.
I want to use spring-retry's @Retryable
annotation with delayExpression
to dynamically set the delay for each retry based on the Retry-After
value returned from the API. I reviewed the spring-retry documentation but could not figure out how (or if it is even possible) to reference the thrown exception in the delayExpression
.
It seems you can reference the thrown exception as #root
in the @Retryable.exceptionExpression
, but in @Backoff.delayExpression
I can only access the method arguments on the #root
object - I can't figure out how to reference the thrown exception.
Is what I am trying to do possible? I know about RetryTemplate
but would like to keep this simple w/ @Retryable
if I can!
@Component
public class MyService {
@Retryable(retryFor = {RateLimitExceededException.class},
backoff = @Backoff(delayExpression = "#root.retryAfter")) // I want the value from thrownException.retryAfter
public void doSomeApiCall(String parameters) {
// do some API call, get a 429 response
long retryAfter = 30000; // dynamic, parsed from API response's Retry-After header
throw new RateLimitExceededException(retryAfter);
}
public static class RateLimitExceededException extends RuntimeException {
protected long retryAfter;
public RateLimitExceededException(long retryAfter) {
this.retryAfter = retryAfter;
}
public long getRetryAfter() { return this.retryAfter; }
}
}
You are correct; currently, only the method arguments are available for evaluating the expression at runtime.
Feel free to open a new feature request issue on GitHub.
https://github.com/spring-projects/spring-retry/issues
You can, however, use @Retryable
with a custom interceptor (using a RetryTemplate
) so the business code can remain simple.
/**
* Retry interceptor bean name to be applied for retryable method. Is mutually
* exclusive with other attributes.
* @return the retry interceptor bean name
*/
String interceptor() default "";