We use the Spring Retry logic to have several attempts on OptimisticLockingExceptions
and deadlocks before a failure is returned.
Since deadlocks in the database are not thrown as specific exceptions, I figured out to ‘catch’ them within a custom RetryPolicy
:
public class NextRetryPolicy extends SimpleRetryPolicy {
private final static NextLogger LOGGER = NextLogger.RETRY_LOGGER_INSTANCE;
public NextRetryPolicy(int maxAttempts) {
// Instantiate super class with OptimisticLockingExceptions as retryable and for counting attempts
super(maxAttempts, Map.ofEntries(new AbstractMap.SimpleEntry<Class<? extends Throwable>, Boolean>(OptimisticLockingException.class, true)), true);
}
@Override
public boolean canRetry(RetryContext context) {
// Log first actual retry
boolean simpleRetry = super.canRetry(context);
if (simpleRetry) {
if (context.getLastThrowable() != null) {
LOGGER.debug("Retry operation on exception [ {} ] with attempt count [ {} ]", context.getLastThrowable().getClass().getName(), context.getRetryCount());
} else if (context.getRetryCount() > 0) {
LOGGER.debug("Retry operation with attempt count [ {} ]", context.getRetryCount());
}
return true;
}
if (context.getRetryCount() >= this.getMaxAttempts()) {
return false;
}
// Check for deadlock exception and allow retry in these cases, too
int indexOfDbException = ExceptionUtils.indexOfType(context.getLastThrowable(), BatchUpdateException.class);
if (indexOfDbException > -1) {
Throwable dbUpdateException = ExceptionUtils.getThrowableList(context.getLastThrowable()).get(indexOfDbException);
if (dbUpdateException.getMessage().contains("deadlock detected")) {
LOGGER.debug("Retry operation on deadlock (last exception [ {} ]) with attempt count [ {} ]", context.getLastThrowable().getClass().getName(), context.getRetryCount());
return true;
}
}
return false;
}
}