Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CircuitBreakerConfigParser {

Check warning on line 10 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L9-L10

Added lines #L9 - L10 were not covered by tests

// Percentage of failures to trigger OPEN state
private static final String FAILURE_RATE_THRESHOLD = "failureRateThreshold";
Expand All @@ -28,81 +28,84 @@
private static final String SLIDING_WINDOW_TYPE = "slidingWindowType";
public static final String ENABLED = "enabled";
public static final String DEFAULT_THRESHOLDS = "defaultThresholds";
private static final Set<String> NON_THRESHOLD_KEYS = Set.of(ENABLED, DEFAULT_THRESHOLDS);
private static final Set<String> NON_THRESHOLD_KEYS = Set.of(ENABLED);

Check warning on line 31 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L31

Added line #L31 was not covered by tests

public static <T> CircuitBreakerConfiguration.CircuitBreakerConfigurationBuilder<T> parseConfig(
Config config) {
CircuitBreakerConfiguration.CircuitBreakerConfigurationBuilder<T> builder =
CircuitBreakerConfiguration.builder();

Check warning on line 36 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L36

Added line #L36 was not covered by tests
if (config.hasPath(ENABLED)) {
builder.enabled(config.getBoolean(ENABLED));

Check warning on line 38 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L38

Added line #L38 was not covered by tests
}

if (config.hasPath(DEFAULT_THRESHOLDS)) {
builder.defaultThresholds(
buildCircuitBreakerThresholds(config.getConfig(DEFAULT_THRESHOLDS)));
} else {
builder.defaultThresholds(buildCircuitBreakerDefaultThresholds());
}

Map<String, CircuitBreakerThresholds> circuitBreakerThresholdsMap =
config.root().keySet().stream()

Check warning on line 42 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L41-L42

Added lines #L41 - L42 were not covered by tests
.filter(key -> !NON_THRESHOLD_KEYS.contains(key)) // Filter out non-threshold keys
Comment thread
aaron-steinfeld marked this conversation as resolved.
.collect(
Collectors.toMap(
key -> key, // Circuit breaker key
key -> buildCircuitBreakerThresholds(config.getConfig(key))));

Check warning on line 47 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L44-L47

Added lines #L44 - L47 were not covered by tests

if (!config.hasPath(DEFAULT_THRESHOLDS)) {
builder.defaultThresholds(buildCircuitBreakerDefaultThresholds());
circuitBreakerThresholdsMap.put(DEFAULT_THRESHOLDS, buildCircuitBreakerDefaultThresholds());

Check warning on line 51 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L50-L51

Added lines #L50 - L51 were not covered by tests
Comment thread
aaron-steinfeld marked this conversation as resolved.
Outdated
}

builder.circuitBreakerThresholdsMap(circuitBreakerThresholdsMap);
log.debug("Loaded circuit breaker configs: {}", builder);
return builder;

Check warning on line 56 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L54-L56

Added lines #L54 - L56 were not covered by tests
}

private static CircuitBreakerThresholds buildCircuitBreakerThresholds(Config config) {
CircuitBreakerThresholds.CircuitBreakerThresholdsBuilder builder =
CircuitBreakerThresholds.builder();

Check warning on line 61 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L61

Added line #L61 was not covered by tests

if (config.hasPath(FAILURE_RATE_THRESHOLD)) {
builder.failureRateThreshold((float) config.getDouble(FAILURE_RATE_THRESHOLD));

Check warning on line 64 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L64

Added line #L64 was not covered by tests
}

if (config.hasPath(SLOW_CALL_RATE_THRESHOLD)) {
builder.slowCallRateThreshold((float) config.getDouble(SLOW_CALL_RATE_THRESHOLD));

Check warning on line 68 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L68

Added line #L68 was not covered by tests
}

if (config.hasPath(SLOW_CALL_DURATION_THRESHOLD)) {
builder.slowCallDurationThreshold(config.getDuration(SLOW_CALL_DURATION_THRESHOLD));

Check warning on line 72 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L72

Added line #L72 was not covered by tests
}

if (config.hasPath(SLIDING_WINDOW_TYPE)) {
builder.slidingWindowType(getSlidingWindowType(config.getString(SLIDING_WINDOW_TYPE)));

Check warning on line 76 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L76

Added line #L76 was not covered by tests
}

if (config.hasPath(SLIDING_WINDOW_SIZE)) {
builder.slidingWindowSize(config.getInt(SLIDING_WINDOW_SIZE));

Check warning on line 80 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L80

Added line #L80 was not covered by tests
}

if (config.hasPath(WAIT_DURATION_IN_OPEN_STATE)) {
builder.waitDurationInOpenState(config.getDuration(WAIT_DURATION_IN_OPEN_STATE));

Check warning on line 84 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L84

Added line #L84 was not covered by tests
}

if (config.hasPath(PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE)) {
builder.permittedNumberOfCallsInHalfOpenState(
config.getInt(PERMITTED_NUMBER_OF_CALLS_IN_HALF_OPEN_STATE));

Check warning on line 89 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L88-L89

Added lines #L88 - L89 were not covered by tests
}

if (config.hasPath(MINIMUM_NUMBER_OF_CALLS)) {
builder.minimumNumberOfCalls(config.getInt(MINIMUM_NUMBER_OF_CALLS));

Check warning on line 93 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L93

Added line #L93 was not covered by tests
}

if (config.hasPath(ENABLED)) {
builder.enabled(config.getBoolean(ENABLED));

Check warning on line 97 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L97

Added line #L97 was not covered by tests
}

return builder.build();

Check warning on line 100 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L100

Added line #L100 was not covered by tests
}

public static CircuitBreakerThresholds buildCircuitBreakerDefaultThresholds() {
return CircuitBreakerThresholds.builder().build();

Check warning on line 104 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L104

Added line #L104 was not covered by tests
}

private static CircuitBreakerThresholds.SlidingWindowType getSlidingWindowType(
String slidingWindowType) {
return CircuitBreakerThresholds.SlidingWindowType.valueOf(slidingWindowType);

Check warning on line 109 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfigParser.java#L109

Added line #L109 was not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hypertrace.circuitbreaker.grpcutils;

import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
Expand All @@ -11,18 +12,16 @@
@Value
@Builder
public class CircuitBreakerConfiguration<T> {
Class<T> requestClass;
BiFunction<RequestContext, T, String> keyFunction;
@Builder.Default boolean enabled = false;

Check warning on line 17 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java#L15-L17

Added lines #L15 - L17 were not covered by tests
// Default value be "global" if not override.
String defaultCircuitBreakerKey = "global";
// Standard/default thresholds
CircuitBreakerThresholds defaultThresholds;

Check warning on line 19 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java#L19

Added line #L19 was not covered by tests
// Custom overrides for specific cases (less common)
@Builder.Default Map<String, CircuitBreakerThresholds> circuitBreakerThresholdsMap = Map.of();

Check warning on line 21 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java#L21

Added line #L21 was not covered by tests

// New exception builder logic
@Builder.Default
Function<String, RuntimeException> exceptionBuilder =
Function<String, StatusRuntimeException> exceptionBuilder =
reason -> Status.RESOURCE_EXHAUSTED.withDescription(reason).asRuntimeException();

Check warning on line 26 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerConfiguration.java#L25-L26

Added lines #L25 - L26 were not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
// Calls allowed in HALF_OPEN state before deciding to
// CLOSE or OPEN again
@Builder.Default int permittedNumberOfCallsInHalfOpenState = 5;
@Builder.Default boolean enabled = true;

Check warning on line 26 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerThresholds.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/CircuitBreakerThresholds.java#L26

Added line #L26 was not covered by tests

public enum SlidingWindowType {
COUNT_BASED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@
import java.util.Map;
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerConfiguration;

public class ResilienceCircuitBreakerFactory {

Check warning on line 9 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java#L9

Added line #L9 was not covered by tests
public static ResilienceCircuitBreakerInterceptor getResilienceCircuitBreakerInterceptor(
CircuitBreakerConfiguration<?> circuitBreakerConfiguration, Clock clock) {
Map<String, CircuitBreakerConfig> resilienceCircuitBreakerConfigMap =
ResilienceCircuitBreakerConfigConverter.getCircuitBreakerConfigs(
circuitBreakerConfiguration.getCircuitBreakerThresholdsMap());
CircuitBreakerRegistry resilicenceCircuitBreakerRegistry =

Check warning on line 15 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java#L12-L15

Added lines #L12 - L15 were not covered by tests
new ResilienceCircuitBreakerRegistryProvider(
circuitBreakerConfiguration.getDefaultThresholds())
Comment thread
aaron-steinfeld marked this conversation as resolved.
.getCircuitBreakerRegistry();
ResilienceCircuitBreakerProvider resilienceCircuitBreakerProvider =

Check warning on line 19 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java#L17-L19

Added lines #L17 - L19 were not covered by tests
new ResilienceCircuitBreakerProvider(
resilicenceCircuitBreakerRegistry, resilienceCircuitBreakerConfigMap);
resilicenceCircuitBreakerRegistry,
Comment thread
pavan-traceable marked this conversation as resolved.
Outdated
resilienceCircuitBreakerConfigMap,
circuitBreakerConfiguration.getCircuitBreakerThresholdsMap());
Comment thread
pavan-traceable marked this conversation as resolved.
Outdated
return new ResilienceCircuitBreakerInterceptor(

Check warning on line 24 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerFactory.java#L23-L24

Added lines #L23 - L24 were not covered by tests
circuitBreakerConfiguration, clock, resilienceCircuitBreakerProvider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerConfiguration;
Expand All @@ -37,7 +38,7 @@

@Override
protected boolean isCircuitBreakerEnabled() {
return circuitBreakerConfiguration.isEnabled();

Check warning on line 41 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerInterceptor.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerInterceptor.java#L41

Added line #L41 was not covered by tests
}

@Override
Expand All @@ -45,7 +46,7 @@
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new ForwardingClientCall.SimpleForwardingClientCall<>(
next.newCall(method, callOptions)) {
CircuitBreaker circuitBreaker;
Optional<CircuitBreaker> circuitBreaker;
String circuitBreakerKey;

@Override
Expand All @@ -64,20 +65,24 @@
(CircuitBreakerConfiguration<ReqT>) circuitBreakerConfiguration;
// Type check for message class compatibility
if (config.getRequestClass() != null && !config.getRequestClass().isInstance(message)) {
Comment thread
aaron-steinfeld marked this conversation as resolved.
super.sendMessage(message);
return;

Check warning on line 69 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerInterceptor.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerInterceptor.java#L68-L69

Added lines #L68 - L69 were not covered by tests
}
if (config.getKeyFunction() == null) {
log.debug("Circuit breaker will apply to all requests as keyFunction config is not set");
circuitBreakerKey = config.getDefaultCircuitBreakerKey();
} else {
if (config.getKeyFunction() != null) {
circuitBreakerKey = config.getKeyFunction().apply(RequestContext.CURRENT.get(), message);
Comment thread
aaron-steinfeld marked this conversation as resolved.
circuitBreaker = resilienceCircuitBreakerProvider.getCircuitBreaker(circuitBreakerKey);

Check warning on line 73 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerInterceptor.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerInterceptor.java#L72-L73

Added lines #L72 - L73 were not covered by tests
Comment thread
aaron-steinfeld marked this conversation as resolved.
Outdated
} else {
log.debug("Circuit breaker will apply to all requests as keyFunction config is not set");
circuitBreaker = resilienceCircuitBreakerProvider.getDefaultCircuitBreaker();
}
if (circuitBreaker.isEmpty()) {
super.sendMessage(message);
return;
}
circuitBreaker = resilienceCircuitBreakerProvider.getCircuitBreaker(circuitBreakerKey);
if (!circuitBreaker.tryAcquirePermission()) {
logCircuitBreakerRejection(circuitBreakerKey, circuitBreaker);
if (!circuitBreaker.get().tryAcquirePermission()) {
Comment thread
pavan-traceable marked this conversation as resolved.
Outdated
logCircuitBreakerRejection(circuitBreakerKey, circuitBreaker.get());
String rejectionReason =
circuitBreaker.getState() == CircuitBreaker.State.HALF_OPEN
circuitBreaker.get().getState() == CircuitBreaker.State.HALF_OPEN
? "Circuit Breaker is HALF-OPEN and rejecting excess requests"
: "Circuit Breaker is OPEN and blocking requests";
throw config.getExceptionBuilder().apply(rejectionReason);
Expand All @@ -89,18 +94,21 @@
wrapListenerWithCircuitBreaker(Listener<RespT> responseListener, Instant startTime) {
return new ForwardingClientCallListener.SimpleForwardingClientCallListener<>(
responseListener) {
@SuppressWarnings("OptionalGetWithoutIsPresent")
@Override
public void onClose(Status status, Metadata trailers) {
long duration = Duration.between(startTime, clock.instant()).toNanos();
if (status.isOk()) {
circuitBreaker.onSuccess(duration, TimeUnit.NANOSECONDS);
circuitBreaker.get().onSuccess(duration, TimeUnit.NANOSECONDS);
} else {
log.debug(
"Circuit Breaker '{}' detected failure. Status: {}, Description: {}",
circuitBreaker.getName(),
circuitBreaker.get().getName(),
status.getCode(),
status.getDescription());
circuitBreaker.onError(duration, TimeUnit.NANOSECONDS, status.asRuntimeException());
circuitBreaker
.get()
.onError(duration, TimeUnit.NANOSECONDS, status.asRuntimeException());
}
super.onClose(status, trailers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,67 @@
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerConfigParser;
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerThresholds;

/** Utility class to provide Resilience4j CircuitBreaker */
@Slf4j
class ResilienceCircuitBreakerProvider {

private final CircuitBreakerRegistry circuitBreakerRegistry;
private final Map<String, CircuitBreakerConfig> circuitBreakerConfigMap;
private final Map<String, CircuitBreakerThresholds> circuitBreakerThresholdsMap;
private final Map<String, CircuitBreaker> circuitBreakerCache = new ConcurrentHashMap<>();

Check warning on line 20 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L20

Added line #L20 was not covered by tests
Comment thread
aaron-steinfeld marked this conversation as resolved.
Outdated

public ResilienceCircuitBreakerProvider(
CircuitBreakerRegistry circuitBreakerRegistry,
Map<String, CircuitBreakerConfig> circuitBreakerConfigMap) {
Map<String, CircuitBreakerConfig> circuitBreakerConfigMap,
Map<String, CircuitBreakerThresholds> circuitBreakerThresholdsMap) {
this.circuitBreakerRegistry = circuitBreakerRegistry;
this.circuitBreakerConfigMap = circuitBreakerConfigMap;
this.circuitBreakerThresholdsMap = circuitBreakerThresholdsMap;
}

Check warning on line 29 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L25-L29

Added lines #L25 - L29 were not covered by tests

public CircuitBreaker getCircuitBreaker(String circuitBreakerKey) {
return circuitBreakerCache.computeIfAbsent(
circuitBreakerKey,
key -> {
CircuitBreaker circuitBreaker = getCircuitBreakerFromConfigMap(circuitBreakerKey);
circuitBreaker
.getEventPublisher()
.onStateTransition(
event ->
log.info(
"State transition: {} for circuit breaker {}",
event.getStateTransition(),
event.getCircuitBreakerName()))
.onCallNotPermitted(
event ->
log.debug(
"Call not permitted: Circuit is OPEN for circuit breaker {}",
event.getCircuitBreakerName()))
.onEvent(
event ->
log.debug(
"Circuit breaker event type {} for circuit breaker name {}",
event.getEventType(),
event.getCircuitBreakerName()));
return circuitBreaker;
});
public Optional<CircuitBreaker> getCircuitBreaker(String circuitBreakerKey) {
Comment thread
aaron-steinfeld marked this conversation as resolved.
if (circuitBreakerThresholdsMap.containsKey(circuitBreakerKey)
&& !circuitBreakerThresholdsMap.get(circuitBreakerKey).isEnabled()) {
return Optional.empty();

Check warning on line 34 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L34

Added line #L34 was not covered by tests
}
return Optional.of(
Comment thread
aaron-steinfeld marked this conversation as resolved.
Outdated
circuitBreakerCache.computeIfAbsent(

Check warning on line 37 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L36-L37

Added lines #L36 - L37 were not covered by tests
circuitBreakerKey,
key -> {
CircuitBreaker circuitBreaker = getCircuitBreakerFromConfigMap(circuitBreakerKey);
circuitBreaker
.getEventPublisher()
.onStateTransition(

Check warning on line 43 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L40-L43

Added lines #L40 - L43 were not covered by tests
event ->
log.info(

Check warning on line 45 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L45

Added line #L45 was not covered by tests
"State transition: {} for circuit breaker {}",
event.getStateTransition(),
event.getCircuitBreakerName()))
.onCallNotPermitted(

Check warning on line 49 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L47-L49

Added lines #L47 - L49 were not covered by tests
event ->
log.debug(

Check warning on line 51 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L51

Added line #L51 was not covered by tests
"Call not permitted: Circuit is OPEN for circuit breaker {}",
event.getCircuitBreakerName()))
.onEvent(

Check warning on line 54 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L53-L54

Added lines #L53 - L54 were not covered by tests
event ->
log.debug(

Check warning on line 56 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L56

Added line #L56 was not covered by tests
"Circuit breaker event type {} for circuit breaker name {}",
event.getEventType(),
event.getCircuitBreakerName()));
return circuitBreaker;

Check warning on line 60 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L58-L60

Added lines #L58 - L60 were not covered by tests
}));
}

public Optional<CircuitBreaker> getDefaultCircuitBreaker() {
return getCircuitBreaker(CircuitBreakerConfigParser.DEFAULT_THRESHOLDS);

Check warning on line 65 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L65

Added line #L65 was not covered by tests
Comment thread
aaron-steinfeld marked this conversation as resolved.
Outdated
}

private CircuitBreaker getCircuitBreakerFromConfigMap(String circuitBreakerKey) {
return Optional.ofNullable(circuitBreakerConfigMap.get(circuitBreakerKey))
.map(config -> circuitBreakerRegistry.circuitBreaker(circuitBreakerKey, config))
.orElseGet(() -> circuitBreakerRegistry.circuitBreaker(circuitBreakerKey));

Check warning on line 71 in grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java

View check run for this annotation

Codecov / codecov/patch

grpc-circuitbreaker-utils/src/main/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerProvider.java#L69-L71

Added lines #L69 - L71 were not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
Expand All @@ -11,6 +10,7 @@
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerConfiguration;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -40,14 +40,11 @@ void setUp() {

fixedClock = Clock.fixed(Instant.now(), ZoneOffset.UTC);
when(mockChannel.newCall(any(), any())).thenReturn(mockClientCall);
when(mockCircuitBreakerProvider.getCircuitBreaker(anyString())).thenReturn(mockCircuitBreaker);
when(mockCircuitBreakerConfig.getDefaultCircuitBreakerKey()).thenReturn("global");
}

@Test
void testSendMessage_CallsSuperSendMessage_Success() {
doNothing().when(mockClientCall).sendMessage(any());
when(mockCircuitBreaker.tryAcquirePermission()).thenReturn(true);

ResilienceCircuitBreakerInterceptor interceptor =
new ResilienceCircuitBreakerInterceptor(
Expand All @@ -67,6 +64,8 @@ void testSendMessage_CallsSuperSendMessage_Success() {
void testSendMessage_CircuitBreakerRejectsRequest() {
when(mockCircuitBreaker.tryAcquirePermission()).thenReturn(false);
when(mockCircuitBreaker.getState()).thenReturn(CircuitBreaker.State.OPEN);
when(mockCircuitBreakerProvider.getDefaultCircuitBreaker())
.thenReturn(Optional.of(mockCircuitBreaker));
when(mockCircuitBreakerConfig.getExceptionBuilder())
.thenReturn(
reason ->
Expand Down Expand Up @@ -94,6 +93,8 @@ void testSendMessage_CircuitBreakerRejectsRequest() {
void testSendMessage_CircuitBreakerInHalfOpenState() {
when(mockCircuitBreaker.tryAcquirePermission()).thenReturn(false);
when(mockCircuitBreaker.getState()).thenReturn(CircuitBreaker.State.HALF_OPEN);
when(mockCircuitBreakerProvider.getDefaultCircuitBreaker())
.thenReturn(Optional.of(mockCircuitBreaker));
when(mockCircuitBreakerConfig.getExceptionBuilder())
.thenReturn(
reason ->
Expand All @@ -120,6 +121,8 @@ void testSendMessage_CircuitBreakerInHalfOpenState() {
@Test
void testWrapListenerWithCircuitBreaker_Success() {
when(mockCircuitBreaker.tryAcquirePermission()).thenReturn(true);
when(mockCircuitBreakerProvider.getDefaultCircuitBreaker())
.thenReturn(Optional.of(mockCircuitBreaker));
ResilienceCircuitBreakerInterceptor interceptor =
new ResilienceCircuitBreakerInterceptor(
mockCircuitBreakerConfig, fixedClock, mockCircuitBreakerProvider);
Expand All @@ -144,6 +147,8 @@ void testWrapListenerWithCircuitBreaker_Success() {
@Test
void testWrapListenerWithCircuitBreaker_Failure() {
when(mockCircuitBreaker.tryAcquirePermission()).thenReturn(true);
when(mockCircuitBreakerProvider.getDefaultCircuitBreaker())
.thenReturn(Optional.of(mockCircuitBreaker));
ResilienceCircuitBreakerInterceptor interceptor =
new ResilienceCircuitBreakerInterceptor(
mockCircuitBreakerConfig, fixedClock, mockCircuitBreakerProvider);
Expand Down
Loading