Open oldratlee opened 1 week ago
Many libraries offer methods that accept Future
parameters. When a Runnable
or Callable
throws an exception, the error is typically wrapped within a Future
(such as CompletableFuture
or ListenableFuture
in Guava). Methods like mSupplyFailFastAsync
only capture the first fail-fast exception, which means other possible exceptions might be ignored. This can lead to poor error handling since it's undesirable to overlook exceptions.
Currently, users must either handle exceptions themselves using try-catch
blocks or rely on utility methods that return multiple values—such as a Tuple
(e.g., CompletableFuture<List<T>>, List<CompletableFuture<T>>
) or a custom class providing methods to process exceptions.
For example:
Code 1: Ignoring exceptions:
CompletableFutureUtils.mSupplyFailFastAsync(suppliers).thenApply(func)...
Code 2: Handling exceptions explicitly:
FailFastResult<List<Integer>> failFastResult = CompletableFutureUtils.mSupplyFailFastAsync(suppliers);
failFastResult.getFutures().forEach(cf -> cf.exceptionally(errorHandler));
failFastResult.toCompletableFuture().thenApply(func)...
In the second approach, it might be more efficient to pass the futures
directly as parameters. Regardless, we should explicitly document that exceptions could be ignored if not handled properly.
PS: Scala well-known library Cats supports fail-fast operation to enhance for-comprehension with Future. It may also ignore exceptions. Guava methods like whenAllSucceed return FutureCombiner, which handles cancellation propagation and callbacks. It does not need to handle possible exceptions becase of the Future parameters.
report the swallowed exceptions by
M*
(includingthenM*
/MTuple*
) methods:mSupplyFailFastAsync
mSupplyAllSuccessAsync
mRunAsync
thenMApplyMostSuccessTupleAsync
thenMAcceptAnySuccessAsync
thenMRunAnyAsync
Problem Description
about these
M*
methods:CompletableFuture
can return at most one exceptionso may swallow uncaught exceptions silently.
Implementation Note
handle the
CompletableFuture
wrappers of actions in order to report the swallowed exceptions.do NOT report the exceptions that returned to caller, aka. non-swallowed exceptions. repetitive report of exceptions is redundant and unnecessary.
different concurrency strategies are different on exception report:
AllSuccess
/MostSuccess
concurrency strategies return none exception.AllFailFast
/All(Complete)
/AnySuccess
concurrency strategies return only one exception if there are the failed actions.AtomicBoolean
tracks whether the first exception or not? fairly efficient and simpleAny(Complete)
concurrency strategy return at most one exception if there are the failed actions.AtomicBoolean
tracks whether the first completed action(future) or not?