FINERACT-2304: Handle Stuck CommandProcessing Retries

This commit is contained in:
Soma Sörös
2025-06-12 10:12:24 +02:00
committed by Arnold Gálovics
parent 7c656ecae8
commit e811491da7
4 changed files with 238 additions and 5 deletions
@@ -26,6 +26,7 @@ import java.util.Arrays;
import lombok.AllArgsConstructor;
import org.apache.fineract.batch.service.BatchExecutionException;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
import org.springframework.stereotype.Service;
@AllArgsConstructor
@@ -34,19 +35,32 @@ public class RetryConfigurationAssembler {
public static final String EXECUTE_COMMAND = "executeCommand";
public static final String BATCH_RETRY = "batchRetry";
private static final String LAST_EXECUTION_EXCEPTION_KEY = "LAST_EXECUTION_EXCEPTION";
private final RetryRegistry registry;
private final FineractProperties fineractProperties;
private final FineractRequestContextHolder fineractRequestContextHolder;
private boolean isAssignableFrom(Object e, Class<? extends Throwable>[] exceptionList) {
return Arrays.stream(exceptionList).anyMatch(re -> re.isAssignableFrom(e.getClass()));
}
private void setLastException(Object ex) {
fineractRequestContextHolder.setAttribute(LAST_EXECUTION_EXCEPTION_KEY, ex.getClass());
}
public Class<?> getLastException() {
return (Class<?>) fineractRequestContextHolder.getAttribute(RetryConfigurationAssembler.LAST_EXECUTION_EXCEPTION_KEY);
}
public Retry getRetryConfigurationForExecuteCommand() {
Class<? extends Throwable>[] exceptionList = fineractProperties.getRetry().getInstances().getExecuteCommand().getRetryExceptions();
RetryConfig.Builder configBuilder = buildCommonExecuteCommandConfiguration();
if (exceptionList != null) {
configBuilder.retryOnException(e -> isAssignableFrom(e, exceptionList));
configBuilder.retryOnException(ex -> {
setLastException(ex);
return isAssignableFrom(ex, exceptionList);
});
}
RetryConfig config = configBuilder.build();
@@ -60,8 +74,10 @@ public class RetryConfigurationAssembler {
if (exceptionList != null) {
configBuilder.retryOnException(ex -> {
if (ex instanceof BatchExecutionException e) {
setLastException(e.getCause().getClass());
return isAssignableFrom(e.getCause(), exceptionList);
} else {
setLastException(ex);
return isAssignableFrom(ex, exceptionList);
}
});
@@ -188,7 +188,13 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
}
CommandProcessingResultType status = CommandProcessingResultType.fromInt(command.getStatus());
switch (status) {
case UNDER_PROCESSING -> throw new IdempotentCommandProcessUnderProcessingException(wrapper, idempotencyKey);
case UNDER_PROCESSING -> {
Class<?> lastExecutionExceptionClass = retryConfigurationAssembler.getLastException();
if (lastExecutionExceptionClass == null
|| IdempotentCommandProcessUnderProcessingException.class.isAssignableFrom(lastExecutionExceptionClass)) {
throw new IdempotentCommandProcessUnderProcessingException(wrapper, idempotencyKey);
}
}
case PROCESSED -> throw new IdempotentCommandProcessSucceedException(wrapper, idempotencyKey, command);
case ERROR -> {
if (!retry) {