question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Resilience4j-rxjava2 doesn't report CircuitBreaker success metrics post 0.15.0 upgrade

See original GitHub issue

With the 0.15.0 upgrade, we have noticed that success events are no longer reported for our RxJava2 circuits.

0.14.1 code:

CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .ringBufferSizeInClosedState(10)
                .ringBufferSizeInHalfOpenState(5)
                .waitDurationInOpenState(Duration.ofMillis(10000))
                .failureRateThreshold(0.50f)
                .build();

        CircuitBreaker breaker1 = CircuitBreaker.of("one", config);

        breaker1.getEventPublisher()
                .onEvent(r4jEvent -> System.out.println(String.format("Got an event %s for Circuit %s",
                        r4jEvent, breaker1)));

        for (int i = 0; i < 10; i++) {
            final int index = i;
            String output = Observable.create(new ObservableOnSubscribe<String>() {
                @Override
                public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                    if (index % 2 == 0) {
                        emitter.onNext("Success");
                    } else {
                        emitter.onError(new RuntimeException());
                    }
                }
            })
                    .take(1)
                    .subscribeOn(Schedulers.io())
                    .lift(CircuitBreakerOperator.of(breaker1))
                    .onErrorReturn(e -> "Failure")
                    .blockingFirst();
            System.out.println(output);
        }
        Metrics metrics = breaker1.getMetrics();
        System.out.println(String.format("Calls = %d, BufferSize = %d, Success = %d, Failed = %d",
                metrics.getNumberOfBufferedCalls(),
                metrics.getMaxNumberOfBufferedCalls(),
                metrics.getNumberOfSuccessfulCalls(),
                metrics.getNumberOfFailedCalls()));

Result of above code: A Success event is given every time onNext() is called, and a failure event everytime onError() is called.

But now, after upgrading to 0.15.0, the code transforms as such:

CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .ringBufferSizeInClosedState(10)
                .ringBufferSizeInHalfOpenState(5)
                .waitDurationInOpenState(Duration.ofMillis(10000))
                .failureRateThreshold(0.50f)
                .build();

        CircuitBreaker breaker1 = CircuitBreaker.of("one", config);

        breaker1.getEventPublisher()
                .onEvent(r4jEvent -> System.out.println(String.format("Got an event %s for Circuit %s",
                        r4jEvent, breaker1)));

        for (int i = 0; i < 10; i++) {
            final int index = i;
            String output = Observable.create(new ObservableOnSubscribe<String>() {
                @Override
                public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                    if (index % 2 == 0) {
                        emitter.onNext("Success");
                    } else {
                        emitter.onError(new RuntimeException());
                    }
                }
            })
                    .take(1)
                    .subscribeOn(Schedulers.io())
                    .compose(CircuitBreakerOperator.of(breaker1))
                    .onErrorReturn(e -> "Failure")
                    .blockingFirst();
            System.out.println(output);
        }

Results: Only onError() events are recorded by the event Publisher , and no Success events are recorded.

Given that .compose() is a stream level operation while .lift() is an item level one, I would expect that a potential workaround would be to ensure emitter.onComplete() is called within the ObservableOnSubscribe.subscribe()

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:8 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
RobWincommented, Aug 27, 2019

I think I have a solution. The CircuitBreakerOperator could track if an event has been emitted successfully (onNext). And when dispose is invoked, the CircuitBreakerOperator either invokes circuitBreaker.onSuccess or circuitBreaker.releasePermission.

if(wasEventEmitted()){
       circuitBreaker.onSuccess(System.nanoTime() - start, TimeUnit.NANOSECONDS);
 }else{
       circuitBreaker.releasePermission();
 }

This change results in:

Got an event 2019-08-27T15:14:37.516+02:00[Europe/Berlin]: CircuitBreaker 'one' recorded a successful call. Elapsed time: 5 ms for Circuit CircuitBreaker 'one'

Obs2 Disposed

Got an event 2019-08-27T15:14:37.534+02:00[Europe/Berlin]: CircuitBreaker 'two' recorded a successful call. Elapsed time: 0 ms for Circuit CircuitBreaker 'two'

Got an event 2019-08-27T15:14:37.534+02:00[Europe/Berlin]: CircuitBreaker 'three' recorded a successful call. Elapsed time: 47 ms for Circuit CircuitBreaker 'three'
0reactions
RobWincommented, Aug 27, 2019

I think I understand it now. When the first observable is complete, the second observable is cancelled as soon as it emitted the next event. It’s because zip must combine two events. It makes no sense to consume further events of the second observable when the first is completed.

Unfortunately the cancellation is bad for the CircuitBreakerOperator. I’ve to think about how the CircuitBreakerOperator fits into this design.

Read more comments on GitHub >

github_iconTop Results From Across the Web

CircuitBreaker - resilience4j
In these two states no Circuit Breaker events (apart from the state transition) are generated, and no metrics are recorded. The only way...
Read more >
CircuitBreakerOperator (resilience4j-rxjava2 0.14.0 API)
A RxJava operator which protects a reactive type by a CircuitBreaker. Please note that the subscribed operators might affect the circuit breaker even...
Read more >
Implementing a Circuit Breaker with Resilience4j - Reflectoring
A deep dive into the Resilience4j circuit breaker module. This article shows why, when and how to use it to build resilient applications....
Read more >
Issue with io.github.resilience4j version 1.7.2 ... - Stack Overflow
Everything is working fine with 1.6.1 version but when upgraded to 1.7.1 version my application is not running. Please find my code changes ......
Read more >
Spring Cloud Circuit Breaker
The Spring Cloud CircuitBreaker project contains implementations for Resilience4J and Spring Retry. The APIs implemented in Spring Cloud CircuitBreaker live ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found