I am trying to achieve parallel process with completableFuture
i have 2 downstreams in completableFuture, where i need the response of 1 downstream.
Am able to my downstream calls with completableFuture and fetch the results as well.
But I am unable to read response and act on that , which also includes handling exception.
I tried with .completedExceptionally, handle in completableFuture (after .get() which waits for process to get completed) . . Can you please help me where am going wrong here
//no response needed
CompletableFuture<Void> cf0
= CompletableFuture.runAsync(() -> sdk1.method1(param1));
//response needed
CompletableFuture<Response> cf1
= CompletableFuture.supplyAsync(() -> service1.callapi(
param1, param2));
final CompletableFuture<Void> cf3 = CompletableFuture.allOf(
cf0,
cf1);
try {
//wait to get both the calls completed.
cf3.get();
response1 = cf1.get();
}catch (Exception e) {
throw new Exception(e.getMessage(), e.getMessage(), e.getMessage(),
500);
}
My question is
I have processingResponse method in different java class for the second downstream, how to call that method with param irrespective of happyPath or exception
if that processingResponse throws some error, how do i wrap or throw same error with completableFuture.
when the api throws some error my catch block is throwing default exception(which i dont want, the processResponse method exception should be thrown and stopped there). in my case i see the catch block exception immediately when the api call fails
Appreciate your help in this, Thanks
Related
I am using resilience4j Timelimiter in my project.
The timelimiter is throwing an error if a request is taking more than 10s, but it is not interrupting the thread.
When call comes from postman, i have put the debug and tested, after 10s in postman it displays an exception, but the thread still executes the method and after that added some print statements and it executed as well.
How to cancel or interrupt the thread after 10s in resilience4j.
class A {
TimeLimiterConfig config = TimeLimiterConfig.custom().cancelRunningFuture(true)
.timeoutDuration(Duration.ofMillis(TimeLimit)).build();
TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.of(config);
TimeLimiter timeLimiter = timeLimiterRegistry.timeLimiter("APITimelimiter", config);
public Response someMethod() throws Exception {
try {
timeLimiter.executeFutureSupplier(() -> CompletableFuture.supplyAsync(() -> {
return getData();
}));
} catch (Exception e) {
logger.error("Request has crossed the execution time of " + TimeLimit
+ " seconds");
throw new Exception("Your request has crossed the execution time of "+ TimeLimit+" seconds.");
}
}
public UserData getData() {
String jsonData = "";
return jsonData;
}
}
TimeLimiter cannot cancel a CompletableFuture. See #TimeLimiter times out slow method but does not cancel running future #905 Points out, that: the limited cancel() in case of CompletableFuture is not a bug, but a design decision. CompletableFuture is not inherently bound to any thread, while Future almost always represents background task.
My unban command sometimes throws a ContextException, when you unban a person who wasn't banned.
I wanted to catch it with a try catch block to notify the user that the person they are trying to unban isn't banned. This is what I tried:
try {
event.getGuild().unban(event.getMessage().getContentRaw().substring(8)).queue();
} catch(ContextException e) {
event.getChannel().sendMessage("This user isn't banned!").queue();
return;
}
But the catch() line just says Exception 'net.dv8tion.jda.api.exceptions.ContextException' is never thrown in the corresponding try block.
Your exception, in this case isn't even a ContextException but an ErrorResponseException. Since queue(...) does asynchronous operations in a different thread, the exceptions cannot be thrown from here. Instead, you should use the failure callback as described by the documentation.
You can use ErrorHandler to handle specific ErrorResponses.
Example:
String userId = event.getMessage().getContentRaw().substring(8);
ErrorHandler handler = new ErrorHandler().handle(ErrorResponse.UNKNOWN_BAN, (error) -> {
event.getChannel().sendMessage("This user isn't banned!").queue();
});
event.getGuild()
.unban(userId)
.queue(null, handler);
The ContextException is only there to tell you where in your code the error originated from. Since the actual exception happens on other threads which give you no context to find the issue.
ContextException handles async exception. So your try block cannot catch the exception.
You can change your code like this.
event.getGuild().unban(event.getMessage().getContentRaw().substring(8)).queue(
null,
(error) -> {
if (error.getMessage().equals("10026: Unknown Ban")) {
event.getChannel().sendMessage("This user isn't banned!").queue();
}
}
);
My current Lambda function is calling a 3rd party web service Synchronously.This function occasionally times out (current timeout set to 25s and cannot be increased further)
My code is something like:
handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
try{
response = calling 3rd party REST service
}catch(Exception e){
//handle exceptions
}
}
1)I want to custom handle the timeout (tracking the time and handling few milli seconds before actual timeout) within my Lambda function by sending a custom error message back to the client.
How can I effectively use the
context.getRemainingTimeInMillis()
method to track the time remaining while my synchronous call is running? Planning to call the context.getRemainingTimeInMillis() asynchronously.Is that the right approach?
2)What is a good way to test the timeout custom functionality ?
I solved my problem by increasing the Lambda timeout and invoking my process in a new thread and timing out the Thread after n seconds.
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Runnable r = () ->{
try {
myFunction();
} catch (Exception e) {
e.printStackTrace();
}
};
f = service.submit(r);
f.get(n, TimeUnit.MILLISECONDS);// attempt the task for n milliseconds
}catch(TimeoutException toe){
//custom logic
}
Another option is to use the
readTimeOut
property of the RestClient(in my case Jersey) to set the timeout.But I see that this property is not working consistently within the Lambda code.Not sure if it's and issue with the Jersey client or the Lambda.
You can try with cancellation token to return custom exceptions with lambda before timeout.
try
{
var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(1)); // set timeout value
var taskResult = ApiCall(); // call web service method
while (!taskResult.IsCompleted)
{
if (tokenSource.IsCancellationRequested)
{
throw new OperationCanceledException("time out for lambda"); // throw custom exceptions eg : OperationCanceledException
}
}
return taskResult.Result;
}
catch (OperationCanceledException ex)
{
// handle exception
}
I want to consume three REST service parallel calls in Java and my code is as below:
private CompletableFuture < EmployeesListResponse > makeAsyncCall(Request request) {
return CompletableFuture.supplyAsync(
() -> {
try {
LOGGER.warn("- - - - - Employees Service async call - - - - -");
return serviceObj.findServiceImpl(request);
} catch (Exception e) {
LOGGER.warn("service async call failed...", e);
}
return null;
}, asyncExecutor).handle((res, ex) -> {
LOGGER.warn("Exceptionally...", ex.toString(), res.toString());
return new EmployeesListResponse();
});
}
CompletableFuture < EmployeesListResponse > asyncFirstCall = makeAsyncCall(request);
CompletableFuture < EmployeesListResponse > asyncSecondCall = makeAsyncCall(request);
CompletableFuture < EmployeesListResponse > asyncThirdCall = makeAsyncCall(request);
CompletableFuture.allOf(asyncFirstCall, asyncSecondCall, asyncThirdCall).join();
In the above code, I am making three calls and joining them using CompletableFuture.allOf().join(). This code is working perfectly fine when the service response is 200 OK for all the three calls.
If one call fails(500 Internal Server Error or 404 Not Found) and other two service calls are 200 OK, then the code is throwing exception and the entire API response is getting failed with an exception. In this case, i want to ignore one service call with exception and return success response from the other two calls.
How to handle to ignore an exception in this scenario ?
So you're trying to wait for all 3 futures to complete before finishing, but the allOf future returns immediately when a single one fails. Instead you can explicitly wait on each:
List<CompletableFuture<String>> allFutures = Arrays.asList(asyncFirstCall, asyncSecondCall,
asyncThirdCall);
// await completion of all futures
allFutures.forEach(future -> {
try {
future.join();
} catch (CompletionException ex) {
// handled below.
}
});
if (allFutures.stream().filter(CompletableFuture::isCompletedExceptionally).count() > 2) {
throw new RuntimeException("Multiple failures");
}
// else continue with your business logic...
You don't need to handle the exception in try catch block as you are already handling handle() method. No matter you get the exception or not but this handle() method will execute every time. you just need to check if is there any exception if yes send default response as you want.
private CompletableFuture < EmployeesListResponse > makeAsyncCall(Request request) {
return CompletableFuture.supplyAsync(
() -> {
LOGGER.warn("- - - - - Employees Service async call - - - - -");
return serviceObj.findServiceImpl(request);
}, asyncExecutor).handle((res, ex) -> {
if (ex != null) {
LOGGER.warn("Exceptionally...", ex);
return "what ever default you want to return";
// or return new EmployeesListResponse();
}
return res;
});
}
If you want to ignore, try like below
CompletableFuture<Void> allOf = CompletableFuture.allOf(asyncFirstCall,asyncSecondCall);
allOf.whenComplete((aVoid, throwable) -> {
allOf.join();
});
I have been unable to catch time out exception that happens in my vertx HttpClientRequest. I have enclosed my connection and request creation code in try-catch block. Also I have added exceptionHandler and endHandler. But none of them gets fired when the time out happens. All I receive is the below error message which gets printed on the console. Please give me idea how to catch this exception, so that I can call the caller back with relevant info.
io.vertx.core.http.impl.HttpClientRequestImpl
SEVERE: io.netty.channel.ConnectTimeoutException: connection timed out:
The code below is what I use to make request to server. As you can see I have used try-catch and added exceptionHandler as well.
try{
HttpClient httpClient = Vert.x.createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true).setVerifyHost(false));
HttpClientRequest request = httpClient.get(port, host, uri.getRawPath(), event-> {
event.exceptionHandler(e -> {
log.error(" Error:: " + e);
});
event.handler(handler -> {
//code
});
});
request.putHeader(HttpHeaders.Names.AUTHORIZATION, "Basic "+authEnc);
request.end();
} catch(Exception e){
log.error(" Exception :: " + e);
}
Due to the async programing model you won't be able to use try-catch since your method has long been terminated before you get the timeout event. In order to catch it you need to setup an exception handler like:
request.exceptionHandler(t -> {
// where t is a throwable
// do something with it...
}
If you're interested in catching response exceptions same concept applies.