Skip to content

Add a possibility to use listener per @Retryable method instead of global listeners #485

Open
@yura-arab4uk

Description

@yura-arab4uk

In my project I wanted to add a listener and use it along with @Retryable annotation.
But when I add a listener, it's automatically considered by Spring Retry as global one, meaning it will be applied at least to every method marked with @Retryable annotation, which prevents me from creating listener, because I don't want to impact an existing code.

Example:
@Retryable(retryFor = SomeException.class, maxAttempts = 5, listeners = "myListenerBean")

When I create myListenerBean, it will be automatically applied to all existing methods with @Retryable annotation, which is not desired behavior.
The only way to prevent this is to either specify an empty string on existing code, which is not always possible to modify an existing code

@Retryable(listeners = "")

or use interceptor attribute which cannot be used with any other attributes and which requires interceptor bean along with all retry configurations code:

@Retryable(interceptor = "myInterceptorBean")

As a result instead of using neat single line:
@Retryable(retryFor = SomeException.class, maxAttempts = 5, listeners = "myListenerBean")

I'm forced to use:
@Retryable(interceptor = "myInterceptorBean")

along with whole retry configuration, for example:

@Slf4j
@Configuration
@RequiredArgsConstructor
public class ConnectionErrorRetryConfig {
    public static final String CONNECTION_ERROR_RETRY_INTERCEPTOR = "connectionErrorRetryInterceptor";

    @Value("${request.max-attempts:5}")
    private int maxAttempts;

    private final MyService myService;

    @Bean(CONNECTION_ERROR_RETRY_INTERCEPTOR)
    public RetryOperationsInterceptor connectionErrorRetryInterceptor() {
        SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
        simpleRetryPolicy.setMaxAttempts(maxAttempts);

        RetryTemplate retryTemplate = RetryTemplate.builder()
            .customPolicy(simpleRetryPolicy)
            .retryOn(SomeException.class)
            // forced to create a new RetryListener here as NOT a bean, because bean is impacting globally every @Retryable method
            .withListener(new RetryListener() {
                @Override
                public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable)
                {
                    if (throwable instanceof SomeException someException)
                    {
                        myService.doImportantAction(someException);
                    }

                    RetryListener.super.onError(context, callback, throwable);
                }
            })
            .build();

        return RetryInterceptorBuilder.stateless()
            .retryOperations(retryTemplate)
            .recoverer(new ConnectionErrorRecoverer())
            .build();
    }
}

As it's been already discussed here, we could think of any alternative, for example deprecating existing listeners attribute and/or providing some other attribute(s) etc. in order to not use global listeners.

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions