-
-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Canary Rate Strategy Implementation (#570)
* creating empty strategy * creating tests to validate reslience4j behavior * more tests * new test * new tests and features * notes * refactoring and testing * creating healthchecker * refactoring * implementing * adjusting test * clean code * refactoring * release notes * [Gradle Release Plugin] - new version commit: '3.30.1-snapshot'.
- Loading branch information
Showing
36 changed files
with
1,045 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
version=3.30.0-snapshot | ||
version=3.30.1-snapshot |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
...va/com/mageddo/dnsproxyserver/config/CanaryRateThresholdCircuitBreakerStrategyConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.mageddo.dnsproxyserver.config; | ||
|
||
import lombok.Builder; | ||
import lombok.Value; | ||
|
||
@Value | ||
@Builder | ||
public class CanaryRateThresholdCircuitBreakerStrategyConfig implements CircuitBreakerStrategyConfig { | ||
|
||
private float failureRateThreshold; | ||
private int minimumNumberOfCalls; | ||
private int permittedNumberOfCallsInHalfOpenState; | ||
|
||
@Override | ||
public Name name() { | ||
return Name.CANARY_RATE_THRESHOLD; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...va/com/mageddo/dnsproxyserver/solver/remote/circuitbreaker/application/HealthChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application; | ||
|
||
public interface HealthChecker { | ||
boolean isHealthy(); | ||
} |
41 changes: 41 additions & 0 deletions
41
.../remote/circuitbreaker/canaryratethreshold/CircuitBreakerDelegateCanaryRateThreshold.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold; | ||
|
||
import com.mageddo.commons.circuitbreaker.CircuitIsOpenException; | ||
import com.mageddo.dnsproxyserver.solver.remote.CircuitStatus; | ||
import com.mageddo.dnsproxyserver.solver.remote.Result; | ||
import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegate; | ||
import com.mageddo.dnsproxyserver.solver.remote.mapper.Resilience4jStatusMapper; | ||
import io.github.resilience4j.circuitbreaker.CallNotPermittedException; | ||
import io.github.resilience4j.circuitbreaker.CircuitBreaker; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import java.util.function.Supplier; | ||
|
||
@Slf4j | ||
public class CircuitBreakerDelegateCanaryRateThreshold implements CircuitBreakerDelegate { | ||
|
||
private final CircuitBreaker circuitBreaker; | ||
|
||
public CircuitBreakerDelegateCanaryRateThreshold(CircuitBreaker circuitBreaker) { | ||
this.circuitBreaker = circuitBreaker; | ||
} | ||
|
||
@Override | ||
public Result execute(Supplier<Result> sup) { | ||
try { | ||
return this.circuitBreaker.executeSupplier(sup); | ||
} catch (CallNotPermittedException e) { | ||
throw new CircuitIsOpenException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public CircuitStatus findStatus() { | ||
return Resilience4jStatusMapper.toCircuitStatus(this.circuitBreaker.getState()); | ||
} | ||
|
||
@Override | ||
public void transitionToHalfOpenState() { | ||
this.circuitBreaker.transitionToHalfOpenState(); | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
...olver/remote/circuitbreaker/canaryratethreshold/CircuitBreakerDelegateSelfObservable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold; | ||
|
||
import com.mageddo.commons.concurrent.Threads; | ||
import com.mageddo.concurrent.ThreadsV2; | ||
import com.mageddo.dnsproxyserver.solver.remote.CircuitStatus; | ||
import com.mageddo.dnsproxyserver.solver.remote.Result; | ||
import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegate; | ||
import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.HealthChecker; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import java.time.Duration; | ||
import java.util.function.Supplier; | ||
|
||
@Slf4j | ||
public class CircuitBreakerDelegateSelfObservable implements CircuitBreakerDelegate, AutoCloseable { | ||
|
||
private final CircuitBreakerDelegate delegate; | ||
private final Duration sleepDuration; | ||
private final HealthChecker healthChecker; | ||
private boolean open = true; | ||
|
||
public CircuitBreakerDelegateSelfObservable( | ||
CircuitBreakerDelegate delegate, HealthChecker healthChecker | ||
) { | ||
this(delegate, Duration.ofSeconds(1), healthChecker); | ||
} | ||
|
||
public CircuitBreakerDelegateSelfObservable( | ||
CircuitBreakerDelegate delegate, Duration sleepDuration, HealthChecker healthChecker | ||
) { | ||
this.delegate = delegate; | ||
this.sleepDuration = sleepDuration; | ||
this.healthChecker = healthChecker; | ||
this.startOpenCircuitHealthCheckWorker(); | ||
} | ||
|
||
@Override | ||
public Result execute(Supplier<Result> sup) { | ||
return this.delegate.execute(sup); | ||
} | ||
|
||
@Override | ||
public CircuitStatus findStatus() { | ||
return this.delegate.findStatus(); | ||
} | ||
|
||
@Override | ||
public void transitionToHalfOpenState() { | ||
this.delegate.transitionToHalfOpenState(); | ||
} | ||
|
||
private void startOpenCircuitHealthCheckWorker() { | ||
Thread | ||
.ofVirtual() | ||
.start(() -> { | ||
while (this.shouldRun()) { | ||
Threads.sleep(this.sleepDuration); | ||
this.healthCheckWhenInOpenState(); | ||
} | ||
}); | ||
} | ||
|
||
private boolean shouldRun() { | ||
return ThreadsV2.isNotInterrupted() && this.open; | ||
} | ||
|
||
private void healthCheckWhenInOpenState() { | ||
final var status = this.findStatus(); | ||
log.trace("status=checking, status={}", status); | ||
if (!CircuitStatus.isOpen(status)) { | ||
log.trace("status=notOpenStatus, status={}", status); | ||
return; | ||
} | ||
final var success = this.isHealthy(); | ||
if (success) { | ||
this.transitionToHalfOpenState(); | ||
log.debug("status=halfOpenStatus, circuitBreaker={}", this); | ||
} | ||
} | ||
|
||
private boolean isHealthy() { | ||
return this.healthChecker.isHealthy(); | ||
} | ||
|
||
@Override | ||
public void close() throws Exception { | ||
this.open = false; | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
...nsproxyserver/solver/remote/circuitbreaker/canaryratethreshold/CircuitBreakerFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold; | ||
|
||
import com.mageddo.dnsproxyserver.config.CanaryRateThresholdCircuitBreakerStrategyConfig; | ||
import io.github.resilience4j.circuitbreaker.CircuitBreaker; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import javax.inject.Inject; | ||
import javax.inject.Singleton; | ||
|
||
@Slf4j | ||
@Singleton | ||
@RequiredArgsConstructor(onConstructor = @__({@Inject})) | ||
public class CircuitBreakerFactory { | ||
|
||
public CircuitBreakerDelegateSelfObservable build(CanaryRateThresholdCircuitBreakerStrategyConfig config){ | ||
final var circuitBreakerDelegate = new CircuitBreakerDelegateCanaryRateThreshold( | ||
this.createResilienceCircuitBreakerFrom(config) | ||
); | ||
final var healthChecker = new CircuitExecutionsAsHealthChecker(circuitBreakerDelegate); | ||
return new CircuitBreakerDelegateSelfObservable( | ||
healthChecker, healthChecker | ||
); | ||
} | ||
|
||
private CircuitBreaker createResilienceCircuitBreakerFrom(CanaryRateThresholdCircuitBreakerStrategyConfig config) { | ||
return Resilience4jMapper.from(config); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
...er/solver/remote/circuitbreaker/canaryratethreshold/CircuitExecutionsAsHealthChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold; | ||
|
||
import com.mageddo.commons.circuitbreaker.CircuitCheckException; | ||
import com.mageddo.dnsproxyserver.solver.remote.CircuitStatus; | ||
import com.mageddo.dnsproxyserver.solver.remote.Result; | ||
import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegate; | ||
import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.HealthChecker; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import java.util.function.Supplier; | ||
|
||
@Slf4j | ||
public class CircuitExecutionsAsHealthChecker implements HealthChecker, CircuitBreakerDelegate { | ||
|
||
private final CircuitBreakerDelegate delegate; | ||
private final boolean healthWhenNoCallTemplateToDo; | ||
private Supplier<Result> lastCall = null; | ||
|
||
public CircuitExecutionsAsHealthChecker(CircuitBreakerDelegate delegate) { | ||
this.delegate = delegate; | ||
this.healthWhenNoCallTemplateToDo = false; | ||
} | ||
|
||
@Override | ||
public boolean isHealthy() { | ||
try { | ||
if (this.lastCall == null) { | ||
log.trace("status=noLastCall, answer={}", this.healthWhenNoCallTemplateToDo); | ||
return this.healthWhenNoCallTemplateToDo; | ||
} | ||
final var res = this.lastCall.get(); | ||
log.trace("status=delegateToLastCall, answer={}", res); | ||
return true; | ||
} catch (CircuitCheckException e) { | ||
log.trace("status=callFailed, answer=false, msg={}", e.getMessage()); | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public Result execute(Supplier<Result> sup) { | ||
this.lastCall = sup; | ||
return this.delegate.execute(sup); | ||
} | ||
|
||
@Override | ||
public CircuitStatus findStatus() { | ||
return this.delegate.findStatus(); | ||
} | ||
|
||
@Override | ||
public void transitionToHalfOpenState() { | ||
this.delegate.transitionToHalfOpenState(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...o/dnsproxyserver/solver/remote/circuitbreaker/canaryratethreshold/Resilience4jMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold; | ||
|
||
import com.mageddo.commons.circuitbreaker.CircuitCheckException; | ||
import com.mageddo.dnsproxyserver.config.CanaryRateThresholdCircuitBreakerStrategyConfig; | ||
import io.github.resilience4j.circuitbreaker.CircuitBreaker; | ||
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; | ||
|
||
import java.time.Duration; | ||
|
||
public class Resilience4jMapper { | ||
public static CircuitBreaker from(CanaryRateThresholdCircuitBreakerStrategyConfig config) { | ||
final var circuitBreaker = CircuitBreaker.of( | ||
"defaultCircuitBreaker", | ||
CircuitBreakerConfig | ||
.custom() | ||
|
||
.failureRateThreshold(config.getFailureRateThreshold()) | ||
.minimumNumberOfCalls(config.getMinimumNumberOfCalls()) | ||
.permittedNumberOfCallsInHalfOpenState(config.getPermittedNumberOfCallsInHalfOpenState()) | ||
|
||
.waitDurationInOpenState(Duration.ofDays(365)) | ||
.recordExceptions(CircuitCheckException.class) | ||
|
||
.build() | ||
); | ||
return circuitBreaker; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/mageddo/dnsproxyserver/solver/remote/mapper/Resilience4jStatusMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.mapper; | ||
|
||
import com.mageddo.dnsproxyserver.solver.remote.CircuitStatus; | ||
import io.github.resilience4j.circuitbreaker.CircuitBreaker; | ||
import org.apache.commons.lang3.EnumUtils; | ||
|
||
public class Resilience4jStatusMapper { | ||
public static CircuitStatus toCircuitStatus(CircuitBreaker.State state){ | ||
return EnumUtils.getEnum(CircuitStatus.class, state.name()); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
...server/solver/remote/circuitbreaker/application/CircuitExecutionsAsHealthCheckerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application; | ||
|
||
import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold.CircuitExecutionsAsHealthChecker; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.InjectMocks; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import testing.templates.solver.remote.ResultSupplierTemplates; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class CircuitExecutionsAsHealthCheckerTest { | ||
|
||
@Mock | ||
CircuitBreakerDelegate circuitBreaker; | ||
|
||
@InjectMocks | ||
CircuitExecutionsAsHealthChecker obj; | ||
|
||
@Test | ||
void mustUseTheLastCallAsHealthCheck() { | ||
|
||
final var sup = ResultSupplierTemplates.withCallsCounterNullRes(); | ||
|
||
this.obj.execute(sup); | ||
this.obj.isHealthy(); | ||
|
||
assertEquals(1, sup.getCalls()); | ||
} | ||
|
||
@Test | ||
void mustAnswerHealthWhenExecuteNeverCalled(){ | ||
final var healthy = this.obj.isHealthy(); | ||
assertFalse(healthy); | ||
} | ||
} |
Oops, something went wrong.