Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RetryTemplate - CircuitBreakerPolicy with Retry is not open the circuit #129

Closed
fmunozse opened this issue Sep 18, 2018 · 5 comments
Closed

Comments

@fmunozse
Copy link

Hi,

In my testing, using CircuitBreakerPolicy , by default use the delegation pattern SimpleRetryPolicy.

The issue that i have seem is that if the code inside of template always generate an exception (just for testing), the circuit NEVER is open and always try to repeat.

See

Template used:

	@Bean
	public RetryTemplate retryTemplate() {
		RetryTemplate retryTemplate = new RetryTemplate();

		FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
		fixedBackOffPolicy.setBackOffPeriod(2000l);
		retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

		//SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
		//retryPolicy.setMaxAttempts(3);

		CircuitBreakerRetryPolicy circuitBreakerRetryPolicy = new CircuitBreakerRetryPolicy();
		//circuitBreakerRetryPolicy.setOpenTimeout();
		retryTemplate.setRetryPolicy(circuitBreakerRetryPolicy);



		return retryTemplate;
	}

Service:

@Service
public class HelloService {
..
    class BoomException extends RuntimeException {
        public BoomException(String message) {
            super(message);
        }
    }
---
    public int callExternalSystem (String uuid ) {
            throw new BoomException("Error happened");
            return 1;
    }

And the code to call to the service:

@RestController
public class HelloController {

    Logger logger = LoggerFactory.getLogger(HelloController.class);

    HelloService helloService;
    RetryTemplate retryTemplate;

    public HelloController(HelloService helloService, RetryTemplate retryTemplate) {
        this.helloService = helloService;
        this.retryTemplate = retryTemplate;
    }

    @RequestMapping("/")
    public String index() {
        String uuid = UUID.randomUUID().toString();
        logger.info("\nRequest {} ", uuid);
         retryTemplate.execute(context -> {
            logger.info("uuid - retryCount" + context.getRetryCount());
            helloService.callExternalSystem(uuid);
            return null;
        }, context -> {
            logger.info("fallback uuid - " + context.getRetryCount());
            return null;
        });

        return "Greetings from Spring Boot! ";
    }

So, the problem is that system doing an infinite retry without open the circuit.

Originally posted by @fmunozse in #83 (comment)

@dsyer
Copy link
Member

dsyer commented Sep 18, 2018

CircuitBreakerRetryPolicy can only be used with a stateful retry (it has to remember the state of the circuit). If you used the annotation you wouldn't need to know about that, but since you are calling the RetryTemplate explicitly, you will need to supply a RetryState instance as well. E.g. look at the unit tests for CircuitBreakerRetryPolicy for similar patterns.

@fmunozse
Copy link
Author

First, thanks for the quick answer ... and maybe i miss understand the approach...

My initial idea was to use spring-retry like a way to do "automatically" retries and in case that we have a number of retries and fail, then, open the circuit.

if i try to use directly the annotation

@Service
public class HelloService {
....
    @CircuitBreaker( include = BoomException.class)
    public int callExternalSystem (String uuid ) {
        throw new BoomException("Error happened");
...
    @Recover
    private int fallbackForCall(String uuid) {
        logger.info("[{}] Fallback for call invoked ", uuid );
        return 0;
    }
@RestController
public class HelloController {
...
    @RequestMapping("/")
    public String index() {
        String uuid = UUID.randomUUID().toString();
        logger.info("\nRequest {} ", uuid);
        return "Greetings from Spring Boot! - call service: " + helloService.callExternalSystem(uuid);

In this case, when i call to REST the first time, the system call to the recover ... but really, it doenst do any automatically retry .. and if i call manually several times to REST, after 3 retries (default value), then it's open the circuit

But, i would like that system automatically does the retries, and then, i try to use the RetryTemplate ... but also dont work like expected.

	@Bean
	public RetryTemplate retryTemplate() {
		RetryTemplate retryTemplate = new RetryTemplate();

		FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
		fixedBackOffPolicy.setBackOffPeriod(2000l);
		retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

		SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
		retryPolicy.setMaxAttempts(3);

		CircuitBreakerRetryPolicy circuitBreakerRetryPolicy = new CircuitBreakerRetryPolicy(retryPolicy);
		retryTemplate.setRetryPolicy(circuitBreakerRetryPolicy);
		return retryTemplate;
	}

@RestController
public class HelloController {
        RetryState retryState = new DefaultRetryState("foo", new BinaryExceptionClassifier(false));

        retryTemplate.execute(context -> {
            logger.info("uuid - retryCount" + context.getRetryCount());
            helloService.callExternalSystem(uuid);
            return null;
        }, context -> {
            logger.info("fallback uuid - " + context.getRetryCount());
            return null;
        }, retryState);

So, really , do you know if there is any option in order to system does automatically the retries and in case that is not successful, then call to recover ... and depends on attends, open the circuit ?

Thanks in advance ... (i have tried several options and i am a little bit block)

@dsyer
Copy link
Member

dsyer commented Sep 19, 2018

I guess this would work:

@Service
public class HelloService {
....
    @CircuitBreaker( include = BoomException.class)
    public int callExternalSystem (String uuid ) {
       new RetryTemplate().execute(context -> {
           doCallSystem(uuid);
            return null;
        });
    }

    private int doCallSystem(String uuid) {
         throw new BoomException("Error happened");
    }
...
    @Recover
    private int fallbackForCall(String uuid) {
        return 0;
    }

I haven't tried it.

@fmunozse
Copy link
Author

fmunozse commented Sep 19, 2018

Hi again.

Yes, that it's works ;)

It's a pity that annotations can not mix.

In any case, thanks.

@dsyer
Copy link
Member

dsyer commented Sep 19, 2018

Great. And now I understand #83. So thank you.

@dsyer dsyer closed this as completed Sep 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants