/** * 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 org.junit.Test; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import rx.Observable; import java.util.ArrayList; import java.util.List; public class HystrixCommandTimeoutConcurrencyTesting { private final static int NUM_CONCURRENT_COMMANDS = 30; @Test public void testTimeoutRace() throws InterruptedException { final int NUM_TRIALS = 10; for (int i = 0; i < NUM_TRIALS; i++) { List<Observable<String>> observables = new ArrayList<Observable<String>>(); HystrixRequestContext context = null; try { context = HystrixRequestContext.initializeContext(); for (int j = 0; j < NUM_CONCURRENT_COMMANDS; j++) { observables.add(new TestCommand().observe()); } Observable<String> overall = Observable.merge(observables); List<String> results = overall.toList().toBlocking().first(); //wait for all commands to complete for (String s : results) { if (s == null) { System.err.println("Received NULL!"); throw new RuntimeException("Received NULL"); } } for (HystrixInvokableInfo<?> hi : HystrixRequestLog.getCurrentRequest().getAllExecutedCommands()) { if (!hi.isResponseTimedOut()) { System.err.println("Timeout not found in executed command"); throw new RuntimeException("Timeout not found in executed command"); } if (hi.isResponseTimedOut() && hi.getExecutionEvents().size() == 1) { System.err.println("Missing fallback status!"); throw new RuntimeException("Missing fallback status on timeout."); } } } catch (Exception e) { System.err.println("Error: " + e.getMessage()); e.printStackTrace(); throw new RuntimeException(e); } finally { System.out.println(HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString()); if (context != null) { context.shutdown(); } } System.out.println("*************** TRIAL " + i + " ******************"); System.out.println(); Thread.sleep(50); } Hystrix.reset(); } public static class TestCommand extends HystrixCommand<String> { protected TestCommand() { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("testTimeoutConcurrency")) .andCommandKey(HystrixCommandKey.Factory.asKey("testTimeoutConcurrencyCommand")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(3) .withCircuitBreakerEnabled(false) .withFallbackIsolationSemaphoreMaxConcurrentRequests(NUM_CONCURRENT_COMMANDS)) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() .withCoreSize(NUM_CONCURRENT_COMMANDS) .withMaxQueueSize(NUM_CONCURRENT_COMMANDS) .withQueueSizeRejectionThreshold(NUM_CONCURRENT_COMMANDS))); } @Override protected String run() throws Exception { //System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " sleeping"); Thread.sleep(500); //System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " awake and returning"); return "hello"; } @Override protected String getFallback() { return "failed"; } } }