/**
* 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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.hamcrest.core.Is.is;
import com.netflix.hystrix.HystrixThreadPool.Factory;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.*;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherFactory;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherThreadPool;
import org.junit.Before;
import org.junit.Test;
import rx.Scheduler;
import rx.functions.Action0;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class HystrixThreadPoolTest {
@Before
public void setup() {
Hystrix.reset();
}
@Test
public void testShutdown() {
// other unit tests will probably have run before this so get the count
int count = Factory.threadPools.size();
HystrixThreadPool pool = Factory.getInstance(HystrixThreadPoolKey.Factory.asKey("threadPoolFactoryTest"),
HystrixThreadPoolPropertiesTest.getUnitTestPropertiesBuilder());
assertEquals(count + 1, Factory.threadPools.size());
assertFalse(pool.getExecutor().isShutdown());
Factory.shutdown();
// ensure all pools were removed from the cache
assertEquals(0, Factory.threadPools.size());
assertTrue(pool.getExecutor().isShutdown());
}
@Test
public void testShutdownWithWait() {
// other unit tests will probably have run before this so get the count
int count = Factory.threadPools.size();
HystrixThreadPool pool = Factory.getInstance(HystrixThreadPoolKey.Factory.asKey("threadPoolFactoryTest"),
HystrixThreadPoolPropertiesTest.getUnitTestPropertiesBuilder());
assertEquals(count + 1, Factory.threadPools.size());
assertFalse(pool.getExecutor().isShutdown());
Factory.shutdown(1, TimeUnit.SECONDS);
// ensure all pools were removed from the cache
assertEquals(0, Factory.threadPools.size());
assertTrue(pool.getExecutor().isShutdown());
}
private static class HystrixMetricsPublisherThreadPoolContainer implements HystrixMetricsPublisherThreadPool {
private final HystrixThreadPoolMetrics hystrixThreadPoolMetrics;
private HystrixMetricsPublisherThreadPoolContainer(HystrixThreadPoolMetrics hystrixThreadPoolMetrics) {
this.hystrixThreadPoolMetrics = hystrixThreadPoolMetrics;
}
@Override
public void initialize() {
}
public HystrixThreadPoolMetrics getHystrixThreadPoolMetrics() {
return hystrixThreadPoolMetrics;
}
}
@Test
public void ensureThreadPoolInstanceIsTheOneRegisteredWithMetricsPublisherAndThreadPoolCache() throws IllegalAccessException, NoSuchFieldException {
HystrixPlugins.getInstance().registerMetricsPublisher(new HystrixMetricsPublisher() {
@Override
public HystrixMetricsPublisherThreadPool getMetricsPublisherForThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolMetrics metrics, HystrixThreadPoolProperties properties) {
return new HystrixMetricsPublisherThreadPoolContainer(metrics);
}
});
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("threadPoolFactoryConcurrencyTest");
HystrixThreadPool poolOne = new HystrixThreadPool.HystrixThreadPoolDefault(
threadPoolKey, HystrixThreadPoolPropertiesTest.getUnitTestPropertiesBuilder());
HystrixThreadPool poolTwo = new HystrixThreadPool.HystrixThreadPoolDefault(
threadPoolKey, HystrixThreadPoolPropertiesTest.getUnitTestPropertiesBuilder());
assertThat(poolOne.getExecutor(), is(poolTwo.getExecutor())); //Now that we get the threadPool from the metrics object, this will always be equal
HystrixMetricsPublisherThreadPoolContainer hystrixMetricsPublisherThreadPool =
(HystrixMetricsPublisherThreadPoolContainer)HystrixMetricsPublisherFactory
.createOrRetrievePublisherForThreadPool(threadPoolKey, null, null);
ThreadPoolExecutor threadPoolExecutor = hystrixMetricsPublisherThreadPool.getHystrixThreadPoolMetrics().getThreadPool();
//assert that both HystrixThreadPools share the same ThreadPoolExecutor as the one in HystrixMetricsPublisherThreadPool
assertTrue(threadPoolExecutor.equals(poolOne.getExecutor()) && threadPoolExecutor.equals(poolTwo.getExecutor()));
assertFalse(threadPoolExecutor.isShutdown());
//Now the HystrixThreadPool ALWAYS has the same reference to the ThreadPoolExecutor so that it no longer matters which
//wins to be inserted into the HystrixThreadPool.Factory.threadPools cache.
}
@Test(timeout = 2500)
public void testUnsubscribeHystrixThreadPool() throws InterruptedException {
// methods are package-private so can't test it somewhere else
HystrixThreadPool pool = Factory.getInstance(HystrixThreadPoolKey.Factory.asKey("threadPoolFactoryTest"),
HystrixThreadPoolPropertiesTest.getUnitTestPropertiesBuilder());
final AtomicBoolean interrupted = new AtomicBoolean();
final CountDownLatch start = new CountDownLatch(1);
final CountDownLatch end = new CountDownLatch(1);
HystrixContextScheduler hcs = new HystrixContextScheduler(HystrixPlugins.getInstance().getConcurrencyStrategy(), pool);
Scheduler.Worker w = hcs.createWorker();
try {
w.schedule(new Action0() {
@Override
public void call() {
start.countDown();
try {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
interrupted.set(true);
}
} finally {
end.countDown();
}
}
});
start.await();
w.unsubscribe();
end.await();
Factory.shutdown();
assertTrue(interrupted.get());
} finally {
w.unsubscribe();
}
}
}