У меня есть пружинный RetryTemplate, оборачивающий набор вызовов http в службу обертывания БД, которая при объединении имеет весь транзакционный характер, что означает, что в случае сбоя последнего из них мне нужно откатить все ранее выполненные операции и начать обработку все кончено.
Проблема в том, что восстановление выполняется только после последней попытки сбоя, что делает проблему больше, чем я пытался решить:
У меня есть состояние БД, несовместимое между каждой попыткой.
Как мне выполнить восстановление после каждой неудачной попытки?
Я сам пытался написать шаблонный код. Помимо очень уродливого вида, я предпочитаю хорошо проверенный и разработанный метод.
//I'm only experimenting now, when I'm done the template and policy will be injected.
RetryTemplate relshpProcessRetry = new RetryTemplate();
SimpleRetryPolicy retryPolicy=new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
relshpProcessRetry.setRetryPolicy(retryPolicy);
try {
relshpProcessRetry
.execute((RetryCallback<Void, StorageAccessorException>) context -> {
final var decisions = decideRelationships(relationshipRequest).getDecisions();
context
.setAttribute(String.valueOf(relationshipRequest.getParentListingId()), decisions);
decisions
.forEach(relationshipDecision -> relationshipDecision.apply(storageAccessor));
return null;
}, context -> {
//noinspection unchecked
List<RelationshipDecision> decisions = (List<RelationshipDecision>) context
.getAttribute(String.valueOf(relationshipRequest.getParentListingId()));
//noinspection ConstantConditions
Validate.notEmpty(decisions, "Decisions to rollback must not be null");
ReverseListIterator<RelationshipDecision> decisionReverseIterator = new ReverseListIterator<>(
decisions);
while (decisionReverseIterator.hasNext()) {
final var decision = decisionReverseIterator.next();
if (decision.isApplied()) {
decision.rollback(storageAccessor);
}
}
return null;
});
} catch (ExhaustedRetryException e) {
//if we are here, all attempts ended in optimistic lock exceptions
log.debug(
"Failed to process relationship(s) for request {} , after all retry attempts have been exhausted",
relationshipRequest, e);
throw new IllegalStateException(
"Relationship processing failed - " + relationshipRequest.toString(), e);
}
В моих модульных тестах я вижу следующее, напечатанное до первого восстановления:
14:24:38.882 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
14:24:40.726 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1
14:24:40.726 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=1
14:24:41.843 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=2
14:24:41.843 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=2
14:24:42.781 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=3
14:24:42.781 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry failed last attempt: count=3