Skip to content

Commit 3592deb

Browse files
RomehRobWin
authored andcommitted
Issue 773: Added support to retry configuration customization (ReactiveX#792)
1 parent 650e37b commit 3592deb

File tree

17 files changed

+193
-32
lines changed

17 files changed

+193
-32
lines changed

resilience4j-framework-common/src/main/java/io/github/resilience4j/common/circuitbreaker/configuration/CompositeCircuitBreakerCustomizer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
*/
1313
public class CompositeCircuitBreakerCustomizer {
1414

15-
final Map<String, CircuitBreakerConfigCustomizer> customizerMap = new HashMap<>();
15+
private final Map<String, CircuitBreakerConfigCustomizer> customizerMap = new HashMap<>();
1616

1717
public CompositeCircuitBreakerCustomizer(List<CircuitBreakerConfigCustomizer> customizers) {
1818

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.github.resilience4j.common.retry.configuration;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.Optional;
7+
import java.util.function.Function;
8+
import java.util.stream.Collectors;
9+
10+
/**
11+
* the composite of any retry {@link RetryConfigCustomizer} implementations.
12+
*/
13+
public class CompositeRetryCustomizer {
14+
15+
private final Map<String, RetryConfigCustomizer> customizerMap = new HashMap<>();
16+
17+
public CompositeRetryCustomizer(List<RetryConfigCustomizer> customizers) {
18+
19+
if (customizers != null && !customizers.isEmpty()) {
20+
customizerMap.putAll(customizers.stream()
21+
.collect(
22+
Collectors.toMap(RetryConfigCustomizer::name, Function.identity())));
23+
}
24+
25+
}
26+
27+
/**
28+
* @param retryInstanceName the retry instance name
29+
* @return the found {@link RetryConfigCustomizer} if any .
30+
*/
31+
public Optional<RetryConfigCustomizer> getRetryConfigCustomizer(
32+
String retryInstanceName) {
33+
return Optional.ofNullable(customizerMap.get(retryInstanceName));
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.github.resilience4j.common.retry.configuration;
2+
3+
import io.github.resilience4j.retry.RetryConfig;
4+
5+
/**
6+
* Enable customization retry configuration builders programmatically.
7+
*/
8+
public interface RetryConfigCustomizer {
9+
10+
/**
11+
* Retry configuration builder.
12+
*
13+
* @param configBuilder to be customized
14+
*/
15+
void customize(RetryConfig.Builder configBuilder);
16+
17+
/**
18+
* @return name of the retry instance to be customized
19+
*/
20+
String name();
21+
}

resilience4j-framework-common/src/main/java/io/github/resilience4j/common/retry/configuration/RetryConfigurationProperties.java

+20-9
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ public class RetryConfigurationProperties extends CommonProperties {
4242
* @param backend backend name
4343
* @return the retry configuration
4444
*/
45-
public RetryConfig createRetryConfig(String backend) {
46-
return createRetryConfig(getBackendProperties(backend));
45+
public RetryConfig createRetryConfig(String backend,
46+
CompositeRetryCustomizer compositeRetryCustomizer) {
47+
return createRetryConfig(getBackendProperties(backend), compositeRetryCustomizer, backend);
4748
}
4849

4950
/**
@@ -80,23 +81,29 @@ public Map<String, InstanceProperties> getConfigs() {
8081
* @param instanceProperties the retry backend spring properties
8182
* @return the retry configuration
8283
*/
83-
public RetryConfig createRetryConfig(InstanceProperties instanceProperties) {
84+
public RetryConfig createRetryConfig(InstanceProperties instanceProperties,
85+
CompositeRetryCustomizer compositeRetryCustomizer, String backend) {
8486
if (instanceProperties != null && StringUtils
8587
.isNotEmpty(instanceProperties.getBaseConfig())) {
8688
InstanceProperties baseProperties = configs.get(instanceProperties.getBaseConfig());
8789
if (baseProperties == null) {
8890
throw new ConfigurationNotFoundException(instanceProperties.getBaseConfig());
8991
}
90-
return buildConfigFromBaseConfig(baseProperties, instanceProperties);
92+
return buildConfigFromBaseConfig(baseProperties, instanceProperties,
93+
compositeRetryCustomizer, backend);
9194
}
92-
return buildRetryConfig(RetryConfig.custom(), instanceProperties);
95+
return buildRetryConfig(RetryConfig.custom(), instanceProperties, compositeRetryCustomizer,
96+
backend);
9397
}
9498

9599
private RetryConfig buildConfigFromBaseConfig(InstanceProperties baseProperties,
96-
InstanceProperties instanceProperties) {
97-
RetryConfig baseConfig = buildRetryConfig(RetryConfig.custom(), baseProperties);
100+
InstanceProperties instanceProperties, CompositeRetryCustomizer compositeRetryCustomizer,
101+
String backend) {
102+
RetryConfig baseConfig = buildRetryConfig(RetryConfig.custom(), baseProperties,
103+
compositeRetryCustomizer, backend);
98104
ConfigUtils.mergePropertiesIfAny(baseProperties, instanceProperties);
99-
return buildRetryConfig(RetryConfig.from(baseConfig), instanceProperties);
105+
return buildRetryConfig(RetryConfig.from(baseConfig), instanceProperties,
106+
compositeRetryCustomizer, backend);
100107
}
101108

102109
/**
@@ -105,7 +112,8 @@ private RetryConfig buildConfigFromBaseConfig(InstanceProperties baseProperties,
105112
*/
106113
@SuppressWarnings("unchecked")
107114
private RetryConfig buildRetryConfig(RetryConfig.Builder builder,
108-
InstanceProperties properties) {
115+
InstanceProperties properties, CompositeRetryCustomizer compositeRetryCustomizer,
116+
String backend) {
109117
if (properties == null) {
110118
return builder.build();
111119
}
@@ -150,6 +158,9 @@ private RetryConfig buildRetryConfig(RetryConfig.Builder builder,
150158
}
151159
}
152160

161+
compositeRetryCustomizer.getRetryConfigCustomizer(backend)
162+
.ifPresent(customizer -> customizer.customize(builder));
163+
153164
return builder.build();
154165
}
155166

resilience4j-framework-common/src/test/java/io/github/resilience4j/common/retry/configuration/RetryConfigurationPropertiesTest.java

+15-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.mockito.junit.MockitoJUnitRunner;
2424

2525
import java.time.Duration;
26+
import java.util.Collections;
2627
import java.util.HashMap;
2728
import java.util.Map;
2829

@@ -61,8 +62,10 @@ public void testRetryProperties() {
6162
assertThat(retryConfigurationProperties.getTags().size()).isEqualTo(1);
6263
assertThat(retryConfigurationProperties.getInstances().size()).isEqualTo(2);
6364
assertThat(retryConfigurationProperties.getBackends().size()).isEqualTo(2);
64-
final RetryConfig retry1 = retryConfigurationProperties.createRetryConfig("backend1");
65-
final RetryConfig retry2 = retryConfigurationProperties.createRetryConfig("backend2");
65+
final RetryConfig retry1 = retryConfigurationProperties
66+
.createRetryConfig("backend1", compositeRetryCustomizer());
67+
final RetryConfig retry2 = retryConfigurationProperties
68+
.createRetryConfig("backend2", compositeRetryCustomizer());
6669
RetryConfigurationProperties.InstanceProperties instancePropertiesForRetry1 = retryConfigurationProperties
6770
.getInstances().get("backend1");
6871
assertThat(instancePropertiesForRetry1.getWaitDuration().toMillis()).isEqualTo(1000);
@@ -106,20 +109,21 @@ public void testCreateRetryPropertiesWithSharedConfigs() {
106109
//Then
107110
// Should get default config and overwrite max attempt and wait time
108111
RetryConfig retry1 = retryConfigurationProperties
109-
.createRetryConfig("backendWithDefaultConfig");
112+
.createRetryConfig("backendWithDefaultConfig", compositeRetryCustomizer());
110113
assertThat(retry1).isNotNull();
111114
assertThat(retry1.getMaxAttempts()).isEqualTo(3);
112115
assertThat(retry1.getIntervalFunction().apply(1)).isEqualTo(200L);
113116

114117
// Should get shared config and overwrite wait time
115118
RetryConfig retry2 = retryConfigurationProperties
116-
.createRetryConfig("backendWithSharedConfig");
119+
.createRetryConfig("backendWithSharedConfig", compositeRetryCustomizer());
117120
assertThat(retry2).isNotNull();
118121
assertThat(retry2.getMaxAttempts()).isEqualTo(2);
119122
assertThat(retry2.getIntervalFunction().apply(1)).isEqualTo(300L);
120123

121124
// Unknown backend should get default config of Registry
122-
RetryConfig retry3 = retryConfigurationProperties.createRetryConfig("unknownBackend");
125+
RetryConfig retry3 = retryConfigurationProperties
126+
.createRetryConfig("unknownBackend", compositeRetryCustomizer());
123127
assertThat(retry3).isNotNull();
124128
assertThat(retry3.getMaxAttempts()).isEqualTo(3);
125129

@@ -134,7 +138,8 @@ public void testCreatePropertiesWithUnknownConfig() {
134138
retryConfigurationProperties.getInstances().put("backend", instanceProperties);
135139

136140
//then
137-
assertThatThrownBy(() -> retryConfigurationProperties.createRetryConfig("backend"))
141+
assertThatThrownBy(() -> retryConfigurationProperties
142+
.createRetryConfig("backend", compositeRetryCustomizer()))
138143
.isInstanceOf(ConfigurationNotFoundException.class)
139144
.hasMessage("Configuration with name 'unknownConfig' does not exist");
140145
}
@@ -156,4 +161,8 @@ public void testIllegalArgumentOnMaxRetryAttempts() {
156161
RetryConfigurationProperties.InstanceProperties defaultProperties = new RetryConfigurationProperties.InstanceProperties();
157162
defaultProperties.setMaxRetryAttempts(0);
158163
}
164+
165+
private CompositeRetryCustomizer compositeRetryCustomizer() {
166+
return new CompositeRetryCustomizer(Collections.emptyList());
167+
}
159168
}

resilience4j-ratpack/src/main/java/io/github/resilience4j/ratpack/Resilience4jModule.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties;
3737
import io.github.resilience4j.common.circuitbreaker.configuration.CompositeCircuitBreakerCustomizer;
3838
import io.github.resilience4j.common.ratelimiter.configuration.RateLimiterConfigurationProperties;
39+
import io.github.resilience4j.common.retry.configuration.CompositeRetryCustomizer;
3940
import io.github.resilience4j.common.retry.configuration.RetryConfigurationProperties;
4041
import io.github.resilience4j.consumer.DefaultEventConsumerRegistry;
4142
import io.github.resilience4j.consumer.EventConsumerRegistry;
@@ -290,14 +291,16 @@ public RetryRegistry get() {
290291
RetryConfigurationProperties RetryProperties = resilience4jConfig.getRetry();
291292
Map<String, RetryConfig> configs = RetryProperties.getConfigs()
292293
.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
293-
entry -> RetryProperties.createRetryConfig(entry.getValue())));
294+
entry -> RetryProperties.createRetryConfig(entry.getValue(),
295+
new CompositeRetryCustomizer(Collections.emptyList()), entry.getKey())));
294296
RetryRegistry retryRegistry = RetryRegistry.of(configs);
295297

296298
// build retries
297299
EndpointsConfig endpointsConfig = resilience4jConfig.getEndpoints();
298300
RetryProperties.getInstances().forEach((name, retryConfig) -> {
299301
io.github.resilience4j.retry.Retry retry =
300-
retryRegistry.retry(name, RetryProperties.createRetryConfig(retryConfig));
302+
retryRegistry.retry(name, RetryProperties.createRetryConfig(retryConfig,
303+
new CompositeRetryCustomizer(Collections.emptyList()), name));
301304
if (endpointsConfig.getRetry().isEnabled()) {
302305
retry.getEventPublisher().onEvent(eventConsumerRegistry
303306
.createEventConsumer(name,

resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/retry/autoconfigure/AbstractRetryConfigurationOnMissingBean.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package io.github.resilience4j.retry.autoconfigure;
1717

18+
import io.github.resilience4j.common.retry.configuration.CompositeRetryCustomizer;
1819
import io.github.resilience4j.consumer.EventConsumerRegistry;
1920
import io.github.resilience4j.core.registry.RegistryEventConsumer;
2021
import io.github.resilience4j.fallback.FallbackDecorators;
@@ -56,10 +57,11 @@ public AbstractRetryConfigurationOnMissingBean() {
5657
@ConditionalOnMissingBean
5758
public RetryRegistry retryRegistry(RetryConfigurationProperties retryConfigurationProperties,
5859
EventConsumerRegistry<RetryEvent> retryEventConsumerRegistry,
59-
RegistryEventConsumer<Retry> retryRegistryEventConsumer) {
60+
RegistryEventConsumer<Retry> retryRegistryEventConsumer,
61+
CompositeRetryCustomizer compositeRetryCustomizer) {
6062
return retryConfiguration
6163
.retryRegistry(retryConfigurationProperties, retryEventConsumerRegistry,
62-
retryRegistryEventConsumer);
64+
retryRegistryEventConsumer, compositeRetryCustomizer);
6365
}
6466

6567
@Bean

resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/SpringBootCommonTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.github.resilience4j.common.bulkhead.configuration.ThreadPoolBulkheadConfigurationProperties;
2626
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigCustomizer;
2727
import io.github.resilience4j.common.circuitbreaker.configuration.CompositeCircuitBreakerCustomizer;
28+
import io.github.resilience4j.common.retry.configuration.CompositeRetryCustomizer;
2829
import io.github.resilience4j.consumer.DefaultEventConsumerRegistry;
2930
import io.github.resilience4j.core.registry.CompositeRegistryEventConsumer;
3031
import io.github.resilience4j.fallback.CompletionStageFallbackDecorator;
@@ -94,7 +95,8 @@ public void testRetryCommonConfig() {
9495
assertThat(retryConfigurationOnMissingBean.rxJava2RetryAspectExt()).isNotNull();
9596
assertThat(retryConfigurationOnMissingBean
9697
.retryRegistry(new RetryConfigurationProperties(), new DefaultEventConsumerRegistry<>(),
97-
new CompositeRegistryEventConsumer<>(emptyList()))).isNotNull();
98+
new CompositeRegistryEventConsumer<>(emptyList()),
99+
new CompositeRetryCustomizer(Collections.emptyList()))).isNotNull();
98100
assertThat(retryConfigurationOnMissingBean
99101
.retryAspect(new RetryConfigurationProperties(), RetryRegistry.ofDefaults(),
100102
Collections.emptyList(),

resilience4j-spring-boot/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryConfigurationOnMissingBean.java

+13
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@
1717

1818
import io.github.resilience4j.common.IntegerToDurationConverter;
1919
import io.github.resilience4j.common.StringToDurationConverter;
20+
import io.github.resilience4j.common.retry.configuration.CompositeRetryCustomizer;
21+
import io.github.resilience4j.common.retry.configuration.RetryConfigCustomizer;
2022
import io.github.resilience4j.consumer.DefaultEventConsumerRegistry;
2123
import io.github.resilience4j.consumer.EventConsumerRegistry;
2224
import io.github.resilience4j.retry.event.RetryEvent;
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2327
import org.springframework.context.annotation.Bean;
2428
import org.springframework.context.annotation.Configuration;
2529
import org.springframework.context.annotation.Import;
2630

31+
import java.util.List;
32+
2733
/**
2834
* {@link Configuration Configuration} for resilience4j-retry.
2935
*/
@@ -43,4 +49,11 @@ public EventConsumerRegistry<RetryEvent> retryEventConsumerRegistry() {
4349
return retryConfiguration.retryEventConsumerRegistry();
4450
}
4551

52+
@Bean
53+
@ConditionalOnMissingBean
54+
public CompositeRetryCustomizer compositeRetryCustomizer(
55+
@Autowired(required = false) List<RetryConfigCustomizer> customizers) {
56+
return new CompositeRetryCustomizer(customizers);
57+
}
58+
4659
}

resilience4j-spring-boot2/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryConfigurationOnMissingBean.java

+12
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@
1515
*/
1616
package io.github.resilience4j.retry.autoconfigure;
1717

18+
import io.github.resilience4j.common.retry.configuration.CompositeRetryCustomizer;
19+
import io.github.resilience4j.common.retry.configuration.RetryConfigCustomizer;
1820
import io.github.resilience4j.consumer.DefaultEventConsumerRegistry;
1921
import io.github.resilience4j.consumer.EventConsumerRegistry;
2022
import io.github.resilience4j.retry.event.RetryEvent;
2123
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2224
import org.springframework.context.annotation.Bean;
2325
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.lang.Nullable;
27+
28+
import java.util.List;
2429

2530
/**
2631
* {@link Configuration Configuration} for resilience4j-retry.
@@ -41,4 +46,11 @@ public EventConsumerRegistry<RetryEvent> retryEventConsumerRegistry() {
4146
return retryConfiguration.retryEventConsumerRegistry();
4247
}
4348

49+
@Bean
50+
@ConditionalOnMissingBean
51+
public CompositeRetryCustomizer compositeRetryCustomizer(
52+
@Nullable List<RetryConfigCustomizer> customizers) {
53+
return new CompositeRetryCustomizer(customizers);
54+
}
55+
4456
}

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/retry/RetryAutoConfigurationTest.java

+5
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ public void testRetryAutoConfiguration() throws IOException {
182182
assertThat(retry.getRetryConfig().getExceptionPredicate().test(new IgnoredException()))
183183
.isFalse();
184184
assertThat(retryAspect.getOrder()).isEqualTo(399);
185+
186+
// test Customizer effect
187+
Retry retryCustom = retryRegistry.retry("retryBackendD");
188+
assertThat(retryCustom.getRetryConfig().getMaxAttempts()).isEqualTo(4);
189+
185190
}
186191

187192
private RetryEventsEndpointResponse retryEvents(String s) {

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/service/test/TestApplication.java

+19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
44
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigCustomizer;
5+
import io.github.resilience4j.common.retry.configuration.RetryConfigCustomizer;
6+
import io.github.resilience4j.retry.RetryConfig;
57
import org.springframework.boot.SpringApplication;
68
import org.springframework.boot.autoconfigure.SpringBootApplication;
79
import org.springframework.cloud.openfeign.EnableFeignClients;
@@ -33,4 +35,21 @@ public String name() {
3335
};
3436

3537
}
38+
39+
@Bean
40+
public RetryConfigCustomizer testRetryCustomizer() {
41+
return new RetryConfigCustomizer() {
42+
@Override
43+
public void customize(RetryConfig.Builder builder) {
44+
builder.maxAttempts(4);
45+
}
46+
47+
@Override
48+
public String name() {
49+
return "retryBackendD";
50+
}
51+
};
52+
53+
}
54+
3655
}

resilience4j-spring-boot2/src/test/resources/application.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ resilience4j.retry:
2828
- io.github.resilience4j.circuitbreaker.IgnoredException
2929
retryDummyFeignClient:
3030
baseConfig: default
31+
retryBackendD:
32+
baseConfig: default
3133

3234
resilience4j.circuitbreaker:
3335
circuitBreakerAspectOrder: 400

0 commit comments

Comments
 (0)