use of com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable in project Hystrix by Netflix.
the class RollingThreadPoolMaxConcurrencyStreamTest method testConcurrencyStreamProperlyFiltersOutSemaphoreRejections.
@Test
public void testConcurrencyStreamProperlyFiltersOutSemaphoreRejections() throws InterruptedException {
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("ThreadPool-Concurrency-I");
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("ThreadPool-Concurrency-I");
HystrixCommandKey key = HystrixCommandKey.Factory.asKey("RollingConcurrency-I");
stream = RollingThreadPoolMaxConcurrencyStream.getInstance(threadPoolKey, 10, 100);
stream.startCachingStreamValuesIfUnstarted();
final CountDownLatch latch = new CountDownLatch(1);
stream.observe().take(10).subscribe(getSubscriber(latch));
//10 commands executed concurrently on different caller threads should saturate semaphore
//once these are in-flight, execute 10 more concurrently on new caller threads.
//since these are semaphore-rejected, the max concurrency should be 10
List<Command> saturators = new ArrayList<Command>();
for (int i = 0; i < 10; i++) {
saturators.add(Command.from(groupKey, key, HystrixEventType.SUCCESS, 400, HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE));
}
final List<Command> rejected = new ArrayList<Command>();
for (int i = 0; i < 10; i++) {
rejected.add(Command.from(groupKey, key, HystrixEventType.SUCCESS, 100, HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE));
}
for (final Command saturatingCmd : saturators) {
threadPool.submit(new HystrixContextRunnable(new Runnable() {
@Override
public void run() {
saturatingCmd.observe();
}
}));
}
Thread.sleep(30);
for (final Command rejectedCmd : rejected) {
threadPool.submit(new HystrixContextRunnable(new Runnable() {
@Override
public void run() {
rejectedCmd.observe();
}
}));
}
assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
System.out.println("ReqLog : " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString());
for (Command rejectedCmd : rejected) {
assertTrue(rejectedCmd.isResponseSemaphoreRejected() || rejectedCmd.isResponseShortCircuited());
}
//should be 0 since all are executed in a semaphore
assertEquals(0, stream.getLatestRollingMax());
}
use of com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable in project Hystrix by Netflix.
the class HystrixCollapserTest method testRequestVariableLifecycle2.
@Test
public void testRequestVariableLifecycle2() throws Exception {
final HystrixRequestContext reqContext = HystrixRequestContext.initializeContext();
final TestCollapserTimer timer = new TestCollapserTimer();
final ConcurrentLinkedQueue<Future<String>> responses = new ConcurrentLinkedQueue<Future<String>>();
ConcurrentLinkedQueue<Thread> threads = new ConcurrentLinkedQueue<Thread>();
// kick off work (simulating a single request with multiple threads)
for (int t = 0; t < 5; t++) {
final int outerLoop = t;
Thread th = new Thread(new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
int uniqueInt = (outerLoop * 100) + i;
responses.add(new TestRequestCollapser(timer, uniqueInt).queue());
}
}
}));
threads.add(th);
th.start();
}
for (Thread th : threads) {
// wait for each thread to finish
th.join();
}
// we expect 5 threads * 100 responses each
assertEquals(500, responses.size());
for (Future<String> f : responses) {
// they should not be done yet because the counter hasn't incremented
assertFalse(f.isDone());
}
timer.incrementTime(5);
HystrixCollapser<List<String>, String, String> collapser1 = new TestRequestCollapser(timer, 2);
Future<String> response2 = collapser1.queue();
timer.incrementTime(8);
// should execute here
Future<String> response3 = new TestRequestCollapser(timer, 3).queue();
timer.incrementTime(6);
Future<String> response4 = new TestRequestCollapser(timer, 4).queue();
timer.incrementTime(8);
// should execute here
Future<String> response5 = new TestRequestCollapser(timer, 5).queue();
timer.incrementTime(10);
// wait for all tasks to complete
for (Future<String> f : responses) {
f.get(1000, TimeUnit.MILLISECONDS);
}
assertEquals("2", response2.get(1000, TimeUnit.MILLISECONDS));
assertEquals("3", response3.get(1000, TimeUnit.MILLISECONDS));
assertEquals("4", response4.get(1000, TimeUnit.MILLISECONDS));
assertEquals("5", response5.get(1000, TimeUnit.MILLISECONDS));
// each task should have been executed 3 times
for (TestCollapserTimer.ATask t : timer.tasks) {
assertEquals(3, t.task.count.get());
}
Iterator<HystrixInvokableInfo<?>> cmdIterator = HystrixRequestLog.getCurrentRequest().getAllExecutedCommands().iterator();
assertEquals(500, cmdIterator.next().getNumberCollapsed());
assertEquals(2, cmdIterator.next().getNumberCollapsed());
assertEquals(1, cmdIterator.next().getNumberCollapsed());
HystrixRequestVariableHolder<RequestCollapser<?, ?, ?>> rv = RequestCollapserFactory.getRequestVariable(new TestRequestCollapser(timer, 1).getCollapserKey().name());
reqContext.close();
assertNotNull(rv);
// they should have all been removed as part of ThreadContext.remove()
assertEquals(0, timer.tasks.size());
}
use of com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable in project Hystrix by Netflix.
the class HystrixCommandTest method testRejectedExecutionSemaphoreWithFallbackViaExecute.
@Test
public void testRejectedExecutionSemaphoreWithFallbackViaExecute() throws Exception {
final TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
final ArrayBlockingQueue<Boolean> results = new ArrayBlockingQueue<Boolean>(2);
final AtomicBoolean exceptionReceived = new AtomicBoolean();
final TestSemaphoreCommandWithFallback command1 = new TestSemaphoreCommandWithFallback(circuitBreaker, 1, 200, false);
Runnable r1 = new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
@Override
public void run() {
try {
results.add(command1.execute());
} catch (Exception e) {
e.printStackTrace();
exceptionReceived.set(true);
}
}
});
final TestSemaphoreCommandWithFallback command2 = new TestSemaphoreCommandWithFallback(circuitBreaker, 1, 200, false);
Runnable r2 = new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
@Override
public void run() {
try {
results.add(command2.execute());
} catch (Exception e) {
e.printStackTrace();
exceptionReceived.set(true);
}
}
});
// 2 threads, the second should be rejected by the semaphore and return fallback
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
// make sure that t2 gets a chance to run before queuing the next one
Thread.sleep(50);
t2.start();
t1.join();
t2.join();
if (exceptionReceived.get()) {
fail("We should have received a fallback response");
}
// both threads should have returned values
assertEquals(2, results.size());
// should contain both a true and false result
assertTrue(results.contains(Boolean.TRUE));
assertTrue(results.contains(Boolean.FALSE));
assertCommandExecutionEvents(command1, HystrixEventType.SUCCESS);
assertCommandExecutionEvents(command2, HystrixEventType.SEMAPHORE_REJECTED, HystrixEventType.FALLBACK_SUCCESS);
assertEquals(0, circuitBreaker.metrics.getCurrentConcurrentExecutionCount());
assertSaneHystrixRequestLog(2);
}
use of com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable in project Hystrix by Netflix.
the class HystrixCommandTest method testExecutionSemaphoreWithQueue.
@Test
public void testExecutionSemaphoreWithQueue() throws Exception {
final TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
// single thread should work
TestSemaphoreCommand command1 = new TestSemaphoreCommand(circuitBreaker, 1, 200, TestSemaphoreCommand.RESULT_SUCCESS, TestSemaphoreCommand.FALLBACK_NOT_IMPLEMENTED);
boolean result = command1.queue().get();
assertTrue(result);
final AtomicBoolean exceptionReceived = new AtomicBoolean();
final TryableSemaphore semaphore = new TryableSemaphoreActual(HystrixProperty.Factory.asProperty(1));
final TestSemaphoreCommand command2 = new TestSemaphoreCommand(circuitBreaker, semaphore, 200, TestSemaphoreCommand.RESULT_SUCCESS, TestSemaphoreCommand.FALLBACK_NOT_IMPLEMENTED);
Runnable r2 = new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
@Override
public void run() {
try {
command2.queue().get();
} catch (Exception e) {
e.printStackTrace();
exceptionReceived.set(true);
}
}
});
final TestSemaphoreCommand command3 = new TestSemaphoreCommand(circuitBreaker, semaphore, 200, TestSemaphoreCommand.RESULT_SUCCESS, TestSemaphoreCommand.FALLBACK_NOT_IMPLEMENTED);
Runnable r3 = new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
@Override
public void run() {
try {
command3.queue().get();
} catch (Exception e) {
e.printStackTrace();
exceptionReceived.set(true);
}
}
});
// 2 threads, the second should be rejected by the semaphore
Thread t2 = new Thread(r2);
Thread t3 = new Thread(r3);
t2.start();
// make sure that t2 gets a chance to run before queuing the next one
Thread.sleep(50);
t3.start();
t2.join();
t3.join();
if (!exceptionReceived.get()) {
fail("We expected an exception on the 2nd get");
}
assertCommandExecutionEvents(command1, HystrixEventType.SUCCESS);
assertCommandExecutionEvents(command2, HystrixEventType.SUCCESS);
assertCommandExecutionEvents(command3, HystrixEventType.SEMAPHORE_REJECTED, HystrixEventType.FALLBACK_MISSING);
assertEquals(0, circuitBreaker.metrics.getCurrentConcurrentExecutionCount());
assertSaneHystrixRequestLog(3);
}
use of com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable in project Hystrix by Netflix.
the class HystrixCommandTest method testSemaphorePermitsInUse.
/**
* Tests that semaphores are counted separately for commands with unique keys
*/
@Test
public void testSemaphorePermitsInUse() throws Exception {
final TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
// this semaphore will be shared across multiple command instances
final TryableSemaphoreActual sharedSemaphore = new TryableSemaphoreActual(HystrixProperty.Factory.asProperty(3));
// used to wait until all commands have started
final CountDownLatch startLatch = new CountDownLatch((sharedSemaphore.numberOfPermits.get() * 2) + 1);
// used to signal that all command can finish
final CountDownLatch sharedLatch = new CountDownLatch(1);
// tracks failures to obtain semaphores
final AtomicInteger failureCount = new AtomicInteger();
final Runnable sharedSemaphoreRunnable = new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
public void run() {
try {
new LatchedSemaphoreCommand("Command-Shared", circuitBreaker, sharedSemaphore, startLatch, sharedLatch).execute();
} catch (Exception e) {
startLatch.countDown();
e.printStackTrace();
failureCount.incrementAndGet();
}
}
});
// creates group of threads each using command sharing a single semaphore
// I create extra threads and commands so that I can verify that some of them fail to obtain a semaphore
final int sharedThreadCount = sharedSemaphore.numberOfPermits.get() * 2;
final Thread[] sharedSemaphoreThreads = new Thread[sharedThreadCount];
for (int i = 0; i < sharedThreadCount; i++) {
sharedSemaphoreThreads[i] = new Thread(sharedSemaphoreRunnable);
}
// creates thread using isolated semaphore
final TryableSemaphoreActual isolatedSemaphore = new TryableSemaphoreActual(HystrixProperty.Factory.asProperty(1));
final CountDownLatch isolatedLatch = new CountDownLatch(1);
final Thread isolatedThread = new Thread(new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
public void run() {
try {
new LatchedSemaphoreCommand("Command-Isolated", circuitBreaker, isolatedSemaphore, startLatch, isolatedLatch).execute();
} catch (Exception e) {
startLatch.countDown();
e.printStackTrace();
failureCount.incrementAndGet();
}
}
}));
// verifies no permits in use before starting threads
assertEquals("before threads start, shared semaphore should be unused", 0, sharedSemaphore.getNumberOfPermitsUsed());
assertEquals("before threads start, isolated semaphore should be unused", 0, isolatedSemaphore.getNumberOfPermitsUsed());
for (int i = 0; i < sharedThreadCount; i++) {
sharedSemaphoreThreads[i].start();
}
isolatedThread.start();
// waits until all commands have started
startLatch.await(1000, TimeUnit.MILLISECONDS);
// verifies that all semaphores are in use
assertEquals("immediately after command start, all shared semaphores should be in-use", sharedSemaphore.numberOfPermits.get().longValue(), sharedSemaphore.getNumberOfPermitsUsed());
assertEquals("immediately after command start, isolated semaphore should be in-use", isolatedSemaphore.numberOfPermits.get().longValue(), isolatedSemaphore.getNumberOfPermitsUsed());
// signals commands to finish
sharedLatch.countDown();
isolatedLatch.countDown();
for (int i = 0; i < sharedThreadCount; i++) {
sharedSemaphoreThreads[i].join();
}
isolatedThread.join();
// verifies no permits in use after finishing threads
System.out.println("REQLOG : " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString());
assertEquals("after all threads have finished, no shared semaphores should be in-use", 0, sharedSemaphore.getNumberOfPermitsUsed());
assertEquals("after all threads have finished, isolated semaphore not in-use", 0, isolatedSemaphore.getNumberOfPermitsUsed());
// verifies that some executions failed
assertEquals("expected some of shared semaphore commands to get rejected", sharedSemaphore.numberOfPermits.get().longValue(), failureCount.get());
assertEquals(0, circuitBreaker.metrics.getCurrentConcurrentExecutionCount());
}
Aggregations