/**
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Test;
import com.netflix.config.ConfigurationManager;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.HystrixCommandProperties.Setter;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
public class HystrixCommandPropertiesTest {
/**
* Utility method for creating baseline properties for unit tests.
*/
/* package */static HystrixCommandProperties.Setter getUnitTestPropertiesSetter() {
return new HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(1000)// when an execution will be timed out
.withExecutionTimeoutEnabled(true)
.withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD) // we want thread execution by default in tests
.withExecutionIsolationThreadInterruptOnTimeout(true)
.withExecutionIsolationThreadInterruptOnFutureCancel(true)
.withCircuitBreakerForceOpen(false) // we don't want short-circuiting by default
.withCircuitBreakerErrorThresholdPercentage(40) // % of 'marks' that must be failed to trip the circuit
.withMetricsRollingStatisticalWindowInMilliseconds(5000)// milliseconds back that will be tracked
.withMetricsRollingStatisticalWindowBuckets(5) // buckets
.withCircuitBreakerRequestVolumeThreshold(0) // in testing we will not have a threshold unless we're specifically testing that feature
.withCircuitBreakerSleepWindowInMilliseconds(5000000) // milliseconds after tripping circuit before allowing retry (by default set VERY long as we want it to effectively never allow a singleTest for most unit tests)
.withCircuitBreakerEnabled(true)
.withRequestLogEnabled(true)
.withExecutionIsolationSemaphoreMaxConcurrentRequests(20)
.withFallbackIsolationSemaphoreMaxConcurrentRequests(10)
.withFallbackEnabled(true)
.withCircuitBreakerForceClosed(false)
.withMetricsRollingPercentileEnabled(true)
.withRequestCacheEnabled(true)
.withMetricsRollingPercentileWindowInMilliseconds(60000)
.withMetricsRollingPercentileWindowBuckets(12)
.withMetricsRollingPercentileBucketSize(1000)
.withMetricsHealthSnapshotIntervalInMilliseconds(100);
}
/**
* Return a static representation of the properties with values from the Builder so that UnitTests can create properties that are not affected by the actual implementations which pick up their
* values dynamically.
*
* @param builder command properties builder
* @return HystrixCommandProperties
*/
/* package */static HystrixCommandProperties asMock(final Setter builder) {
return new HystrixCommandProperties(TestKey.TEST) {
@Override
public HystrixProperty<Boolean> circuitBreakerEnabled() {
return HystrixProperty.Factory.asProperty(builder.getCircuitBreakerEnabled());
}
@Override
public HystrixProperty<Integer> circuitBreakerErrorThresholdPercentage() {
return HystrixProperty.Factory.asProperty(builder.getCircuitBreakerErrorThresholdPercentage());
}
@Override
public HystrixProperty<Boolean> circuitBreakerForceClosed() {
return HystrixProperty.Factory.asProperty(builder.getCircuitBreakerForceClosed());
}
@Override
public HystrixProperty<Boolean> circuitBreakerForceOpen() {
return HystrixProperty.Factory.asProperty(builder.getCircuitBreakerForceOpen());
}
@Override
public HystrixProperty<Integer> circuitBreakerRequestVolumeThreshold() {
return HystrixProperty.Factory.asProperty(builder.getCircuitBreakerRequestVolumeThreshold());
}
@Override
public HystrixProperty<Integer> circuitBreakerSleepWindowInMilliseconds() {
return HystrixProperty.Factory.asProperty(builder.getCircuitBreakerSleepWindowInMilliseconds());
}
@Override
public HystrixProperty<Integer> executionIsolationSemaphoreMaxConcurrentRequests() {
return HystrixProperty.Factory.asProperty(builder.getExecutionIsolationSemaphoreMaxConcurrentRequests());
}
@Override
public HystrixProperty<ExecutionIsolationStrategy> executionIsolationStrategy() {
return HystrixProperty.Factory.asProperty(builder.getExecutionIsolationStrategy());
}
@Override
public HystrixProperty<Boolean> executionIsolationThreadInterruptOnTimeout() {
return HystrixProperty.Factory.asProperty(builder.getExecutionIsolationThreadInterruptOnTimeout());
}
@Override
public HystrixProperty<Boolean> executionIsolationThreadInterruptOnFutureCancel() {
return HystrixProperty.Factory.asProperty(builder.getExecutionIsolationThreadInterruptOnFutureCancel());
}
@Override
public HystrixProperty<String> executionIsolationThreadPoolKeyOverride() {
return HystrixProperty.Factory.nullProperty();
}
@Override
public HystrixProperty<Integer> executionTimeoutInMilliseconds() {
return HystrixProperty.Factory.asProperty(builder.getExecutionTimeoutInMilliseconds());
}
@Override
public HystrixProperty<Boolean> executionTimeoutEnabled() {
return HystrixProperty.Factory.asProperty(builder.getExecutionTimeoutEnabled());
}
@Override
public HystrixProperty<Integer> fallbackIsolationSemaphoreMaxConcurrentRequests() {
return HystrixProperty.Factory.asProperty(builder.getFallbackIsolationSemaphoreMaxConcurrentRequests());
}
@Override
public HystrixProperty<Boolean> fallbackEnabled() {
return HystrixProperty.Factory.asProperty(builder.getFallbackEnabled());
}
@Override
public HystrixProperty<Integer> metricsHealthSnapshotIntervalInMilliseconds() {
return HystrixProperty.Factory.asProperty(builder.getMetricsHealthSnapshotIntervalInMilliseconds());
}
@Override
public HystrixProperty<Integer> metricsRollingPercentileBucketSize() {
return HystrixProperty.Factory.asProperty(builder.getMetricsRollingPercentileBucketSize());
}
@Override
public HystrixProperty<Boolean> metricsRollingPercentileEnabled() {
return HystrixProperty.Factory.asProperty(builder.getMetricsRollingPercentileEnabled());
}
@Override
public HystrixProperty<Integer> metricsRollingPercentileWindow() {
return HystrixProperty.Factory.asProperty(builder.getMetricsRollingPercentileWindowInMilliseconds());
}
@Override
public HystrixProperty<Integer> metricsRollingPercentileWindowBuckets() {
return HystrixProperty.Factory.asProperty(builder.getMetricsRollingPercentileWindowBuckets());
}
@Override
public HystrixProperty<Integer> metricsRollingStatisticalWindowInMilliseconds() {
return HystrixProperty.Factory.asProperty(builder.getMetricsRollingStatisticalWindowInMilliseconds());
}
@Override
public HystrixProperty<Integer> metricsRollingStatisticalWindowBuckets() {
return HystrixProperty.Factory.asProperty(builder.getMetricsRollingStatisticalWindowBuckets());
}
@Override
public HystrixProperty<Boolean> requestCacheEnabled() {
return HystrixProperty.Factory.asProperty(builder.getRequestCacheEnabled());
}
@Override
public HystrixProperty<Boolean> requestLogEnabled() {
return HystrixProperty.Factory.asProperty(builder.getRequestLogEnabled());
}
};
}
// NOTE: We use "unitTestPrefix" as a prefix so we can't end up pulling in external properties that change unit test behavior
public enum TestKey implements HystrixCommandKey {
TEST
}
private static class TestPropertiesCommand extends HystrixCommandProperties {
protected TestPropertiesCommand(HystrixCommandKey key, Setter builder, String propertyPrefix) {
super(key, builder, propertyPrefix);
}
}
@After
public void cleanup() {
ConfigurationManager.getConfigInstance().clear();
}
@Test
public void testBooleanBuilderOverride1() {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(true), "unitTestPrefix");
// the builder override should take precedence over the default
assertEquals(true, properties.circuitBreakerForceClosed().get());
}
@Test
public void testBooleanBuilderOverride2() {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(false), "unitTestPrefix");
// the builder override should take precedence over the default
assertEquals(false, properties.circuitBreakerForceClosed().get());
}
@Test
public void testBooleanCodeDefault() {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST, new HystrixCommandProperties.Setter(), "unitTestPrefix");
assertEquals(HystrixCommandProperties.default_circuitBreakerForceClosed, properties.circuitBreakerForceClosed().get());
}
@Test
public void testBooleanGlobalDynamicOverrideOfCodeDefault() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST, new HystrixCommandProperties.Setter(), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed", true);
// the global dynamic property should take precedence over the default
assertEquals(true, properties.circuitBreakerForceClosed().get());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed");
}
@Test
public void testBooleanInstanceBuilderOverrideOfGlobalDynamicOverride1() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(true), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed", false);
// the builder injected should take precedence over the global dynamic property
assertEquals(true, properties.circuitBreakerForceClosed().get());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed");
}
@Test
public void testBooleanInstanceBuilderOverrideOfGlobalDynamicOverride2() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(false), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed", true);
// the builder injected should take precedence over the global dynamic property
assertEquals(false, properties.circuitBreakerForceClosed().get());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed");
}
@Test
public void testBooleanInstanceDynamicOverrideOfEverything() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(false), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed", false);
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.TEST.circuitBreaker.forceClosed", true);
// the instance specific dynamic property should take precedence over everything
assertEquals(true, properties.circuitBreakerForceClosed().get());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.circuitBreaker.forceClosed");
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.TEST.circuitBreaker.forceClosed");
}
@Test
public void testIntegerBuilderOverride() {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(5000), "unitTestPrefix");
// the builder override should take precedence over the default
assertEquals(5000, properties.metricsRollingStatisticalWindowInMilliseconds().get().intValue());
}
@Test
public void testIntegerCodeDefault() {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST, new HystrixCommandProperties.Setter(), "unitTestPrefix");
assertEquals(HystrixCommandProperties.default_metricsRollingStatisticalWindow, properties.metricsRollingStatisticalWindowInMilliseconds().get());
}
@Test
public void testIntegerGlobalDynamicOverrideOfCodeDefault() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST, new HystrixCommandProperties.Setter(), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.metrics.rollingStats.timeInMilliseconds", 1234);
// the global dynamic property should take precedence over the default
assertEquals(1234, properties.metricsRollingStatisticalWindowInMilliseconds().get().intValue());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.metrics.rollingStats.timeInMilliseconds");
}
@Test
public void testIntegerInstanceBuilderOverrideOfGlobalDynamicOverride() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(5000), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.rollingStats.timeInMilliseconds", 3456);
// the builder injected should take precedence over the global dynamic property
assertEquals(5000, properties.metricsRollingStatisticalWindowInMilliseconds().get().intValue());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.rollingStats.timeInMilliseconds");
}
@Test
public void testIntegerInstanceDynamicOverrideOfEverything() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST,
new HystrixCommandProperties.Setter().withMetricsRollingStatisticalWindowInMilliseconds(5000), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.metrics.rollingStats.timeInMilliseconds", 1234);
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.TEST.metrics.rollingStats.timeInMilliseconds", 3456);
// the instance specific dynamic property should take precedence over everything
assertEquals(3456, properties.metricsRollingStatisticalWindowInMilliseconds().get().intValue());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.metrics.rollingStats.timeInMilliseconds");
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.TEST.metrics.rollingStats.timeInMilliseconds");
}
@Test
public void testThreadPoolOnlyHasInstanceOverride() throws Exception {
HystrixCommandProperties properties = new TestPropertiesCommand(TestKey.TEST, new HystrixCommandProperties.Setter(), "unitTestPrefix");
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.default.threadPoolKeyOverride", 1234);
// it should be null
assertEquals(null, properties.executionIsolationThreadPoolKeyOverride().get());
ConfigurationManager.getConfigInstance().setProperty("unitTestPrefix.command.TEST.threadPoolKeyOverride", "testPool");
// now it should have a value
assertEquals("testPool", properties.executionIsolationThreadPoolKeyOverride().get());
// cleanup
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.default.threadPoolKeyOverride");
ConfigurationManager.getConfigInstance().clearProperty("unitTestPrefix.command.TEST.threadPoolKeyOverride");
}
}