I am trying to figure out if there is a way to process a List items in a non blocking way ?
The scenario is as follows
There is method gets invoked to send a Collection of requests to remote API
Each request contains an ID
For each request that is sent the remote API returns a Future<SomeData>
For all futures complete successfully the method collects the associated request ID and sends it back.
Is it possible to check if the all the collected futures are now done without blocking ? Not sure if there is a way around future.get() while not losing the information to which request the future is associated with. The remote api does provide an option specify a call back. But I am unsure how to leverage that in way that won't be blocking in some way.
Future<SomeData> processRequests(Data d, CallBack());
If you simply want to test if all of the futures have completed, failed or been cancelled:
public boolean allDone(List<Future<?>> futures) {
for (f Future<?>: futures) {
if (!f.isDone()) {
return false;
}
}
return true;
}
Obviously, that (potentially) entails checking each one. But that is unavoidable.
On the other hand, if you need to wait until the futures are all done, that will (potentially) block. That also is unavoidable.
On the other hand, if you need to process the futures that have completed without blocking on those that haven't, do something like this:
public <T> void processDone(List<Future<T>> futures, Consumer<T> process) {
for (Iterator<Future<T>> it = futures.iterator();
it.hasNext; /* */) {
Future<T> f = it.next();
if (f.isDone()) {
process(f.get());
it.remove();
}
}
}
Related
Is there any possible safe way to detect timeouts in a CompletableFuture chain?
O someValue = CompletableFuture.supplyAsync(() -> {
...
// API Call
...
}).thenApply(o -> {
...
}).thenApply(o -> {
// If the chain has timed out, I still have 'o' ready here
// So at least cache it here, so it's available for the next request
// Even though the current request will return with a 'null'
...
}).get(10, TimeUnit.SECONDS);
// cache 'someValue'
return someValue;
It completes successfully without a timeout, I can use 'someValue' and do whatever with it
If it times out, it throws a TimeoutException and I have lost the value, even though it's still being processed in the background
The idea is that even if it times out and since the API call in the thread still completes in the background and returns the response, I can use that value, let's say, for caching
Not at least in the way you show. When the exception is thrown, you lose any chance of getting your hands on the results of the API call even if it finishes. Your only chances of caching in a chain like that would be something like the following, which would not help with the time-outing API call itself
.thenApplyAsync(o -> {
cache = o;
// do something
}).thenApplyAsync(o -> {
cache = o;
// do something more
}).get(10, TimeUnit.SECONDS);
However reading through this gave me an idea, that what if you did something like the following
SynchronousQueue<Result> q = new SynchronousQueue<>();
CompletableFuture.supplyAsync(() -> {
// API call
}.thenAccept(result -> {
cache.put(result); // cache the value
q.offer(result); // offer value to main thread, if still there
}
);
// Main thread waits 10 seconds for a value to be asynchronously offered into the queue
// In case of timeout, null is returned, but any operations done
// before q.offer(result) are still performed
return queue.poll(10, TimeUnit.SECONDS);
An API call that doesn't finish in 10 seconds is still processed into cache as it is asynchronously accepted and the timeout happens in the main thread and not the CompletableFuture chain, even though the original request won't get the results (and I guess has to deal with it gracefully).
After spending the day of learning about the java Concurrency API, I still dont quite get how could I create the following functionality with the help of CompletableFuture and ExecutorService classes:
When I get a request on my REST endpoint I need to:
Start an asynchronous task (includes DB query, filtering, etc.), which will give me a list of String URLs at the end
In the meanwhile, responde back to the REST caller with HTTP OK, that the request was received, I'm working on it
When the asynchronous task is finished, I need to send HTTP requests (with the payload, the REST caller gave me) to the URLs I got from the job. At most the number of URLs would be around a 100, so I need these to happen in parallel.
Ideally I have some syncronized counter which counts how many of the http requests were a success/fail, and I can send this information back to the REST caller (the URL I need to send it back to is provided inside the request payload).
I have the building blocks (methods like: getMatchingObjectsFromDB(callerPayload), getURLs(resultOfgetMachingObjects), sendHttpRequest(Url, methodType), etc...) written for these already, I just cant quite figure out how to tie step 1 and step 3 together. I would use CompletableFuture.supplyAsync() for step 1, then I would need the CompletableFuture.thenComponse method to start step 3, but it's not clear to me how parallelism can be done with this API. It is rather intuitive with ExecutorService executor = Executors.newWorkStealingPool(); though, which creates a thread pool based on how much processing power is available and the tasks can be submitted via the invokeAll() method.
How can I use CompletableFutureand ExecutorService together? Or how can I guarantee parallel execution of a list of tasks with CompletableFuture? Demonstrating code snippet would be much appreciated. Thanks.
You should use join() to wait for all thread finish.
Create Map<String, Boolean> result to store your request result.
In your controller:
public void yourControllerMethod() {
CompletableFuture.runAsync(() -> yourServiceMethod());
}
In your service:
// Execute your logic to get List<String> urls
List<CompletableFuture> futures = urls.stream().map(v ->
CompletableFuture.supplyAsync(url -> requestUrl(url))
.thenAcceptAsync(requestResult -> result.put(url, true or false))
).collect(toList()); // You have list of completeable future here
Then use .join() to wait for all thread (Remember that your service are executed in its own thread already)
CompletableFuture.allOf(futures).join();
Then you can determine which one success/fail by accessing result map
Edit
Please post your proceduce code so that other may understand you also.
I've read your code and here are the needed modification:
When this for loop was not commented out, the receiver webserver got
the same request twice,
I dont understand the purpose of this for loop.
Sorry in my previous answer, I did not clean it up. That's just a temporary idea on my head that I forgot to remove at the end :D
Just remove it from your code
// allOf() only accepts arrays, so the List needed to be converted
/* The code never gets over this part (I know allOf() is a blocking call), even long after when the receiver got the HTTP request
with the correct payload. I'm not sure yet where exactly the code gets stuck */
Your map should be a ConcurrentHashMap because you're modifying it concurrently later.
Map<String, Boolean> result = new ConcurrentHashMap<>();
If your code still does not work as expected, I suggest to remove the parallelStream() part.
CompletableFuture and parallelStream use common forkjoin pool. I think the pool is exhausted.
And you should create your own pool for your CompletableFuture:
Executor pool = Executors.newFixedThreadPool(10);
And execute your request using that pool:
CompletableFuture.supplyAsync(YOURTASK, pool).thenAcceptAsync(Yourtask, pool)
For the sake of completion here is the relevant parts of the code, after clean-up and testing (thanks to Mạnh Quyết Nguyễn):
Rest controller class:
#POST
#Path("publish")
public Response publishEvent(PublishEvent eventPublished) {
/*
Payload verification, etc.
*/
//First send the event to the right subscribers, then send the resulting hashmap<String url, Boolean subscriberGotTheRequest> back to the publisher
CompletableFuture.supplyAsync(() -> EventHandlerService.propagateEvent(eventPublished)).thenAccept(map -> {
if (eventPublished.getDeliveryCompleteUri() != null) {
String callbackUrl = Utility
.getUri(eventPublished.getSource().getAddress(), eventPublished.getSource().getPort(), eventPublished.getDeliveryCompleteUri(), isSecure,
false);
try {
Utility.sendRequest(callbackUrl, "POST", map);
} catch (RuntimeException e) {
log.error("Callback after event publishing failed at: " + callbackUrl);
e.printStackTrace();
}
}
});
//return OK while the event publishing happens in async
return Response.status(Status.OK).build();
}
Service class:
private static List<EventFilter> getMatchingEventFilters(PublishEvent pe) {
//query the database, filter the results based on the method argument
}
private static boolean sendRequest(String url, Event event) {
//send the HTTP request to the given URL, with the given Event payload, return true if the response is positive (status code starts with 2), false otherwise
}
static Map<String, Boolean> propagateEvent(PublishEvent eventPublished) {
// Get the event relevant filters from the DB
List<EventFilter> filters = getMatchingEventFilters(eventPublished);
// Create the URLs from the filters
List<String> urls = new ArrayList<>();
for (EventFilter filter : filters) {
String url;
try {
boolean isSecure = filter.getConsumer().getAuthenticationInfo() != null;
url = Utility.getUri(filter.getConsumer().getAddress(), filter.getPort(), filter.getNotifyUri(), isSecure, false);
} catch (ArrowheadException | NullPointerException e) {
e.printStackTrace();
continue;
}
urls.add(url);
}
Map<String, Boolean> result = new ConcurrentHashMap<>();
Stream<CompletableFuture> stream = urls.stream().map(url -> CompletableFuture.supplyAsync(() -> sendRequest(url, eventPublished.getEvent()))
.thenAcceptAsync(published -> result.put(url, published)));
CompletableFuture.allOf(stream.toArray(CompletableFuture[]::new)).join();
log.info("Event published to " + urls.size() + " subscribers.");
return result;
}
Debugging this was a bit harder than usual, sometimes the code just magically stopped. To fix this, I only put code parts into the async task which was absolutely necessary, and I made sure the code in the task was using thread-safe stuff. Also I was a dumb-dumb at first, and my methods inside the EventHandlerService.class used the synchronized keyword, which resulted in the CompletableFuture inside the Service class method not executing, since it uses a thread pool by default.
A piece of logic marked with synchronized becomes a synchronized block, allowing only one thread to execute at any given time.
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.
I use REST API based system, in which there are some requests that take long time to complete. I want to give user an option to cancel the request.
First, support
POST /requests
which will return a reference to the status of the request
{
"id": 1234,
"self"": "/requests/1234"
"status": "Running"
}
Then add support for
PUT /requests/1234
{
"status": "Canceled:"
}
That will let clients cancel a request if it hasn't finished yet. If the request is to create some other kind of resource, then instead of POST /requests, do POST /myResource, but still return the status object with the pointer to /requests in the response.
Clients can then poll /requests to see when the request is complete.
Firstly you need to use multiple threads because your program will be on hold while it is sending the request so you cannot click on something until it is back from hold.
Create a thread which calls the rest API in background without hanging the overall application and terminate that thread on click of a button.
note for terminating the thread you need to use stop function which is depreciated now because you cannot interrupt the thread or check a Boolean during the process.
#Deprecated
public final void stop()
Alternatively, you can use Maximum Time for a HTTP Request call by
HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
_For all scenario
Make thread pool method
executorService = Executors.newFixedThreadPool(1);
Put your logical method in callable store in future object
Future<Boolean> futureObjects = executorService.submit(newCallable<Boolean>() { ....call your logical method which you going run in multiple thread....});
3.Gets your results future object
return (futureObjects != null)
? futureObjects.get(timeout, TimeUnit.SECONDS)
: null;
The default waits until getting separate calls response.
4.IN between calling multiple threads requesting user want to stop or break their multiple thread calls then simply check executor is not terminated then terminate immediately
if (executorService != null && !executorService.isTerminated(){
executorService.shutdownNow();
}
I am using the new Couchbase Java Client API 2.1.1 and therefore JavaRx to access my Couchbase cluster.
When using asynchronous getAndLock on an already locked document, getAndLock fails with a TemporaryLockFailureException. In another SO question (rxjava: Can I use retry() but with delay?) I found out how to retry with delay.
Here is my adopted code:
CountDownLatchWithResultData<JsonDocument> resultCdl = new CountDownLatchWithResultData<>(1);
couchbaseBucket.async().getAndLock(key, LOCK_TIME).retryWhen((errorObserver) -> {
return errorObserver.flatMap((Throwable t) -> {
if (t instanceof TemporaryLockFailureException) {
return Observable.timer(RETRY_DELAY_MS, TimeUnit.MILLISECONDS);
}
return Observable.error(t);
});
}).subscribe(new Subscriber<JsonDocument>() {
#Override
public void onCompleted() {
resultCdl.countDown();
}
#Override
public void onError(Throwable e) {
resultCdl.countDown();
}
#Override
public void onNext(JsonDocument t) {
resultCdl.setInformation(t);
}
});
........
resultCdl.await();
if (resultCdl.getInformation() == null) {
//do stuff
} else ....
(CountDownLatchWithResultData simply extends a normal CountDownLatch and adds two methods to store some information before the count has reached 0 and retrieve it afterwards)
So basically I'd like this code to
try to get the lock infinitely once every RETRY_DELAY_MS milliseconds if a TemporaryLockFailureException occured and then call onNext
or to fail completely on other exceptions
or to directly call onNext if there is no exception at all
The problem now is that when retrying, it only retries once and the JsonDocument from resultCdl.getInformation() is always null in this case even though the document exists. It seems onNext is never called.
If there is no exception, the code works fine.
So apparently I am doing something wrong here but I have no clue as to where the problem might be. Does returning Observable.timer imply that with this new Obervable also the previously associated retryWhen is executed again? Is it the CountDownLatch with count 1 getting in the way?
This one is subtle. Up to version 2.2.0, the Observables from the SDK are in the "hot" category. In effect that means that even if no subscription is made, they start emitting. They will also emit the same data to every newcoming Subscriber, so in effect they cache the data.
So what you retry does is resubscribe to an Observable that will always emit the same thing (in this case an error). I suspect it comes out of the retry loop just because the lock maximum duration is LOCK_TIME...
Try wrapping the call to asyncBucket.getAndLock inside an Observable.defer (or migrate to the 2.2.x SDK if that's something you could do, see release and migration notes starting from 2.2.0).