use of rx.Notification in project Hystrix by Netflix.
the class AbstractCommand method getFallbackOrThrowException.
/**
* Execute <code>getFallback()</code> within protection of a semaphore that limits number of concurrent executions.
* <p>
* Fallback implementations shouldn't perform anything that can be blocking, but we protect against it anyways in case someone doesn't abide by the contract.
* <p>
* If something in the <code>getFallback()</code> implementation is latent (such as a network call) then the semaphore will cause us to start rejecting requests rather than allowing potentially
* all threads to pile up and block.
*
* @return K
* @throws UnsupportedOperationException
* if getFallback() not implemented
* @throws HystrixRuntimeException
* if getFallback() fails (throws an Exception) or is rejected by the semaphore
*/
private Observable<R> getFallbackOrThrowException(final AbstractCommand<R> _cmd, final HystrixEventType eventType, final FailureType failureType, final String message, final Exception originalException) {
final HystrixRequestContext requestContext = HystrixRequestContext.getContextForCurrentThread();
long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
// record the executionResult
// do this before executing fallback so it can be queried from within getFallback (see See https://github.com/Netflix/Hystrix/pull/144)
executionResult = executionResult.addEvent((int) latency, eventType);
if (shouldNotBeWrapped(originalException)) {
/* executionHook for all errors */
Exception e = wrapWithOnErrorHook(failureType, originalException);
return Observable.error(e);
} else if (isUnrecoverable(originalException)) {
logger.error("Unrecoverable Error for HystrixCommand so will throw HystrixRuntimeException and not apply fallback. ", originalException);
/* executionHook for all errors */
Exception e = wrapWithOnErrorHook(failureType, originalException);
return Observable.error(new HystrixRuntimeException(failureType, this.getClass(), getLogMessagePrefix() + " " + message + " and encountered unrecoverable error.", e, null));
} else {
if (isRecoverableError(originalException)) {
logger.warn("Recovered from java.lang.Error by serving Hystrix fallback", originalException);
}
if (properties.fallbackEnabled().get()) {
/* fallback behavior is permitted so attempt */
final Action1<Notification<? super R>> setRequestContext = new Action1<Notification<? super R>>() {
@Override
public void call(Notification<? super R> rNotification) {
setRequestContextIfNeeded(requestContext);
}
};
final Action1<R> markFallbackEmit = new Action1<R>() {
@Override
public void call(R r) {
if (shouldOutputOnNextEvents()) {
executionResult = executionResult.addEvent(HystrixEventType.FALLBACK_EMIT);
eventNotifier.markEvent(HystrixEventType.FALLBACK_EMIT, commandKey);
}
}
};
final Action0 markFallbackCompleted = new Action0() {
@Override
public void call() {
long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
eventNotifier.markEvent(HystrixEventType.FALLBACK_SUCCESS, commandKey);
executionResult = executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_SUCCESS);
}
};
final Func1<Throwable, Observable<R>> handleFallbackError = new Func1<Throwable, Observable<R>>() {
@Override
public Observable<R> call(Throwable t) {
Exception e = originalException;
Exception fe = getExceptionFromThrowable(t);
if (fe instanceof UnsupportedOperationException) {
long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
// debug only since we're throwing the exception and someone higher will do something with it
logger.debug("No fallback for HystrixCommand. ", fe);
eventNotifier.markEvent(HystrixEventType.FALLBACK_MISSING, commandKey);
executionResult = executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_MISSING);
/* executionHook for all errors */
e = wrapWithOnErrorHook(failureType, e);
return Observable.error(new HystrixRuntimeException(failureType, _cmd.getClass(), getLogMessagePrefix() + " " + message + " and no fallback available.", e, fe));
} else {
long latency = System.currentTimeMillis() - executionResult.getStartTimestamp();
logger.debug("HystrixCommand execution " + failureType.name() + " and fallback failed.", fe);
eventNotifier.markEvent(HystrixEventType.FALLBACK_FAILURE, commandKey);
executionResult = executionResult.addEvent((int) latency, HystrixEventType.FALLBACK_FAILURE);
/* executionHook for all errors */
e = wrapWithOnErrorHook(failureType, e);
return Observable.error(new HystrixRuntimeException(failureType, _cmd.getClass(), getLogMessagePrefix() + " " + message + " and fallback failed.", e, fe));
}
}
};
final TryableSemaphore fallbackSemaphore = getFallbackSemaphore();
final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false);
final Action0 singleSemaphoreRelease = new Action0() {
@Override
public void call() {
if (semaphoreHasBeenReleased.compareAndSet(false, true)) {
fallbackSemaphore.release();
}
}
};
Observable<R> fallbackExecutionChain;
// acquire a permit
if (fallbackSemaphore.tryAcquire()) {
try {
if (isFallbackUserDefined()) {
executionHook.onFallbackStart(this);
fallbackExecutionChain = getFallbackObservable();
} else {
//same logic as above without the hook invocation
fallbackExecutionChain = getFallbackObservable();
}
} catch (Throwable ex) {
//If hook or user-fallback throws, then use that as the result of the fallback lookup
fallbackExecutionChain = Observable.error(ex);
}
return fallbackExecutionChain.doOnEach(setRequestContext).lift(new FallbackHookApplication(_cmd)).lift(new DeprecatedOnFallbackHookApplication(_cmd)).doOnNext(markFallbackEmit).doOnCompleted(markFallbackCompleted).onErrorResumeNext(handleFallbackError).doOnTerminate(singleSemaphoreRelease).doOnUnsubscribe(singleSemaphoreRelease);
} else {
return handleFallbackRejectionByEmittingError();
}
} else {
return handleFallbackDisabledByEmittingError(originalException, failureType, message);
}
}
}
use of rx.Notification in project Hystrix by Netflix.
the class HystrixObservableCommandTest method testRequestContextOnRejectionWithFallback.
private RequestContextTestResults testRequestContextOnRejectionWithFallback(ExecutionIsolationStrategy isolation, final Scheduler userScheduler) {
final RequestContextTestResults results = new RequestContextTestResults();
TestHystrixObservableCommand<Boolean> command = new TestHystrixObservableCommand<Boolean>(TestHystrixObservableCommand.testPropsBuilder(new TestCircuitBreaker()).setCommandPropertiesDefaults(HystrixCommandPropertiesTest.getUnitTestPropertiesSetter().withExecutionIsolationStrategy(isolation).withExecutionIsolationSemaphoreMaxConcurrentRequests(0)).setThreadPool(new HystrixThreadPool() {
@Override
public ThreadPoolExecutor getExecutor() {
return null;
}
@Override
public void markThreadExecution() {
}
@Override
public void markThreadCompletion() {
}
@Override
public void markThreadRejection() {
}
@Override
public boolean isQueueSpaceAvailable() {
// always return false so we reject everything
return false;
}
@Override
public Scheduler getScheduler() {
return new HystrixContextScheduler(HystrixPlugins.getInstance().getConcurrencyStrategy(), this);
}
@Override
public Scheduler getScheduler(Func0<Boolean> shouldInterruptThread) {
return new HystrixContextScheduler(HystrixPlugins.getInstance().getConcurrencyStrategy(), this, shouldInterruptThread);
}
})) {
@Override
protected Observable<Boolean> construct() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
s.onError(new RuntimeException("onError"));
}
}).subscribeOn(userScheduler);
}
@Override
protected Observable<Boolean> resumeWithFallback() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
results.isContextInitialized.set(HystrixRequestContext.isCurrentThreadInitialized());
results.originThread.set(Thread.currentThread());
s.onNext(false);
s.onCompleted();
}
}).subscribeOn(userScheduler);
}
};
results.command = command;
command.toObservable().doOnEach(new Action1<Notification<? super Boolean>>() {
@Override
public void call(Notification<? super Boolean> n) {
results.isContextInitializedObserveOn.set(HystrixRequestContext.isCurrentThreadInitialized());
results.observeOnThread.set(Thread.currentThread());
}
}).subscribe(results.ts);
results.ts.awaitTerminalEvent();
System.out.println("Run => Initialized: " + results.isContextInitialized.get() + " Thread: " + results.originThread.get());
System.out.println("Observed => Initialized: " + results.isContextInitializedObserveOn.get() + " Thread: " + results.observeOnThread.get());
assertEquals(0, results.ts.getOnErrorEvents().size());
assertEquals(1, results.ts.getOnNextEvents().size());
assertEquals(false, results.ts.getOnNextEvents().get(0));
assertFalse(command.isSuccessfulExecution());
assertTrue(command.isResponseRejected());
if (isolation == ExecutionIsolationStrategy.SEMAPHORE) {
assertCommandExecutionEvents(command, HystrixEventType.SEMAPHORE_REJECTED, HystrixEventType.FALLBACK_EMIT, HystrixEventType.FALLBACK_SUCCESS);
} else {
assertCommandExecutionEvents(command, HystrixEventType.THREAD_POOL_REJECTED, HystrixEventType.FALLBACK_EMIT, HystrixEventType.FALLBACK_SUCCESS);
}
assertEquals(0, command.metrics.getCurrentConcurrentExecutionCount());
assertSaneHystrixRequestLog(1);
return results;
}
use of rx.Notification in project Hystrix by Netflix.
the class HystrixObservableCommandTest method testRequestContextOnShortCircuitedWithFallback.
private RequestContextTestResults testRequestContextOnShortCircuitedWithFallback(ExecutionIsolationStrategy isolation, final Scheduler userScheduler) {
final RequestContextTestResults results = new RequestContextTestResults();
TestHystrixObservableCommand<Boolean> command = new TestHystrixObservableCommand<Boolean>(TestHystrixObservableCommand.testPropsBuilder(new TestCircuitBreaker()).setCommandPropertiesDefaults(HystrixCommandPropertiesTest.getUnitTestPropertiesSetter().withExecutionIsolationStrategy(isolation)).setCircuitBreaker(new TestCircuitBreaker().setForceShortCircuit(true))) {
@Override
protected Observable<Boolean> construct() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
s.onError(new RuntimeException("onError"));
}
}).subscribeOn(userScheduler);
}
@Override
protected Observable<Boolean> resumeWithFallback() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
results.isContextInitialized.set(HystrixRequestContext.isCurrentThreadInitialized());
results.originThread.set(Thread.currentThread());
s.onNext(false);
s.onCompleted();
}
}).subscribeOn(userScheduler);
}
};
results.command = command;
command.toObservable().doOnEach(new Action1<Notification<? super Boolean>>() {
@Override
public void call(Notification<? super Boolean> n) {
results.isContextInitializedObserveOn.set(HystrixRequestContext.isCurrentThreadInitialized());
results.observeOnThread.set(Thread.currentThread());
}
}).subscribe(results.ts);
results.ts.awaitTerminalEvent();
System.out.println("Run => Initialized: " + results.isContextInitialized.get() + " Thread: " + results.originThread.get());
System.out.println("Observed => Initialized: " + results.isContextInitializedObserveOn.get() + " Thread: " + results.observeOnThread.get());
assertEquals(0, results.ts.getOnErrorEvents().size());
assertEquals(1, results.ts.getOnNextEvents().size());
assertEquals(false, results.ts.getOnNextEvents().get(0));
assertTrue(command.getExecutionTimeInMilliseconds() == -1);
assertFalse(command.isSuccessfulExecution());
assertTrue(command.isResponseShortCircuited());
assertCommandExecutionEvents(command, HystrixEventType.SHORT_CIRCUITED, HystrixEventType.FALLBACK_EMIT, HystrixEventType.FALLBACK_SUCCESS);
assertEquals(0, command.metrics.getCurrentConcurrentExecutionCount());
assertSaneHystrixRequestLog(1);
return results;
}
use of rx.Notification in project Hystrix by Netflix.
the class HystrixObservableCommandTest method testRequestContextOnGracefulFailure.
private RequestContextTestResults testRequestContextOnGracefulFailure(ExecutionIsolationStrategy isolation, final Scheduler userScheduler) {
final RequestContextTestResults results = new RequestContextTestResults();
final TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
TestHystrixObservableCommand<Boolean> command = new TestHystrixObservableCommand<Boolean>(TestHystrixObservableCommand.testPropsBuilder(circuitBreaker).setCommandPropertiesDefaults(HystrixCommandPropertiesTest.getUnitTestPropertiesSetter().withExecutionIsolationStrategy(isolation))) {
@Override
protected Observable<Boolean> construct() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
results.isContextInitialized.set(HystrixRequestContext.isCurrentThreadInitialized());
results.originThread.set(Thread.currentThread());
s.onError(new RuntimeException("graceful onError"));
}
}).subscribeOn(userScheduler);
}
};
results.command = command;
command.toObservable().doOnEach(new Action1<Notification<? super Boolean>>() {
@Override
public void call(Notification<? super Boolean> n) {
results.isContextInitializedObserveOn.set(HystrixRequestContext.isCurrentThreadInitialized());
results.observeOnThread.set(Thread.currentThread());
}
}).subscribe(results.ts);
results.ts.awaitTerminalEvent();
System.out.println("Run => Initialized: " + results.isContextInitialized.get() + " Thread: " + results.originThread.get());
System.out.println("Observed => Initialized: " + results.isContextInitializedObserveOn.get() + " Thread: " + results.observeOnThread.get());
assertEquals(1, results.ts.getOnErrorEvents().size());
assertTrue(command.getExecutionTimeInMilliseconds() > -1);
assertFalse(command.isSuccessfulExecution());
assertTrue(command.isFailedExecution());
assertCommandExecutionEvents(command, HystrixEventType.FAILURE, HystrixEventType.FALLBACK_MISSING);
assertEquals(0, command.metrics.getCurrentConcurrentExecutionCount());
assertSaneHystrixRequestLog(1);
return results;
}
use of rx.Notification in project Hystrix by Netflix.
the class HystrixObservableCommandTest method testRequestContextOnTimeoutWithFallback.
private RequestContextTestResults testRequestContextOnTimeoutWithFallback(ExecutionIsolationStrategy isolation, final Scheduler userScheduler) {
final RequestContextTestResults results = new RequestContextTestResults();
TestHystrixObservableCommand<Boolean> command = new TestHystrixObservableCommand<Boolean>(TestHystrixObservableCommand.testPropsBuilder(new TestCircuitBreaker()).setCommandPropertiesDefaults(HystrixCommandPropertiesTest.getUnitTestPropertiesSetter().withExecutionIsolationStrategy(isolation).withExecutionTimeoutInMilliseconds(50))) {
@Override
protected Observable<Boolean> construct() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// ignore the interrupted exception
}
}
}).subscribeOn(userScheduler);
}
@Override
protected Observable<Boolean> resumeWithFallback() {
return Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> s) {
results.isContextInitialized.set(HystrixRequestContext.isCurrentThreadInitialized());
results.originThread.set(Thread.currentThread());
s.onNext(false);
s.onCompleted();
}
}).subscribeOn(userScheduler);
}
};
results.command = command;
command.toObservable().doOnEach(new Action1<Notification<? super Boolean>>() {
@Override
public void call(Notification<? super Boolean> n) {
System.out.println("timeoutWithFallback notification: " + n + " " + Thread.currentThread());
results.isContextInitializedObserveOn.set(HystrixRequestContext.isCurrentThreadInitialized());
results.observeOnThread.set(Thread.currentThread());
}
}).subscribe(results.ts);
results.ts.awaitTerminalEvent();
System.out.println("Fallback => Initialized: " + results.isContextInitialized.get() + " Thread: " + results.originThread.get());
System.out.println("Observed => Initialized: " + results.isContextInitializedObserveOn.get() + " Thread: " + results.observeOnThread.get());
assertEquals(1, results.ts.getOnNextEvents().size());
assertEquals(false, results.ts.getOnNextEvents().get(0));
assertTrue(command.getExecutionTimeInMilliseconds() > -1);
assertFalse(command.isSuccessfulExecution());
assertTrue(command.isResponseTimedOut());
assertCommandExecutionEvents(command, HystrixEventType.TIMEOUT, HystrixEventType.FALLBACK_EMIT, HystrixEventType.FALLBACK_SUCCESS);
assertEquals(0, command.metrics.getCurrentConcurrentExecutionCount());
assertSaneHystrixRequestLog(1);
return results;
}
Aggregations