Describe the Problem
ResilienceConfiguration.CircuitBreakerConfiguration currently exposes only a subset of the properties available in Resilience4j's CircuitBreakerConfig. Notably missing are:
slowCallDurationThreshold (Resilience4j default: 60 seconds)
slowCallRateThreshold (Resilience4j default: 100%)
This is problematic for applications that perform long-running operations (e.g., large SAP OData batch imports that routinely take 2–10 minutes per call). With the default 60-second slow-call threshold, these calls are classified as "slow", and once the slow-call rate exceeds the threshold, the circuit breaker transitions to OPEN — even though all calls succeed.
Since CircuitBreakerConfig is immutable after construction and DefaultCircuitBreakerProvider does not set these properties from the SDK configuration, there is no clean way to adjust the slow-call behavior without resorting to reflection hacks.
Propose a Solution
Add two optional properties to CircuitBreakerConfiguration:
@NoArgsConstructor(staticName = "of")
@Accessors(fluent = true)
@EqualsAndHashCode
@Getter
@Setter
public static final class CircuitBreakerConfiguration {
// ...existing fields...
@Nonnull
private Duration slowCallDurationThreshold = Duration.ofSeconds(60); // Resilience4j default
private float slowCallRateThreshold = 100f; // Resilience4j default
}
Then wire them through in DefaultCircuitBreakerProvider.getCircuitBreaker():
CircuitBreakerConfig.custom()
.failureRateThreshold(config.failureRateThreshold())
.waitDurationInOpenState(config.waitDuration())
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.slidingWindowSize(config.closedBufferSize())
.minimumNumberOfCalls(config.closedBufferSize())
.permittedNumberOfCallsInHalfOpenState(config.halfOpenBufferSize())
.slowCallDurationThreshold(config.slowCallDurationThreshold()) // NEW
.slowCallRateThreshold(config.slowCallRateThreshold()) // NEW
.build();
This is fully backwards-compatible since the proposed defaults match Resilience4j's existing defaults.
Describe Alternatives
The only current workaround is using reflection to modify the CircuitBreakerConfig after construction:
// Step 1: Access DefaultCircuitBreakerProvider via reflection on Resilience4jDecorationStrategy.decorators (protected field)
Field decoratorsField = Resilience4jDecorationStrategy.class.getDeclaredField("decorators");
decoratorsField.setAccessible(true);
List<GenericDecorator> decorators = (List<GenericDecorator>) decoratorsField.get(strategy);
DefaultCircuitBreakerProvider cbProvider = decorators.stream()
.filter(DefaultCircuitBreakerProvider.class::isInstance)
.map(DefaultCircuitBreakerProvider.class::cast)
.findFirst().orElseThrow();
// Step 2: Obtain CircuitBreaker instance and mutate the immutable config via reflection
CircuitBreaker cb = cbProvider.getCircuitBreaker(myResilienceConfiguration);
Field field = cb.getCircuitBreakerConfig().getClass().getDeclaredField("slowCallDurationThreshold");
field.setAccessible(true);
field.set(cb.getCircuitBreakerConfig(), Duration.ofMinutes(5));
This approach is fragile because:
- It accesses
protected internals of Resilience4jDecorationStrategy
- It mutates an immutable Resilience4j config object via reflection
- It can break silently on any minor version update of either the SAP Cloud SDK or Resilience4j
Affected Development Phase
Production
Impact
Inconvenience
Timeline
No immediate deadline — this is a long-standing workaround we'd like to eliminate. Happy to contribute a PR if the team agrees with the approach.
Describe the Problem
ResilienceConfiguration.CircuitBreakerConfigurationcurrently exposes only a subset of the properties available in Resilience4j'sCircuitBreakerConfig. Notably missing are:slowCallDurationThreshold(Resilience4j default: 60 seconds)slowCallRateThreshold(Resilience4j default: 100%)This is problematic for applications that perform long-running operations (e.g., large SAP OData batch imports that routinely take 2–10 minutes per call). With the default 60-second slow-call threshold, these calls are classified as "slow", and once the slow-call rate exceeds the threshold, the circuit breaker transitions to OPEN — even though all calls succeed.
Since
CircuitBreakerConfigis immutable after construction andDefaultCircuitBreakerProviderdoes not set these properties from the SDK configuration, there is no clean way to adjust the slow-call behavior without resorting to reflection hacks.Propose a Solution
Add two optional properties to
CircuitBreakerConfiguration:Then wire them through in
DefaultCircuitBreakerProvider.getCircuitBreaker():This is fully backwards-compatible since the proposed defaults match Resilience4j's existing defaults.
Describe Alternatives
The only current workaround is using reflection to modify the
CircuitBreakerConfigafter construction:This approach is fragile because:
protectedinternals ofResilience4jDecorationStrategyAffected Development Phase
Production
Impact
Inconvenience
Timeline
No immediate deadline — this is a long-standing workaround we'd like to eliminate. Happy to contribute a PR if the team agrees with the approach.