-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Description
The HystrixContextScheduler (specifically its internal ThreadPoolWorker) uses ThreadPoolExecutor.submit() to schedule tasks. While it captures the returned Future object, it wraps it in a FutureCompleterWithConfigurableInterrupt which only uses the Future for cancellation.
Crucially, future.get() is never called anywhere in the implementation.
According to the ExecutorService contract, exceptions thrown by tasks submitted via submit() are captured and stored within the Future. If get() is never called, these exceptions are never thrown to the caller or the uncaught exception handler. This results in a Silent Failure (Exception Not Caught - ENC), where initialization errors or runtime exceptions within the scheduler logic simply disappear without any logs.
Affected Component
- Class:
com.netflix.hystrix.strategy.concurrency.HystrixContextScheduler - Inner Class:
ThreadPoolWorker - Method:
schedule(Action0 action)
Root Cause Analysis
In HystrixContextScheduler.java:
// Inside ThreadPoolWorker.schedule(Action0 action)
ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor();
// 1. Task is submitted, returning a Future. Exceptions are now encapsulated in 'f'.
FutureTask<?> f = (FutureTask<?>) executor.submit(sa);
// 2. 'f' is passed to a helper, but ONLY for cancellation purposes.
sa.add(new FutureCompleterWithConfigurableInterrupt(f, shouldInterruptThread, executor));
return sa;
And in the helper class:
private static class FutureCompleterWithConfigurableInterrupt implements Subscription {
// ...
@Override
public void unsubscribe() {
executor.remove(f);
// 3. The Future is used here only to cancel the task.
// MISSING: No logic to check f.get() for exceptions if the task completed abnormally.
if (shouldInterruptThread.call()) {
f.cancel(true);
} else {
f.cancel(false);
}
}
}
Expected Behavior When a scheduled action fails, the exception should not be swallowed. Since submit() is used, the implementation should ensure that exceptions are either:
1. Propagated to a registered error handler (e.g., via HystrixPlugins or RxJava plugins).
2. Or, execute() should be used instead of submit() if the Future is only needed for cancellation (though execute does not return a Future, making standard cancellation harder, implying submit + get handling is the correct path).
Actual Behavior Any unchecked exception thrown during the execution of the ScheduledAction (that isn't handled internally by RxJava's SafeSubscriber logic) is caught by the FutureTask, stored, and effectively ignored because get() is never invoked.