How to wait for WebClient response on timeout in Spring? - java

I have a WebClient that I want to stop and provide a fallback value after a certain timeout.
webClient.post()
.uri(path)
.bodyValue(body)
.retrieve()
.bodyToMono(type)
.timeout(Duration.ofSeconds(15))
.onErrorResume(ex -> Mono.just(provideFallbackValue())); //not only timeout, but any failure
Anyways, I would like to continue waiting for the response in another background thread in case of a TimeoutException (at least for let's say for another 60s), and still process the response then async.
Is that possible?

What you can do is change the point of view of this code. Instead of wait for additional 60 seconds in another thread start immediately the new thread and work asynchronously.
If the answer from the second thread arrives in 15 seconds or less you can reply immediately. Otherwise, send the message related to the error, but you still have active the call until 60 seconds.
Here is a code that can do that:
private Future<YourClass> asyncCall() {
...
// Put here the async call with a timeout of 60 seconds
// This call can be sync because you are already in a secondary thread
}
// Main thread
try {
Future<YourClass> future = executors.submit(() -> asyncCall());
YourClass result = future.get(15, TimeUnit.SECOND);
// Result received in less than 15 seconds
} catch (TimeoutException e) {
// Handle timeout of 15 seconds
}

Related

Jodah Failsafe Library Timeout is taking longer then expected

I have a situation in which I wanted to implement an API retry mechanism.
Let say I have an API that calls third party API where normal response time comes under 2 seconds but sometimes we got an error saying "Service Not available", "Gateway Timeout" Etc.
So I went online to see if we have a library to handle these things and I found out https://jodah.net/failsafe/
Purpose Of using Library:-
If under 5 seconds, I don't get the result, I will cancel the execution of the current call and try one more time.
For that, In Library I can see we have timeout and retry policy.
First I am trying the timeout.
Timeout<Object> timeout = Timeout.of(Duration.ofMillis(1000)).withCancel(true)
.onFailure(e -> logger.error("Connection attempt timed out {} {} ",new DateTime(), e.getFailure()))
.onSuccess(e -> logger.info("Execution completed on time"));
try {
logger.info("TIme: {}", new DateTime());
result = Failsafe.with(timeout).get(() -> restTemplate.postForEntity(messageSendServiceUrl, request, String.class));
} catch (TimeoutExceededException | HttpClientErrorException e) {
logger.info("TIme: {}", new DateTime());
logger.error("Timeout exception", e);
} catch (Exception e) {
logger.error("Exception", e);
}
But while calculating the time I am getting 20 seconds delay between calling the API and receiving TimeoutExceededException, which should be 1 second as duration is Duration.ofMillis(1000). Below you can see a difference of 21 seconds.
TIme: 2020-06-11T10:00:17.964+05:30
Connection attempt timed out 2020-06-11T10:00:39.037+05:30 {}
Can you please let me know what I am doing wrong here.
Second is the retry policy
RetryPolicy<Object> retryPolicy = new RetryPolicy<>()
.handle(HttpClientErrorException.class, TimeoutExceededException.class, Exception.class)
.withDelay(Duration.ofSeconds(1))
.withMaxRetries(3);
I want once TimeoutExceededException exception occurs after let's say 3 seconds, with a delay of 1 second, again the request is fired with max 3 retries.
I am using it as
result = Failsafe.with(retryPolicy,timeout).get(() -> restTemplate.postForEntity(messageSendServiceUrl, request, String.class));
get is a blocking or synchronous operation and it uses the calling thread. There is virtually no way for Failsafe to stop early. Timeout is best used in conjunction with an asynchronous operation, usually indicated by *Async methods. Make sure you read https://jodah.net/failsafe/schedulers/ because the default has some implications and is usually a poor choice for IO-bound operations.

RxJava2 - no event received with .blockingFirst()

I'm trying to get a value from my Observable (BehaviorSubject.create())
When I run locationObservable.subscribe {} I receive the results withing a few seconds. However with
try {
it.locationObservable
.timeout(10, TimeUnit.SECONDS)
.blockingFirst()
} catch (e: Exception) {
}
nothing happens with or without timeout. I simply receive "java.util.concurrent.TimeoutException: The source did not signal an event for 10 seconds and has been terminated". I have tried blockingFirst(), blockingLast() and blockingForEach(). Am I somehow blocking the thread that would provide me with the value?
EDIT: After moving it from the main thread it started working. Thank you everyone for your input.
The operator .observeOn(Schedulers.io()) will queue all emitted items to be emitted in the IO thread of the Android application.
Try the following:
.timeout(10, TimeUnit.SECONDS, Observable.just("fallback"))

How to be sure threads are cleaned up

I have a method producer.postMessage(message) which sends a message to some queue. If for some reason the message could not be sent in a period of time I want this task to be canceled. I came up with the implementation below. My question is if there is an interrupt can I be sure the Future task/ executor service are all shutdown and if not what changes need to be made to make this working without any threads not being terminated.
Thanks a lot
public void postMessage(final Object object)
{
LOG.debug("postMessage object " + object.getClass().getSimpleName());
Message message = new Message("task", 10, object);
try
{
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<?> f = ex.submit(new Runnable()
{
public void run()
{
producer.postMessage(message);
LOG.debug("Finished sending message for " + object.getClass().getSimpleName());
}
});
f.get(5, TimeUnit.SECONDS);
ex.shutdown();
}
catch (InterruptedException | ExecutionException | TimeoutException e)
{
LOG.error("Could not deliver message to the queue, Check AMQ is running");
}
}
The shutdown() method on an Executor will stop it from accepting new tasks, but attempts to complete the running tasks. You should use method shutdownNow() to have the Executor request that currently running tasks are stopped.
This may not be necessary however, if you use method cancel(boolean mayInterruptIfRunning) on the Future object. f.get(5, TimeUnit.SECONDS) will wait at most 5 seconds for completion of the tasks before returning. Since you submit a Runnable rather than a Callable, the return will always be null; Runnables can't return a result, while Callables can. What you can do is call isDone() on the Future after the get with a 5 second timeout, and if it returns false, it means the tasks has not completed. In that case you can then call cancel(true) on the Future to attempt to cancel the task. The true argument indicates that you'll allow the Future to interrupt the thread if it is running. You may want to catch InterruptedExceptions in your Runnable and log them. Note however that not every call may respond to interrupt requests (for example, some I/O operations may not, while a lot of Java NIO stuff does).
Calling the shutdown on the executor may no longer be necessary, since it's only used for a single task and should have no other waiting or running tasks, but it's probably still good form.
So your code would become:
f.get(5, TimeUnit.SECONDS);
if (!f.isDone())
f.cancel(true);
ex.shutdown();

How to interrupt a function call in Java

I am trying to use a Third Party Internal Library which is processing a given request. Unfortunately it is synchronous in nature. Also I have no control on the code for the same. Basically it is a function call. This function seems to a bit erratic in behavior. Sometimes this function takes 10 ms to complete processing and sometimes it takes up to 300 secs to process the request.
Can you suggest me a way to write a wrapper around this function so that it would throw an interrupted exception if the function does not complete processing with x ms/secs. I can live with not having the results and continue processing, but cannot tolerate a 3 min delay.
PS: This function internally sends an update to another system using JMS and waits for that system to respond and sends apart from some other calculations.
Can you suggest me a way to write a wrapper around this function so that it would throw an interrupted exception if the function does not complete processing with x ms/secs.
This is not possible. InterruptException only gets thrown by specific methods. You can certainly call thread.stop() but this is deprecated and not recommended for a number of reasons.
A better alternative would be for your code to wait for the response for a certain amount of time and just abandon the call if doesn't work. For example, you could submit a Callable to a thread pool that actually makes the call to the "Third Party Internal Library". Then your main code would do a future.get(...) with a specific timeout.
// allows 5 JMS calls concurrently, change as necessary or used newCachedThreadPool()
ExecutorService threadPool = Executors.newFixedThreadPool(5);
...
// submit the call to be made in the background by thread-pool
Future<Response> future = threadPool.submit(new Callable<Response>() {
public Response call() {
// this damn call can take 3 to 3000ms to complete dammit
return thirdPartyInternalLibrary.makeJmsRequest();
}
});
// wait for some max amount of time
Response response = null;
try {
response = future.get(TimeUnit.MILLISECONDS, 100);
} catch (TimeoutException te) {
// log that it timed out and continue or throw an exception
}
The problem with this method is that you might spawn a whole bunch of threads waiting for the library to respond to the remote JMS query that you would not have a lot of control over.
No easy solution.
This will throw a TimeoutException if the lambda doesn't finish in the time allotted:
CompletableFuture.supplyAsync(() -> yourCall()).get(1, TimeUnit.SECONDS)
Being that this is 3rd party you cannot modify the code. As such you will need to do two things
Launch the execution in a new thread.
Wait for execution in current thread, with timeout.
One possible way would be to use a Semaphore.
final Semaphore semaphore = new Semaphore(0);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// do work
semaphore.release();
}
});
t.start();
try {
semaphore.tryAcquire(1, TimeUnit.SECONDS); // Whatever your timeout is
} catch (InterruptedException e) {
// handle cleanup
}
The above method is gross, I would suggest instead updateing your desing to use a dedicated worker queue or RxJava with a timeout if possible.

java : execute a method over a maximum period of time

I am using the JavaMail API , and there is a method in the Folder class called "search" that sometimes take too long to execute. What i want is to execute this method over a maximum period of time( say for example 15 seconds in maximum) , that way i am sure that this method will not run up more than 15 seconds.
Pseudo Code
messages = maximumMethod(Folder.search(),15);
Do I have to create a thread just to execute this method and in the main thread use the wait method ?
The best way to do this is create a single threaded executor which you can submit callables with. The return value is a Future<?> which you can get the results from. You can also say wait this long to get the results. Here is sample code:
ExecutorService service = Executors.newSingleThreadExecutor();
Future<Message[]> future = service.submit(new Callable<Message[]>() {
#Override
public Message[] call() throws Exception {
return Folder.search(/*...*/);
}
});
try {
Message[] messages = future.get(15, TimeUnit.SECONDS);
}
catch(TimeoutException e) {
// timeout
}
You could
mark current time
launch a thread that will search in the folder
while you get the result (still in thread) don't do anything if current time exceeds time obtained in 1 plus 15 seconds. You won't be able to stop the connection if it is pending but you could just disgard a late result.
Also, if you have access to the socket used to search the folder, you could set its timeout but I fear it's gonna be fully encapsulated by javamail.
Regards,
Stéphane
This SO question shows how to send a timeout exception to the client code: How do I call some blocking method with a timeout in Java?
You might be able to interrupt the actual search using Thread.interrupt(), but that depends on the method's implementation. You may end up completing the action only to discard the results.

Categories

Resources