Ways to cancel downstream tasks in CompletableFuture chain when exception fired? - java

My database methods return a CompletableFuture to do the work with database asynchronously and then process the result on the main thread.
public interface IAccountDAO {
CompletableFuture<ObjectId> create(AccountEntity accountEntity);
CompletableFuture<Optional<AccountEntity>> get(String nickname);
CompletableFuture<Void> update(AccountEntity accountEntity);
CompletableFuture<Void> delete(AccountEntity accountEntity);
}
Each of the methods have following code inside (example of get(String) method):
#Override
public CompletableFuture<Optional<AccountEntity>> get(String nickname) {
return CompletableFuture.supplyAsync(() -> {
try {
// something logic
return Optional.of(...);
} catch (SomethingException ex) {
ex.printStackTrace();
throw new CompletionException(ex);
}
});
}
Handling the result:
CompletableFuture<Optional<AccountEntity>> cf = get("Test_Nickname");
// Notify end user about exception during process
cf.exceptionally(ex -> {
System.out.println("Database operation failed. Stacktrace:");
ex.printStackTrace();
return Optional.ofEmpty(); // I must return something fallback value that passes to downstream tasks.
});
// Downstream tasks that I would to cancel if exception fired
cf.thenAccept(...);
cf.thenRun(...);
cf.thenRun(...);
So, operation with database can fire exception. In this case I would to pass exception and using .exceptionally(...) or something like this notify the user called that method and STOP chain executing (cancel downstream tasks).
My question is: How I can cancel downstream tasks when CompletableFuture completed with exception?

I don't think you can cancel the downstream tasks.
All you can do is just not execute the downstream tasks incase of exception.
CompletableFuture<Optional<AccountEntity>> cf = get("Test_Nickname");
cf.whenComplete((response, exception) -> {
if (exception == null) {
// Downstream tasks that I would to like to run on this response
} else {
//Notify end user of the exception
}
});
To avoid nesting.
private Response transformResponse(GetResponse r) {
try {
return SuccessResponse();
} catch (Exception e) {
return FailedResponse();
}
}
get("Test_Nickname")
.thenApply(r -> transformResponse(r))
.thenCompose(... //update)

Related

Will the whole batch fails if Callable throws RuntimeException?

I am submit an api call to a threadpool and it has an important CustomRuntimeException to catch and throw to Caller class. However it is a batch request and I would like that the exception is processed later while other tasks are completed. MyMap is of type <Future<K>, List<MyTasks>>
myMap.values().forEach(myBatch -> {
myMap.put(threadPool.submit(() -> {
return myUtil.doSomeWork(myBatch);
}), myBatch);
});
in myUtil i am doing
class MyUtil {
K doSomeWork(List<MyTasks>) {
try {
myTasks.forEach(myCurrenTask -> doSomething(myCurrentTask));
} catch {
throw CustomException("Want this to be processed later and other tasks continue");
}
}
}
Will callable throw exception and record it and later when i do Future.get(), then it will be processed, with other tasks ahead completed successfully or it will return to caller when CustomException is thrown and the other tasks in list remain unprocessed ?

Execute a list of completable futures only if the previous one throws an exception

I have a list of completable futures and I would like to start with the first future and if there are any completion exceptions, I'd like to try the next future in the list and so on until I exhausted all of my futures. If any of the futures succeed, i'd like to stop there without using the next futures in the list. How do I accomplish this? So far, I have tried this:
for (SampleFutures future : getSampleFutures()) {
try {
return future.someMethod();
} catch (Exception e) {
log.error("An exception occurred, Will try the next future.", e);
}
}
But when I was testing this method, I see that when something fails in the future completion exception is thrown and the next set of futures are not tried.
Edit:
This is how SampleFtures look like
public class SampleFutureA implements SampleFutures {
#Override
public CompletableFuture<SomeOject> someMethod() {
return CompletableFuture
.supplyAsync(() -> someOtherMethod())
.thenApply( ()->anotherMethod())
.exceptionally(ex -> exceptionHandler(ex));
}
This is the kind of issue for which I would recommend using EA Async as it provides a kind of async/await mecanism that makes it very easy to implement this:
Initialize async when your application starts: (you can also pre-process the application, read the documentation for details)
Async.init();
then use await() as follows:
for (SampleFutures future : getSampleFutures()) {
try {
return completedFuture(await(future.someMethod()));
} catch (Exception e) {
log.error("An exception occurred, Will try the next future.", e);
}
}
throw new RuntimeException("All futures failed!");
However, if you cannot or do not want to use it, you can implement the same thing with a recursive asynchronous method:
private CompletableFuture<SomeObject> processNext(Iterator<SampleFutures> iterator) {
if (iterator.hasNext()) {
return iterator.next().someMethod()
.handle((r, e) -> {
if (e != null) {
log.error("An exception occurred, Will try the next future.", e);
return processNext(iterator);
} else {
return completedFuture(r);
}
}).thenCompose(c -> c);
}
CompletableFuture<SomeObject> allFailed = new CompletableFuture<>();
allFailed.completeExceptionally(new RuntimeException("All futures failed!"));
return allFailed;
}
that you call with
return processNext(getSampleFutures().iterator());
This method will call the first future, and only when it fails, it will recursively call itself aynchronously which will call the next ones.
We are unfortunately forced to implement it with hande() + thenCompose(c -> c) because there is no "compose" version of handle() and exceptionally(). So handle() returns a CompletableFuture<CompletableFuture<SampleObject>> and thenCompose() just unwraps it.

Use CompletableFuture to execute single or multiple Callables and avoid blocking

I execute a few callables through ThreadPoolExecutor. If thread list contains only 1 callable then I directly call call method of my CallableService. If list contains more than 1 callables then I execute all those threads in parallel via thread pool executor.
How can I achieve this with Java 8 CompletableFuture? And if future.get() is enhanced to avoid blocking, that will be a plus.
private static ThreadPoolExecutor myThreadPoolExecutor = new ThreadPoolExecutor(0, 100, 5L, TimeUnit.SECONDS, new SynchronousQueue<>());
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
List<Future<Boolean>> futureList = null;
CallableService singleService = (CallableService) threadList.get(0);
if (1 == threadList.size()) {
singleService.call();
}
else {
try {
futureList = myThreadPoolExecutor.invokeAll(threadList);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
if (null != futureList) {
for (Future<Boolean> future : futureList) {
try {
future.get();
}
catch (Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
}
There is no need for CompletableFuture, as the way you use the ExecutorService is sufficient, though, there are some aspects of the code flow which could be improved. You fetch the first element, even when not needed, and you cast it to CallableService for no reason, as you can call the method via the Callable interface already. In the other branch you are catching InterruptedException and proceeding, so the caller would never know that not all jobs have been executed. And in a straight-forward code flow, you don't need to check the list for null:
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
if(1 == threadList.size()) {
Callable<Boolean> singleService = threadList.get(0);
singleService.call();
}
else {
List<Future<Boolean>> futureList = myThreadPoolExecutor.invokeAll(threadList);
for(Future<Boolean> future : futureList) {
try {
future.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
}
You could shorten it further to
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
if(1 == threadList.size()) {
threadList.get(0).call();
}
else {
for(Future<Boolean> future : myThreadPoolExecutor.invokeAll(threadList)) {
try {
future.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
}
But that's a matter of preferred coding style. But note that it caught my eye that in the single element case, you're not performing the same exception handling.
To use CompletableFuture, we need an adapter method, as the convenience method supplyAsync requires a Supplier instead of a Callable. Using a modified variant of this answer, we get
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
if(1 == threadList.size()) {
threadList.get(0).call();
}
else {
CompletableFuture<?> all = CompletableFuture.allOf(
threadList.stream()
.map(c -> callAsync(c, myThreadPoolExecutor))
.toArray(CompletableFuture<?>[]::new));
try {
all.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
public static <R> CompletableFuture<R> callAsync(Callable<R> callable, Executor e) {
CompletableFuture<R> cf = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try { cf.complete(callable.call()); }
catch(Throwable ex) { cf.completeExceptionally(ex); }
}, e);
return cf;
}
So we have no invokeAll which takes care of submitting all jobs. We have to do this manually, either with a loop or a stream operation. On the other hand, we get a single future via allOf representing the completion status, exceptionally if at least one job failed.
Unlike invokeAll, which waits for the completion, allOf only returns the future so it is the all.get() call which waits for the completion. We could do other things before it or even use this property to always perform the first job in the caller thread:
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
CompletableFuture<?> tail = CompletableFuture.allOf(
threadList.stream().skip(1)
.map(c -> callAsync(c, myThreadPoolExecutor))
.toArray(CompletableFuture<?>[]::new)),
head = callAsync(threadList.get(0), Runnable::run);
try {
head.get();
tail.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
This will always call the first callable in the current thread, as Runnable::run used as Executor will perform the action immediately in the calling thread. But it's treated uniformly in all other aspects, especially the exception handling. When there is only one job, allOf invoke with an empty array will do nothing and return an already completed future, which will have the desired effect.
Future.isDone() tells us if the executor has finished processing the task. If the task is completed, it will return true otherwise, it returns false.
for (Future<Boolean> future : futureList) {
while(!future.isDone())
{
doSOmethingElse();
Thread.sleep(300);//Optional
}
try {
future.get();
}
catch (Exception e)
{
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
But we don't have to worry about that since we get to the point where get() is called after making sure that the task is finished.
I execute a few callables through ThreadPoolExecutor. If thread list contains only 1 callable then I directly call call method of my CallableService. If list contains more than 1 callables then I execute all those threads in parallel via thread pool executor.
I guess you have already implemented this part. (You might run into memory usage issues if your jobs are heavy and you have 100 threads running as configured. But that is a different problem.)
And if future.get() is enhanced to avoid blocking, that will be a plus.
For this, you may take this approach:
Create another ExecutorService whose job will be just to run the Future.get() calls.
Submit your Future.get() to that service as shown below.
Shut it down and await termination.
if (null != futureList) {
ExecutorService waitSvc = Executors.newCachedThreadPool();
for (Future<Boolean> future : futureList) {
try {
waitSvc.submit( () -> future.get() );
}
catch (Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
waitSvc.shutdown(); //This may take some time. You may want to call awaitTermination() after this.
}
However, I feel that you should redesign the overall approach of using so many threads, unless this is only a for-learning application.

Detecting a timeout exception on a Java Future without calling get() on it

I am building a library that needs to some bluetooth operations on Android. I want to return a Future instance, so whoever is using my library can call .get() on the future returned and can handle ExecutionException, TimeoutException and InterruptedException themselves. However, I want to detect a timeout myself because I need to some cleanup logic like disconnecting from the device and so on. How can I achieve this?
You could implement a wrapper class around Future which delegates to a different one (the one returned by wherever you're getting your Future at the moment). Something like:
final class DelegatingFuture<T> implements Future<T> {
private final Future<T> delegate;
DelegatingFuture(final Future<T> delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
// All other methods simply delegate to 'delegate'
#Override
public T get()
throws InterruptedException, ExecutionException {
try {
return this.delegate.get();
} catch (final Exception ex) {
// Handle cleanup...
throw ex;
}
}
// Something similar for get(long timeout, TimeUnit unit)
}
And then simply return new DelegatingFuture<>(currentFuture); wherever your handing these out.
The timeout is relevant to the caller of the get method with timeout and only to that caller. A timeout is nowhere meant to imply a cancellation. E.g., the following code is a legitimate usage of the Future API:
ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> f = es.submit(() -> {
Thread.sleep(3000);
return "hello";
});
for(;;) try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
break;
}
catch(TimeoutException ex) {
// perhaps, do some other work
System.out.println("will wait something more");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
break;
}
es.shutdown();
Tying the cleanup to the methods actually intended to query the result, is not a useful approach. The timeout provided by the caller(s) of that method do not relate to the actual operation. There’s not even a guaranty that the result will be queried before the operations ends or that it gets queried at all.
The cleanup should happen when either, the operation finished or when the future gets cancelled explicitly. If the caller intends a cancellation after a timeout, the caller only needs to invoke cancel after catching a TimeoutException.
One approach, often pointed to, is to use a CompletionService, e.g.
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
static final CompletionService<String> COMPLETION_SERVICE
= new ExecutorCompletionService<>(MY__EXECUTOR);
static final Future<?> CLEANER = MY__EXECUTOR.submit(() -> {
for(;;) try {
Future<String> completed = COMPLETION_SERVICE.take();
System.out.println("cleanup "+completed);
} catch(InterruptedException ex) {
if(MY__EXECUTOR.isShutdown()) break;
}
});
public static Future<String> doSomeWork() {
return COMPLETION_SERVICE.submit(() -> {
Thread.sleep(3000);
return "hello";
});
}
You are in control over when to poll the completed futures, like in another background thread, as shown in the example, or right before commencing new jobs.
You can test it like
Future<String> f = doSomeWork();
try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
}
catch(TimeoutException ex) {
System.out.println("no result after 500ms");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
}
if(f.cancel(true)) System.out.println("canceled");
f = doSomeWork();
// never calling get() at all
But honestly, I never understood why such complicated things are actually necessary. If you want a cleanup at the right time, you can use
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
FutureTask<String> ft = new FutureTask<>(actualJob) {
#Override
protected void done() {
System.out.println("cleanup "+this);
}
};
MY__EXECUTOR.execute(ft);
return ft;
}
to achieve the same.
Or even simpler
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
return MY__EXECUTOR.submit(() -> {
try {
return actualJob.call();
}
finally {
// perform cleanup
System.out.println("cleanup");
}
});
}
In either case, the cleanup will be performed whether the job was completed successfully, failed, or got canceled. If cancel(true) was used and the actual job supports interruption, the cleanup also will be performed immediately after.

Method returning CompletableFuture executes twice when an exception is thrown

I've got a Spring Boot application which processes messages from an AWS SQS queue. The application makes a request for 10 messages, initiates a CountDownLatch to 10, then passes each message to an #Async method which returns a CompletableFuture. The CompletableFuture has a thenAccept() which deletes the message, and a whenComplete() which logs an exception if there is one and decrements the latch countdown. When the countdown completes, the next batch of messages is retrieved and the process starts over.
When there is no exception, this all runs perfectly. However, if an exception is thrown within the CompletableFuture method, the method executes twice before returning to the whenComplete().
Main method:
public void readAllMessages(String sqsUrl, MessageConsumer messageConsumer) {
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest(sqsUrl).withMaxNumberOfMessages(10);
List<Message> messages;
try {
do {
messages = amazonSQS.receiveMessage(receiveMessageRequest).getMessages();
if (CollectionUtils.isNotEmpty(messages)) {
final CountDownLatch latch = new CountDownLatch(messages.size());
messages.forEach(message -> {
try {
messageConsumer.processMessage(message)
.thenAccept(m -> {
deleteMessage(sqsUrl, message);
})
.whenComplete((value, exception) -> {
LOGGER.info("Processing complete for message {}", message.getMessageId());
latch.countDown();
if (exception != null) {
exceptionLogger.logException(String.format("Couldn't process message. queue:%s. id:%s", sqsUrl, message.getMessageId()), exception);
}
});
} catch (Throwable e) {
LOGGER.error("Refreshing tax rate for message {} failed with exception {} ", message.getBody(), e);
}
});
latch.await();
} else {
LOGGER.debug("Queue is empty: '{}'", sqsUrl);
}
} while (CollectionUtils.isNotEmpty(messages));
} catch (InterruptedException e) {
LOGGER.info("Thread interrupted, stopping.");
}
}
MessageConsumer.processMessage
#XRayLogged(segmentName = "outdated_host_tax_rate_update")
#Async
#Transactional
#Override
public CompletableFuture<?> processMessage(Message message) {
OutdatedTaxRateMessage taxRateMessage;
try {
taxRateMessage = objectMapper.readValue(message.getBody(), OutdatedTaxRateMessage.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
Long id = taxRateMessage.getHostId();
LOGGER.info("Updating tax rate. hostId:'{}', messageId:'{}'", id, message.getMessageId());
hostTaxRateRefreshingService.refreshHostTaxRate(id);
LOGGER.info("Updated tax rate. hostId:'{}', messageId:'{}'", id, message.getMessageId());
return CompletableFuture.completedFuture(null);
}
When an exception is thrown, the "Updating tax rate. hostId:''" message is logged twice, followed by a single set of messages from the whenComplete() block ("Processing complete...", "Couldn't process message...")
Can anyone please help me understand why this is happening?
The cause of the issue was determined to be a custom annotation on the method. The annotation was meant to transmit information to AWS Xray, but was inadvertently executing the annotated method twice when an exception was thrown. That mechanism is still being worked out, but at least we've identified the culprit. The question has been updated, as the annotation had been left off.

Categories

Resources