3

I have a table update that causes a deadlock and am trying to get the Spring Retry to retry when the method gets the a locking exception of some kind. I've tried taking out the maxAttempts, value and backoff but it doesn't ever seem to catch any of the exceptions. Am I missing something? Do I need to declare a bean in the Application file? Any help would be much appreciated!

Application.Java

@SpringBootApplication
@EnableRetry
public class Application extends SpringBootServletInitializer {

DetailService

@Service
public class DetailService {

    @Retryable(maxAttempts = 5, value = { LockAcquisitionException.class, ConcurrencyFailureException.class }, backoff = @Backoff(delay = 500, multiplier = 2) )
    public void delete(final String detailCode) {
        try {
            this.delete(this.dao.findByDetailCode(detailCode));
        } catch (LockAcquisitionException | ConcurrencyFailureException e) {
            LOG.warn("Locking error! Going to retry", e.getMessage());
            throw e;
        }
    }

    public void delete(Details detail) {
           this.dao.delete(detail);            
    }

    @Retryable(maxAttempts = 5, value = { LockAcquisitionException.class, ConcurrencyFailureException.class }, backoff = @Backoff(delay = 500, multiplier = 2) )
    public void delete(final Integer id) {
        if (id != null) {
            try {
                this.delete(this.dao.findOne(id));
            } catch (LockAcquisitionException | ConcurrencyFailureException e) { 
                 LOG.warn("Locking error! Going to retry", e.getMessage());
                 throw e;
            }
        }
    }

EDIT

Rewrote my DetailService above to give more detail and add missing methods

Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82

1 Answers1

12

If you are calling the delete() method (in the DetailService) from within the same class, you are short-circuiting the proxy that Spring wraps the bean in.

The class that has the annotation must be a Spring-managed bean and the delete() method must be called from some other Spring-managed bean that has access to the retryable bean via autowiring, injection etc.

EDIT

If you need to call the delete() method from another method within this class, you can't use the annotation - use a suitably configured RetryTemplate instead.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • The delete() method is part of the dao class that is being autowired in. The dao is a crud repository class. Sorry didn't include the whole DetailService class there. Would this still be short circuiting the proxy? – TacoTacoBurrito175 Aug 03 '16 at 23:46
  • Oh sorry, misunderstood. No this is not being called within the same class. It's being called from a controller or another service – TacoTacoBurrito175 Aug 03 '16 at 23:51
  • No; I mean the `delete()` method in **this** class (the one that's annotated). See my edit for another work around. – Gary Russell Aug 03 '16 at 23:52
  • If you set a breakpoint, you should see a proxy and RetryTemplate in the call stack between the controller and this class; if you don't then there's something wrong with the wiring. – Gary Russell Aug 03 '16 at 23:55
  • I was mistaken last night, that `delete()` is called by two other methods in the class. If i put the `@Retryable` on those methods will that work? Or do I need to write my own RetryTemplate? I'm trying to avoid the writing my own... – TacoTacoBurrito175 Aug 04 '16 at 15:16
  • Yes; as long as the call is from an external bean, `@Retryable` will work. But you don't need to "write" a `RetryTemplate`, just configure one and use it to call the method through one of its `execute` methods. – Gary Russell Aug 04 '16 at 16:25
  • Thank you! I was under the assumption that the Retry Template would have a default bean and I didn't need to declare a bean in the Application class. Once I did that `@Retryable` worked. `@Bean public RetryTemplate retryTemplate() { ` – TacoTacoBurrito175 Aug 04 '16 at 18:50