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 ?
Related
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)
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.
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.
Let's say I have this Java code that does something asynchronously:
public String main() {
try {
// Code before that could throw Exceptions
CompletableFuture.runAsync(() -> {...});
// Code after that could throw Exceptions
} catch (SomeException e) {
// ...
} catch (CompletionException e) {
// ...
}
}
If this were to run and the Async task successfully starts executing, will it still complete even if something else throws an Exception? If not, how can I let the async call finish executing while the Exception gets thrown?
If this were to run and the Async task successfully starts executing, will it still complete even if something else throws an Exception?
yes. The task is not interrupted.
NOTE: If your program exits as a result of an Exception, then the task will be stopped.
If not, how can I let the async call finish executing while the Exception gets thrown?
It does this by default.
If you want to cancel the task however it might ignore the interrupt.
public String main() {
CompletableFuture future = null;
try {
// Code before that could throw Exceptions
future = CompletableFuture.runAsync(() -> {...});
// Code after that could throw Exceptions
} catch (SomeException e) {
if (future != null) future.cancel(true);
// ...
} catch (CompletionException e) {
// ...
}
}
As long as the task has already started, any exceptions thrown after the call to runAsync will not affect that task.
Exceptions propagate up the call stack. A call stack is local to a particular thread. As your task is running asynchronously (i.e. on another thread), there is no way for an exception thrown on another thread to affect it.
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.