0

So I'm trying to retry for specific exceptions and created a bean which has shouldRetry(Throwable t) function. The function returns true if exception has to be retried, otherwise false. But What I'm observing is shouldRetry(Throwable t) is executing twice(log is printing twice) for one retry attempt, however serviceImpl from where exception is being thrown is executing only once for one retry attempt.

Could someone please let me know if I'm doing something wrong here, or is it the default behavior/bug with spring retry itself.

@Component("dbRecoverableExceptionHandler")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class DBRecoverableExceptionHandler {

  private final Environment environment;
  private final MultiTaggedCounter exceptionRetryCounter;

  public Boolean isRetryable(Throwable t) {

    String[] recoverableExceptionClasses = environment
        .getRequiredProperty("db-recoverable-exception-classes", String[].class);

    for (String s1 : recoverableExceptionClasses) {
      if (t.getClass().getSimpleName().contains(s1)) {
        exceptionRetryCounter.increment(1, s1);
        log.warn("Retrying for exception " + t.toString());
        return true;
      }
    }
    return false;
  }
}

       
        
        @Retryable(exceptionExpression = "#{@dbRecoverableExceptionHandler.isRetryable(#root)}",
              maxAttemptsExpression = "#{${max-attempts}}",
              backoff = @Backoff(delayExpression = "#{${retry-backoff-delay-time}}",
                  multiplierExpression = "#{${retry-backoff-multiplier}}"))
greeshma
  • 1
  • 2

1 Answers1

1

It is as expected.

The method will be called by the RetryTemplate twice for each execution...

while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {

    try {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Retry: count=" + context.getRetryCount());
        }
        // Reset the last exception, so if we are successful
        // the close interceptors will not think we failed...
        lastException = null;
        return retryCallback.doWithRetry(context);
    }
    catch (Throwable e) {

        lastException = e;

        try {
            registerThrowable(retryPolicy, state, context, e);
        }
        catch (Exception ex) {
            throw new TerminatedRetryException("Could not register throwable",
                    ex);
        }
        finally {
            doOnErrorInterceptors(retryCallback, context, e);
        }

        if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
...

The first call to canRetry() (in the while loop) is skipped on the very first call since there is no exception yet, on subsequent iterations, when the method throws an exception, it is called twice.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks Gary for confirming, is there any way I can get number of retries in prometheus metrics, if you see in my code, I added exceptionRetryCounter, like you said, since this method executes twice, counter is showing double the number of retries. – greeshma Apr 07 '21 at 09:03
  • There is nothing built in for that. You can add a `RetryListener` bean in the `listeners` property; the `onError` method will only be called once for each failure. – Gary Russell Apr 07 '21 at 13:07
  • You can also use `RetrySynchronizationManager.getContext().getRetryCount()` instead of incrementing your own counter. – Gary Russell Apr 07 '21 at 15:29
  • Thanks Gary, I'll try that – greeshma Apr 09 '21 at 09:15