Unexplained `kotlinx.coroutines.JobCancellationException: Job was cancelled` with kotlin coroutines and spring WebClient? - spring

I have a bit of code written using kotlin coroutines
suspend fun getData(request: Request): Response {
return try {
webClient.post()
.uri(path)
.body(BodyInserters.fromObject(request))
.retrieve()
.awaitExchange()
.awaitBody<Response>()
} catch (e: Exception) {
logger.error("failed", e)
throw Exception("failed", e)
}
}
I am calling this bit of code like
val response = withContext(Dispatchers.IO) {
client.getData(request)
}
In my logs i see this exception happening from time to time
kotlinx.coroutines.JobCancellationException: Job was cancelled but this does not allow me to find what actually went wrong. I assume one of the suspending extension functions (awaitExchange or awaitBody) failed but i am not sure of that.

Related

Panache reactiveTransactional timeout with no stack trace

Hi I have played a lot with the following code and has read https://github.com/quarkusio/quarkus/issues/21111
I think I am facing a very similar issue, where it will work the first 4 times and then it stops working and things are stuck and eventually showing.
2022-09-15 23:21:21,029 ERROR [io.sma.rea.mes.provider] (vert.x-eventloop-thread-16) SRMSG00201: Error caught while processing a message: io.vertx.core.impl.NoStackTraceThrowable: Timeout
I have seen such exact behaviours in multiple bug reports and discussion threads.
I am using quarkus-hibernate-reactive-panache + quarkus-smallrye-reactive-messaging with kafka (v2.12)
#Incoming("words-in")
#ReactiveTransactional
public Uni<Void> storeToDB(Message<String> message) {
return storeMetamodels(message).onItemOrFailure().invoke((v, throwable) -> {
if (throwable == null) {
Log.info("Successfully stored");
message.ack();
} else {
Log.error(throwable, throwable);
message.nack(throwable);
}
});
}
private Uni<Void> storeMetamodels(Message<String> message) {
List<EntityMetamodel> metamodels = Lists.newArrayList();
for (String metamodelDsl : metamodelDsls.getMetamodelDsls()) {
try {
EntityMetamodel metamodel = new EntityMetamodel();
metamodel.setJsonSchema("{}")
metamodels.add(metamodel);
} catch (IOException e) {
Log.error(e, e);
}
}
return Panache.getSession().chain(session -> session.setBatchSize(10)
.persistAll(metamodels.toArray((Object[]) new EntityMetamodel[metamodels.size()])));
}
NOTE This same code works if it is running on RestEasy Reactive but I need to move the actual processing and storing to DB away from rest easy as it will be a large process and I do not want it to be stuck on the Rest API waiting for a few minutes.
Hope some Panache or smallrye reactive messaging experts can shed some lights.
Could you try this approach, please?
#Inject
Mutiny.SessionFactory sf;
#Incoming("words-in")
public Uni<Void> storeToDB(Message<String> message) {
return storeMetamodels(message).onItemOrFailure().invoke((v, throwable) -> {
if (throwable == null) {
Log.info("Successfully stored");
message.ack();
} else {
Log.error(throwable, throwable);
message.nack(throwable);
}
});
}
private Uni<Void> storeMetamodels(Message<String> message) {
List<EntityMetamodel> metamodels = Lists.newArrayList();
for (String metamodelDsl : metamodelDsls.getMetamodelDsls()) {
try {
EntityMetamodel metamodel = new EntityMetamodel();
metamodel.setJsonSchema("{}")
metamodels.add(metamodel);
} catch (IOException e) {
Log.error(e, e);
}
}
return sf
.withTransaction( session -> session
.setBatchSize(10)
.persistAll(metamodels.toArray((Object[]) new EntityMetamodel[metamodels.size()]))
);
}
I suspect you've hit a bug where the session doesn't get closed at the end of storeToDB. Because the session doesn't get closed when injected using Panache or dependency injection, the connection stays open and you hit the limit of connections that can stay open.
At the moment, using the session factory makes it easier to figure out when the session gets closed.

Throw actual exception from completeablefuture

I am making parallel call using completablefuture like below,
public Response getResponse() {
Response resultClass = new Response();
try {
CompletableFuture<Optional<ClassA>> classAFuture
= CompletableFuture.supplyAsync(() -> service.getClassA() );
CompletableFuture<ClassB> classBFuture
= CompletableFuture.supplyAsync(() -> {
try {
return service.getClassB();
}
catch (Exception e) {
throw new CompletionException(e);
}
});
CompletableFuture<Response> responseFuture =
CompletableFuture.allOf(classAFuture, classBFuture)
.thenApplyAsync(dummy -> {
if (classAFuture.join().isPresent() {
ClassA classA = classAFuture.join();
classA.setClassB(classBFuture.join());
response.setClassA(classA)
}
return response;
});
responseFuture.join();
} catch (CompletionExecution e) {
throw e;
}
return response;
}
I need to add try catch for return service.getClassB() as it throwing an exception inside the method getClassB.
Now problem I am facing is if service.getClassB() throws error it always comes wrapped inside java.util.concurrent.ExecutionException. There is a scenario where this method throws UserNameNotFoundException but this is wrapped inside ExecutionException and it is not getting caught in the right place #ControllerAdvice Exception handler class. I tried different option using throwable but it didn't help.
Is there a good way to handle the exception and unwrap and send it to #ControllerAdvice class?
Your code has several errors, like referring to a variable response that is not declared in this code and most probably supposed to be the resultClass declared at the beginning. The line
ClassA classA = classAFuture.join(); suddenly ignores that this future encapsulates an Optional and there are missing ) and ; separators.
Further, you should avoid accessing variables from the surrounding code when there’s a clean alternative. Also, using allOf to combine two futures is an unnecessary complication.
If I understood your intention correctly, you want to do something like
public Response getResponse() {
return CompletableFuture.supplyAsync(() -> service.getClassA())
.thenCombine(CompletableFuture.supplyAsync(() -> {
try {
return service.getClassB();
}
catch(ExecutionException e) {
throw new CompletionException(e.getCause());
}
}), (optA, b) -> {
Response response = new Response();
optA.ifPresent(a -> {
a.setClassB(b);
response.setClassA(a);
});
return response;
})
.join();
}
The key point to solve your described problem is to catch the most specific exception type. When you catch ExecutionException, you know that it will wrap the actual exception and can extract it unconditionally. When the getClassB() declares other checked exceptions which you have to catch, add another catch clause, but be specific instead of catching Exception, e.g.
try {
return service.getClassB();
}
catch(ExecutionException e) {
throw new CompletionException(e.getCause());
}
catch(IOException | SQLException e) { // all of getClassB()’s declared exceptions
throw new CompletionException(e); // except ExecutionException, of course
}

Does a CompletableFuture completes on a re-thrown exception?

I've just started using CompletableFuture and already loving it.
But one strange thing that appears to me while using a CompletableFuture is its method called "exceptionally"
Let's say I've a
CompletableFuture<?> cf1.
Now, once the data arrives, my code is applying some processing logic. In case of an exception, I make use of exceptionally method to rethrow MyCustomException
cf1
.thenApply(myData->Some Processing Logic)
.exceptionally(ex-> throw new MyCustomException())
cf.get();
Interestingly, the call to get method hangs indefinitely until I terminate the program. Does that mean that if a CompletableFuture re-throws an exception from the exceptionally block, the future will not be marked as complete? Do I need to explicity mark it as complete?
From docs the get method throws exception if the future completed exceptionally
ExecutionException - if this future completed exceptionally
So either you can return some value from exceptionally to identify the exception is thrown during thenApply and call get method for value
Second way, before calling get method you can make that future object completed using allOf, and check if the future is completed exceptionally
CompletableFuture.allOf(completableFuture);
completableFuture.isCompletedExceptionally(); //true
Heres a sample code that doesnot finish on throwing exception -
CompletableFuture<String> r1 = CompletableFuture.supplyAsync(() -> {
try{
Thread.sleep(1000);
throw new RuntimeException("blahh !!!");
}catch (Exception e) {
throw new RuntimeException(e);
}
});
CompletableFuture<String> r2 = CompletableFuture.supplyAsync(() -> "55");
CompletableFuture<String> r3 = CompletableFuture.supplyAsync(() -> "56");
CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println(Thread.currentThread()+" --- End."); });
Stream.of(r1, r2, r3).forEach(System.out::println);
try{
System.out.println(Thread.currentThread()+" --- SLEEPING !!!");
Thread.sleep(3000);
System.out.println(Thread.currentThread()+" --- DONE !!!");
} catch (Exception e) {
//e.printStackTrace();
}
Stream.of(r1, r2, r3).forEach(System.out::println);

Handling spring reactor exceptions in imperative spring application

I'm using the webflux in an imperative spring boot application. In this app I need to make rest calls to various backends using webclient and wait on all the responses before proceeding to the next step.
ClassA
public ClassA
{
public Mono<String> restCall1()
{
return webclient....exchange()...
.retryWhen(Retry.backoff(maxAttempts, Duration.ofSeconds(minBackOff))
.filter(this::isTransient)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
return new MyCustomException();
});
}
}
ClassB
public ClassB
{
public Mono<String> restCall2()
{
return webclient....exchange()...
.retryWhen(Retry.backoff(maxAttempts, Duration.ofSeconds(minBackOff))
.filter(this::isTransient)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
return new MyCustomException();
});
}
}
Mono<String> a = classAObj.restCall1();
Mono<String> b = classBObj.restCall2();
ArrayList<Mono<String>> myMonos = new ArrayList<>;
myMonos.add(a);
myMonos.add(b);
try {
List<String> results = Flux.mergeSequential(myMonos).collectList().block();}
catch(WebclientResponseException e) {
....
}
The above code is working as expected. The Webclient is configured to throw error on 5xx and 4xx which I'm able to catch using WebclientResponseException.
The problem is I'm unable to catch any exceptions from the react framework. For example my web clients are configured to retry with exponential backoff and throw exception on exhaustion and I have no way to catch it in my try catch block above. I explored the option to handle that exceptiom in the webclient stream using onErrorReturn but it does not propagate the error back to my subscriber.
I also cannot add the exception to the catch block as it's never being thrown by any part of the code.
Can anyone advice what is the best way to handle these type of error. scenarios. I'm new to webflux and reactive programming.

ExecutorService for Runtime.exec in Spring Boot the right way

I want to use Java ExecutorService in a Spring Boot application.
Multiple module of the application is called as #Scheduled.
This module calls a Process to get a result from an external application using Runtime.exec. The process is supposed to get a result which is processed by java
This method can be called as part of a Scheduled thread as well as part of a request and response which can be called 24/7 . Multiple instances of the method can be running at a time.
Is it optimum to use ExecutorService defined as a local variable like this or some other method is recommended.
The requirement is that Java should not be infinitely waiting to get a result. It should be timed out.
Here is the method called
public String getExternalInformation(String applicationPath, String command, int noOfTries)
{
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future;
boolean taskExecuted=false;
int trialNumber=0;
String response = "";
while((!taskExecuted)&&(trialNumber<noOfTries))
{
trialNumber++;
log.info("Starting Trial: "+trialNumber);
future= executor.submit(new TestProcessTask(applicationPath,command));
try {
System.out.println("Started Information Fetching ");
response=future.get(3, TimeUnit.MINUTES);
taskExecuted =true;
} catch (TimeoutException e) {
future.cancel(true);
System.out.println("Timed out!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdownNow();
return response;
}
The call() method of the TestProcessTask will call the Runtime.exec and parse the returning OutputStream.
I implemented Async in Spring as suggested by M. Deinum
Using the same Future methods and got it working.,

Resources