From 22258f6a42dbb6672dbe252304e8c5d9cdaf4ac2 Mon Sep 17 00:00:00 2001 From: Ingyu Hwang Date: Tue, 4 Feb 2020 17:12:41 +0900 Subject: [PATCH] Support to extend Micrometer tags in TimeLimiter (#838) --- .../TimeLimiterConfigurationProperties.java | 3 +- ...imeLimiterConfigurationPropertiesTest.java | 11 +++ .../tagged/AbstractTimeLimiterMetrics.java | 9 ++ ...TaggedTimeLimiterMetricsPublisherTest.java | 13 +++ .../retry/internal/InMemoryRetryRegistry.java | 2 +- .../TimeLimiterAutoConfigurationTest.java | 1 + .../src/test/resources/application.yaml | 5 +- .../configure/TimeLimiterConfiguration.java | 4 +- .../resilience4j/timelimiter/TimeLimiter.java | 24 +++++ .../timelimiter/TimeLimiterRegistry.java | 93 ++++++++++++++++++ .../internal/InMemoryTimeLimiterRegistry.java | 95 ++++++++++++++++--- .../timelimiter/internal/TimeLimiterImpl.java | 17 ++++ .../timelimiter/TimeLimiterRegistryTest.java | 79 +++++++++++++++ 13 files changed, 337 insertions(+), 19 deletions(-) diff --git a/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationProperties.java b/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationProperties.java index 81296e7447..8e1ca62cad 100644 --- a/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationProperties.java +++ b/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationProperties.java @@ -16,6 +16,7 @@ package io.github.resilience4j.common.timelimiter.configuration; +import io.github.resilience4j.common.CommonProperties; import io.github.resilience4j.common.utils.ConfigUtils; import io.github.resilience4j.core.ConfigurationNotFoundException; import io.github.resilience4j.core.StringUtils; @@ -27,7 +28,7 @@ import java.util.Map; import java.util.Objects; -public class TimeLimiterConfigurationProperties { +public class TimeLimiterConfigurationProperties extends CommonProperties { private final Map instances = new HashMap<>(); private final Map configs = new HashMap<>(); diff --git a/resilience4j-framework-common/src/test/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationPropertiesTest.java b/resilience4j-framework-common/src/test/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationPropertiesTest.java index 192eeab2b8..3a868d21e1 100644 --- a/resilience4j-framework-common/src/test/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationPropertiesTest.java +++ b/resilience4j-framework-common/src/test/java/io/github/resilience4j/common/timelimiter/configuration/TimeLimiterConfigurationPropertiesTest.java @@ -5,6 +5,8 @@ import org.junit.Test; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -27,8 +29,12 @@ public void testTimeLimiterProperties() { TimeLimiterConfigurationProperties timeLimiterConfigurationProperties = new TimeLimiterConfigurationProperties(); timeLimiterConfigurationProperties.getInstances().put("backend1", instanceProperties1); timeLimiterConfigurationProperties.getInstances().put("backend2", instanceProperties2); + Map tags = new HashMap<>(); + tags.put("testKey1","testKet2"); + timeLimiterConfigurationProperties.setTags(tags); // Then + assertThat(timeLimiterConfigurationProperties.getTags()).isNotEmpty(); assertThat(timeLimiterConfigurationProperties.getInstances().size()).isEqualTo(2); final TimeLimiterConfig timeLimiter1 = timeLimiterConfigurationProperties.createTimeLimiterConfig("backend1"); final TimeLimiterConfig timeLimiter2 = timeLimiterConfigurationProperties.createTimeLimiterConfig("backend2"); @@ -71,7 +77,12 @@ public void testCreateTimeLimiterPropertiesWithSharedConfigs() { timeLimiterConfigurationProperties.getInstances().put("backendWithDefaultConfig", backendWithDefaultConfig); timeLimiterConfigurationProperties.getInstances().put("backendWithSharedConfig", backendWithSharedConfig); + Map globalTags = new HashMap<>(); + globalTags.put("testKey1","testKet2"); + timeLimiterConfigurationProperties.setTags(globalTags); + //Then + assertThat(timeLimiterConfigurationProperties.getTags()).isNotEmpty(); // Should get default config and overwrite max attempt and wait time TimeLimiterConfig timeLimiter1 = timeLimiterConfigurationProperties.createTimeLimiterConfig("backendWithDefaultConfig"); assertThat(timeLimiter1).isNotNull(); diff --git a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractTimeLimiterMetrics.java b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractTimeLimiterMetrics.java index a2aea9892a..f896d1053b 100644 --- a/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractTimeLimiterMetrics.java +++ b/resilience4j-micrometer/src/main/java/io/github/resilience4j/micrometer/tagged/AbstractTimeLimiterMetrics.java @@ -20,6 +20,7 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; import java.util.Arrays; import java.util.HashSet; @@ -40,6 +41,11 @@ protected AbstractTimeLimiterMetrics(MetricNames names) { } protected void addMetrics(MeterRegistry meterRegistry, TimeLimiter timeLimiter) { + List customTags = mapToTagsList(timeLimiter.getTags().toJavaMap()); + registerMetrics(meterRegistry, timeLimiter, customTags); + } + + protected void registerMetrics(MeterRegistry meterRegistry, TimeLimiter timeLimiter, List customTags) { // Remove previous meters before register removeMetrics(meterRegistry, timeLimiter.getName()); @@ -47,16 +53,19 @@ protected void addMetrics(MeterRegistry meterRegistry, TimeLimiter timeLimiter) .description("The number of successful calls") .tag(TagNames.NAME, timeLimiter.getName()) .tag(TagNames.KIND, KIND_SUCCESSFUL) + .tags(customTags) .register(meterRegistry); Counter failures = Counter.builder(names.getCallsMetricName()) .description("The number of failed calls") .tag(TagNames.NAME, timeLimiter.getName()) .tag(TagNames.KIND, KIND_FAILED) + .tags(customTags) .register(meterRegistry); Counter timeouts = Counter.builder(names.getCallsMetricName()) .description("The number of timed out calls") .tag(TagNames.NAME, timeLimiter.getName()) .tag(TagNames.KIND, KIND_TIMEOUT) + .tags(customTags) .register(meterRegistry); timeLimiter.getEventPublisher() diff --git a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedTimeLimiterMetricsPublisherTest.java b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedTimeLimiterMetricsPublisherTest.java index a55e9c690a..f11ee74f7f 100644 --- a/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedTimeLimiterMetricsPublisherTest.java +++ b/resilience4j-micrometer/src/test/java/io/github/resilience4j/micrometer/tagged/TaggedTimeLimiterMetricsPublisherTest.java @@ -22,6 +22,7 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.search.RequiredSearch; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.Before; import org.junit.Test; @@ -73,6 +74,18 @@ public void shouldAddMetricsForANewlyCreatedTimeLimiter() { assertThat(successful).map(Counter::count).contains(1d); } + @Test + public void shouldAddCustomTags() { + TimeLimiter timeLimiterF = timeLimiterRegistry.timeLimiter("backendF", io.vavr.collection.HashMap.of("key1", "value1")); + timeLimiterF.onSuccess(); + assertThat(taggedTimeLimiterMetricsPublisher.meterIdMap).containsKeys("backendA", "backendF"); + assertThat(taggedTimeLimiterMetricsPublisher.meterIdMap.get("backendF")).hasSize(3); + + assertThat(meterRegistry.getMeters()).hasSize(6); + RequiredSearch match = meterRegistry.get(DEFAULT_TIME_LIMITER_CALLS).tags("key1", "value1"); + assertThat(match).isNotNull(); + } + @Test public void shouldRemovedMetricsForRemovedRetry() { assertThat(meterRegistry.getMeters()).hasSize(3); diff --git a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java index b7c6009ee8..9e14f00dd2 100644 --- a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java +++ b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/internal/InMemoryRetryRegistry.java @@ -79,7 +79,7 @@ public InMemoryRetryRegistry(Map configs, List> registryEventConsumers, io.vavr.collection.Map tags) { this(configs.getOrDefault(DEFAULT_CONFIG, RetryConfig.ofDefaults()), - registryEventConsumers); + registryEventConsumers, tags); this.configurations.putAll(configs); } diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterAutoConfigurationTest.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterAutoConfigurationTest.java index 1a8999c209..806ad22117 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterAutoConfigurationTest.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterAutoConfigurationTest.java @@ -46,6 +46,7 @@ public class TimeLimiterAutoConfigurationTest { public void testTimeLimiterAutoConfigurationTest() throws Exception { assertThat(timeLimiterRegistry).isNotNull(); assertThat(timeLimiterProperties).isNotNull(); + assertThat(timeLimiterRegistry.getTags()).isNotEmpty(); TimeLimiterEventsEndpointResponse timeLimiterEventsBefore = timeLimiterEvents("/actuator/timelimiterevents"); diff --git a/resilience4j-spring-boot2/src/test/resources/application.yaml b/resilience4j-spring-boot2/src/test/resources/application.yaml index 48909548c1..d73398dea9 100644 --- a/resilience4j-spring-boot2/src/test/resources/application.yaml +++ b/resilience4j-spring-boot2/src/test/resources/application.yaml @@ -138,7 +138,7 @@ resilience4j.bulkhead: maxWaitDuration: 100 maxConcurrentCalls: 10 -resilience4j.thread-pool-bulkhead": +resilience4j.thread-pool-bulkhead: tags: tag1: tag1Value tag2: tag2Value @@ -164,6 +164,9 @@ resilience4j.thread-pool-bulkhead": - io.github.resilience4j.TestThreadLocalContextPropagator resilience4j.timelimiter: + tags: + tag1: tag1Value + tag2: tag2Value time-limiter-aspect-order: 398 configs: default: diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/timelimiter/configure/TimeLimiterConfiguration.java b/resilience4j-spring/src/main/java/io/github/resilience4j/timelimiter/configure/TimeLimiterConfiguration.java index d7c57c43f9..1e79a2ac9c 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/timelimiter/configure/TimeLimiterConfiguration.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/timelimiter/configure/TimeLimiterConfiguration.java @@ -28,6 +28,7 @@ import io.github.resilience4j.utils.AspectJOnClasspathCondition; import io.github.resilience4j.utils.ReactorOnClasspathCondition; import io.github.resilience4j.utils.RxJava2OnClasspathCondition; +import io.vavr.collection.HashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -108,7 +109,8 @@ private static TimeLimiterRegistry createTimeLimiterRegistry(TimeLimiterConfigur .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> timeLimiterConfigurationProperties.createTimeLimiterConfig(entry.getValue()))); - return TimeLimiterRegistry.of(configs, timeLimiterRegistryEventConsumer); + return TimeLimiterRegistry.of(configs, timeLimiterRegistryEventConsumer, + HashMap.ofAll(timeLimiterConfigurationProperties.getTags())); } /** diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java index f9a1ace557..a6c7dc252b 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiter.java @@ -60,6 +60,23 @@ static TimeLimiter of(String name, TimeLimiterConfig timeLimiterConfig) { return new TimeLimiterImpl(name, timeLimiterConfig); } + /** + * Creates a TimeLimiter with a custom TimeLimiter configuration. + *

+ * The {@code tags} passed will be appended to the tags already configured for the registry. + * When tags (keys) of the two collide the tags passed with this method will override the tags + * of the registry. + * + * @param name the name of the TimeLimiter + * @param timeLimiterConfig a custom TimeLimiter configuration + * @param tags tags added to the Retry + * @return a TimeLimiter with a custom TimeLimiter configuration. + */ + static TimeLimiter of(String name, TimeLimiterConfig timeLimiterConfig, + io.vavr.collection.Map tags) { + return new TimeLimiterImpl(name, timeLimiterConfig, tags); + } + /** * Creates a TimeLimiter decorator with a timeout Duration. * @@ -104,6 +121,13 @@ static > Supplier> decorateCo String getName(); + /** + * Returns an unmodifiable map with tags assigned to this TimeLimiter. + * + * @return the tags assigned to this TimeLimiter in an unmodifiable map + */ + io.vavr.collection.Map getTags(); + /** * Get the TimeLimiterConfig of this TimeLimiter decorator. * diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterRegistry.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterRegistry.java index 8e45fe7ccf..4a52e23a48 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterRegistry.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/TimeLimiterRegistry.java @@ -90,6 +90,20 @@ static TimeLimiterRegistry of(Map configs) { return new InMemoryTimeLimiterRegistry(configs); } + /** + * Creates a TimeLimiterRegistry with a Map of shared TimeLimiter configurations. + *

+ * Tags added to the registry will be added to every instance created by this registry. + * + * @param configs a Map of shared TimeLimiter configurations + * @param tags default tags to add to the registry + * @return a TimeLimiterRegistry with a Map of shared TimeLimiter configurations. + */ + static TimeLimiterRegistry of(Map configs, + io.vavr.collection.Map tags) { + return new InMemoryTimeLimiterRegistry(configs, tags); + } + /** * Creates a TimeLimiterRegistry with a Map of shared TimeLimiter configurations and a * TimeLimiter registry event consumer. @@ -104,6 +118,22 @@ static TimeLimiterRegistry of(Map configs, return new InMemoryTimeLimiterRegistry(configs, registryEventConsumer); } + /** + * Creates a TimeLimiterRegistry with a Map of shared TimeLimiter configurations and a + * TimeLimiter registry event consumer. + * + * @param configs a Map of shared TimeLimiter configurations. + * @param registryEventConsumer a TimeLimiter registry event consumer. + * @param tags default tags to add to the registry + * @return a TimeLimiterRegistry with a Map of shared TimeLimiter configurations and a + * TimeLimiter registry event consumer. + */ + static TimeLimiterRegistry of(Map configs, + RegistryEventConsumer registryEventConsumer, + io.vavr.collection.Map tags) { + return new InMemoryTimeLimiterRegistry(configs, registryEventConsumer, tags); + } + /** * Creates a TimeLimiterRegistry with a Map of shared TimeLimiter configurations and a list of * TimeLimiter registry event consumers. @@ -134,6 +164,20 @@ static TimeLimiterRegistry of(Map configs, */ TimeLimiter timeLimiter(String name); + /** + * Returns a managed {@link TimeLimiter} or creates a new one with the default TimeLimiter + * configuration. + *

+ * The {@code tags} passed will be appended to the tags already configured for the registry. + * When tags (keys) of the two collide the tags passed with this method will override the tags + * of the registry. + * + * @param name the name of the TimeLimiter + * @param tags tags added to the TimeLimiter + * @return The {@link TimeLimiter} + */ + TimeLimiter timeLimiter(String name, io.vavr.collection.Map tags); + /** * Returns a managed {@link TimeLimiter} or creates a new one with a custom TimeLimiter * configuration. @@ -144,6 +188,22 @@ static TimeLimiterRegistry of(Map configs, */ TimeLimiter timeLimiter(String name, TimeLimiterConfig timeLimiterConfig); + /** + * Returns a managed {@link TimeLimiter} or creates a new one with a custom TimeLimiter + * configuration. + *

+ * The {@code tags} passed will be appended to the tags already configured for the registry. + * When tags (keys) of the two collide the tags passed with this method will override the tags + * of the registry. + * + * @param name the name of the TimeLimiter + * @param timeLimiterConfig a custom TimeLimiter configuration + * @param tags tags added to the TimeLimiter + * @return The {@link TimeLimiter} + */ + TimeLimiter timeLimiter(String name, TimeLimiterConfig timeLimiterConfig, + io.vavr.collection.Map tags); + /** * Returns a managed {@link TimeLimiterConfig} or creates a new one with a custom * TimeLimiterConfig configuration. @@ -154,6 +214,23 @@ static TimeLimiterRegistry of(Map configs, */ TimeLimiter timeLimiter(String name, Supplier timeLimiterConfigSupplier); + /** + * Returns a managed {@link TimeLimiter} or creates a new one with a custom TimeLimiter + * configuration. + *

+ * The {@code tags} passed will be appended to the tags already configured for the registry. + * When tags (keys) of the two collide the tags passed with this method will override the tags + * of the registry. + * + * @param name the name of the TimeLimiter + * @param timeLimiterConfigSupplier a supplier of a custom TimeLimiter configuration + * @param tags tags added to the TimeLimiter + * @return The {@link TimeLimiter} + */ + TimeLimiter timeLimiter(String name, + Supplier timeLimiterConfigSupplier, + io.vavr.collection.Map tags); + /** * Returns a managed {@link TimeLimiter} or creates a new one. * The configuration must have been added upfront via {@link #addConfiguration(String, Object)}. @@ -164,4 +241,20 @@ static TimeLimiterRegistry of(Map configs, */ TimeLimiter timeLimiter(String name, String configName); + /** + * Returns a managed {@link TimeLimiter} or creates a new one. + * The configuration must have been added upfront via {@link #addConfiguration(String, Object)}. + *

+ * The {@code tags} passed will be appended to the tags already configured for the registry. + * When tags (keys) of the two collide the tags passed with this method will override the tags + * of the registry. + * + * @param name the name of the TimeLimiter + * @param configName the name of the shared configuration + * @param tags tags added to the TimeLimiter + * @return The {@link TimeLimiter} + */ + TimeLimiter timeLimiter(String name, String configName, + io.vavr.collection.Map tags); + } diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/InMemoryTimeLimiterRegistry.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/InMemoryTimeLimiterRegistry.java index dbf530d1c5..659710c2e9 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/InMemoryTimeLimiterRegistry.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/InMemoryTimeLimiterRegistry.java @@ -25,6 +25,7 @@ import io.github.resilience4j.timelimiter.TimeLimiterConfig; import io.github.resilience4j.timelimiter.TimeLimiterRegistry; import io.vavr.collection.Array; +import io.vavr.collection.HashMap; import io.vavr.collection.Seq; import java.util.List; @@ -42,7 +43,11 @@ public class InMemoryTimeLimiterRegistry extends * The constructor with default default. */ public InMemoryTimeLimiterRegistry() { - this(TimeLimiterConfig.ofDefaults()); + this(TimeLimiterConfig.ofDefaults(), HashMap.empty()); + } + + public InMemoryTimeLimiterRegistry(io.vavr.collection.Map tags) { + this(TimeLimiterConfig.ofDefaults(), tags); } public InMemoryTimeLimiterRegistry(Map configs) { @@ -50,6 +55,12 @@ public InMemoryTimeLimiterRegistry(Map configs) { this.configurations.putAll(configs); } + public InMemoryTimeLimiterRegistry(Map configs, + io.vavr.collection.Map tags) { + this(configs.getOrDefault(DEFAULT_CONFIG, TimeLimiterConfig.ofDefaults()), tags); + this.configurations.putAll(configs); + } + public InMemoryTimeLimiterRegistry(Map configs, RegistryEventConsumer registryEventConsumer) { this(configs.getOrDefault(DEFAULT_CONFIG, TimeLimiterConfig.ofDefaults()), @@ -57,6 +68,14 @@ public InMemoryTimeLimiterRegistry(Map configs, this.configurations.putAll(configs); } + public InMemoryTimeLimiterRegistry(Map configs, + RegistryEventConsumer registryEventConsumer, + io.vavr.collection.Map tags) { + this(configs.getOrDefault(DEFAULT_CONFIG, TimeLimiterConfig.ofDefaults()), registryEventConsumer, + tags); + this.configurations.putAll(configs); + } + public InMemoryTimeLimiterRegistry(Map configs, List> registryEventConsumers) { this(configs.getOrDefault(DEFAULT_CONFIG, TimeLimiterConfig.ofDefaults()), @@ -64,6 +83,14 @@ public InMemoryTimeLimiterRegistry(Map configs, this.configurations.putAll(configs); } + public InMemoryTimeLimiterRegistry(Map configs, + List> registryEventConsumers, + io.vavr.collection.Map tags) { + this(configs.getOrDefault(DEFAULT_CONFIG, TimeLimiterConfig.ofDefaults()), + registryEventConsumers, tags); + this.configurations.putAll(configs); + } + /** * The constructor with custom default config. * @@ -73,16 +100,33 @@ public InMemoryTimeLimiterRegistry(TimeLimiterConfig defaultConfig) { super(defaultConfig); } + public InMemoryTimeLimiterRegistry(TimeLimiterConfig defaultConfig, + io.vavr.collection.Map tags) { + super(defaultConfig, tags); + } + public InMemoryTimeLimiterRegistry(TimeLimiterConfig defaultConfig, RegistryEventConsumer registryEventConsumer) { super(defaultConfig, registryEventConsumer); } + public InMemoryTimeLimiterRegistry(TimeLimiterConfig defaultConfig, + RegistryEventConsumer registryEventConsumer, + io.vavr.collection.Map tags) { + super(defaultConfig, registryEventConsumer, tags); + } + public InMemoryTimeLimiterRegistry(TimeLimiterConfig defaultConfig, List> registryEventConsumers) { super(defaultConfig, registryEventConsumers); } + public InMemoryTimeLimiterRegistry(TimeLimiterConfig defaultConfig, + List> registryEventConsumers, + io.vavr.collection.Map tags) { + super(defaultConfig, registryEventConsumers, tags); + } + /** * {@inheritDoc} */ @@ -96,7 +140,13 @@ public Seq getAllTimeLimiters() { */ @Override public TimeLimiter timeLimiter(final String name) { - return timeLimiter(name, getDefaultConfig()); + return timeLimiter(name, getDefaultConfig(), HashMap.empty()); + } + + @Override + public TimeLimiter timeLimiter(String name, + io.vavr.collection.Map tags) { + return timeLimiter(name, getDefaultConfig(), tags); } /** @@ -104,8 +154,15 @@ public TimeLimiter timeLimiter(final String name) { */ @Override public TimeLimiter timeLimiter(final String name, final TimeLimiterConfig config) { - return computeIfAbsent(name, () -> new TimeLimiterImpl(name, - Objects.requireNonNull(config, CONFIG_MUST_NOT_BE_NULL))); + return timeLimiter(name, config, HashMap.empty()); + } + + @Override + public TimeLimiter timeLimiter(String name, + TimeLimiterConfig timeLimiterConfig, + io.vavr.collection.Map tags) { + return computeIfAbsent(name, () -> TimeLimiter.of(name, + Objects.requireNonNull(timeLimiterConfig, CONFIG_MUST_NOT_BE_NULL), getAllTags(tags))); } /** @@ -114,12 +171,16 @@ public TimeLimiter timeLimiter(final String name, final TimeLimiterConfig config @Override public TimeLimiter timeLimiter(final String name, final Supplier timeLimiterConfigSupplier) { - return computeIfAbsent(name, () -> { - TimeLimiterConfig config = Objects - .requireNonNull(timeLimiterConfigSupplier, SUPPLIER_MUST_NOT_BE_NULL).get(); - return new TimeLimiterImpl(name, - Objects.requireNonNull(config, CONFIG_MUST_NOT_BE_NULL)); - }); + return timeLimiter(name, timeLimiterConfigSupplier, HashMap.empty()); + } + + @Override + public TimeLimiter timeLimiter(String name, + Supplier timeLimiterConfigSupplier, + io.vavr.collection.Map tags) { + return computeIfAbsent(name, () -> TimeLimiter.of(name, Objects.requireNonNull( + Objects.requireNonNull(timeLimiterConfigSupplier, SUPPLIER_MUST_NOT_BE_NULL).get(), + CONFIG_MUST_NOT_BE_NULL), getAllTags(tags))); } /** @@ -127,10 +188,14 @@ public TimeLimiter timeLimiter(final String name, */ @Override public TimeLimiter timeLimiter(String name, String configName) { - return computeIfAbsent(name, () -> { - TimeLimiterConfig config = getConfiguration(configName) - .orElseThrow(() -> new ConfigurationNotFoundException(configName)); - return TimeLimiter.of(name, config); - }); + return timeLimiter(name, configName, HashMap.empty()); + } + + @Override + public TimeLimiter timeLimiter(String name, String configName, + io.vavr.collection.Map tags) { + TimeLimiterConfig config = getConfiguration(configName) + .orElseThrow(() -> new ConfigurationNotFoundException(configName)); + return timeLimiter(name, config, tags); } } diff --git a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java index cdac36d0cd..c0ac8a3010 100644 --- a/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java +++ b/resilience4j-timelimiter/src/main/java/io/github/resilience4j/timelimiter/internal/TimeLimiterImpl.java @@ -6,9 +6,12 @@ import io.github.resilience4j.timelimiter.event.TimeLimiterOnErrorEvent; import io.github.resilience4j.timelimiter.event.TimeLimiterOnSuccessEvent; import io.github.resilience4j.timelimiter.event.TimeLimiterOnTimeoutEvent; +import io.vavr.collection.HashMap; +import io.vavr.collection.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Objects; import java.util.concurrent.*; import java.util.function.Supplier; @@ -17,15 +20,24 @@ public class TimeLimiterImpl implements TimeLimiter { private static final Logger LOG = LoggerFactory.getLogger(TimeLimiterImpl.class); private final String name; + private final Map tags; private final TimeLimiterConfig timeLimiterConfig; private final TimeLimiterEventProcessor eventProcessor; public TimeLimiterImpl(String name, TimeLimiterConfig timeLimiterConfig) { + this(name, timeLimiterConfig, HashMap.empty()); + + } + + public TimeLimiterImpl(String name, TimeLimiterConfig timeLimiterConfig, + io.vavr.collection.Map tags) { this.name = name; + this.tags = Objects.requireNonNull(tags, "Tags must not be null"); this.timeLimiterConfig = timeLimiterConfig; this.eventProcessor = new TimeLimiterEventProcessor(); } + @Override public > Callable decorateFutureSupplier(Supplier futureSupplier) { return () -> { @@ -101,6 +113,11 @@ public String getName() { return name; } + @Override + public Map getTags() { + return tags; + } + @Override public TimeLimiterConfig getTimeLimiterConfig() { return timeLimiterConfig; diff --git a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterRegistryTest.java b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterRegistryTest.java index 475c328a73..53c18aa555 100644 --- a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterRegistryTest.java +++ b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterRegistryTest.java @@ -7,6 +7,7 @@ import io.github.resilience4j.core.registry.EntryRemovedEvent; import io.github.resilience4j.core.registry.EntryReplacedEvent; import io.github.resilience4j.core.registry.RegistryEventConsumer; +import io.vavr.Tuple; import org.junit.Test; import java.time.Duration; @@ -40,6 +41,84 @@ public void testCreateWithCustomConfig() { assertThat(timeLimiterConfig).isSameAs(config); } + @Test + public void shouldInitRegistryTags() { + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); + Map timeLimiterConfigs = Collections + .singletonMap("default", timeLimiterConfig); + TimeLimiterRegistry registry = TimeLimiterRegistry.of(timeLimiterConfigs, new NoOpTimeLimiterEventConsumer(),io.vavr.collection.HashMap.of("Tag1Key","Tag1Value")); + assertThat(registry.getTags()).isNotEmpty(); + assertThat(registry.getTags()).containsOnly(Tuple.of("Tag1Key","Tag1Value")); + } + + @Test + public void noTagsByDefault() { + TimeLimiter TimeLimiter = TimeLimiterRegistry.ofDefaults() + .timeLimiter("testName"); + assertThat(TimeLimiter.getTags()).hasSize(0); + } + + @Test + public void tagsOfRegistryAddedToInstance() { + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); + Map timeLimiterConfigs = Collections + .singletonMap("default", timeLimiterConfig); + io.vavr.collection.Map timeLimiterTags = io.vavr.collection.HashMap + .of("key1", "value1", "key2", "value2"); + TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry + .of(timeLimiterConfigs, timeLimiterTags); + TimeLimiter TimeLimiter = timeLimiterRegistry.timeLimiter("testName"); + + assertThat(TimeLimiter.getTags()).containsOnlyElementsOf(timeLimiterTags); + } + + @Test + public void tagsAddedToInstance() { + TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.ofDefaults(); + io.vavr.collection.Map timeLimiterTags = io.vavr.collection.HashMap + .of("key1", "value1", "key2", "value2"); + TimeLimiter TimeLimiter = timeLimiterRegistry + .timeLimiter("testName", timeLimiterTags); + + assertThat(TimeLimiter.getTags()).containsOnlyElementsOf(timeLimiterTags); + } + + @Test + public void tagsOfTimeLimitersShouldNotBeMixed() { + TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.ofDefaults(); + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); + io.vavr.collection.Map timeLimiterTags = io.vavr.collection.HashMap + .of("key1", "value1", "key2", "value2"); + TimeLimiter TimeLimiter = timeLimiterRegistry + .timeLimiter("testName", timeLimiterConfig, timeLimiterTags); + io.vavr.collection.Map timeLimiterTags2 = io.vavr.collection.HashMap + .of("key3", "value3", "key4", "value4"); + TimeLimiter TimeLimiter2 = timeLimiterRegistry + .timeLimiter("otherTestName", timeLimiterConfig, timeLimiterTags2); + + assertThat(TimeLimiter.getTags()).containsOnlyElementsOf(timeLimiterTags); + assertThat(TimeLimiter2.getTags()).containsOnlyElementsOf(timeLimiterTags2); + } + + @Test + public void tagsOfInstanceTagsShouldOverrideRegistryTags() { + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); + Map timeLimiterConfigs = Collections + .singletonMap("default", timeLimiterConfig); + io.vavr.collection.Map timeLimiterTags = io.vavr.collection.HashMap + .of("key1", "value1", "key2", "value2"); + io.vavr.collection.Map instanceTags = io.vavr.collection.HashMap + .of("key1", "value3", "key4", "value4"); + TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry + .of(timeLimiterConfigs, timeLimiterTags); + TimeLimiter timeLimiter = timeLimiterRegistry + .timeLimiter("testName", timeLimiterConfig, instanceTags); + + io.vavr.collection.Map expectedTags = io.vavr.collection.HashMap + .of("key1", "value3", "key2", "value2", "key4", "value4"); + assertThat(timeLimiter.getTags()).containsOnlyElementsOf(expectedTags); + } + @Test public void testCreateWithConfigurationMap() { Map configs = new HashMap<>();