I want to have a list of comletablefutures I want to wait. With following code.
public static <T> CompletableFuture<List<T>> finishAllQuery(
List<CompletableFuture<T>> futures) {
CompletableFuture<Void> allDoneFuture =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return allDoneFuture.thenApply(
v -> futures.stream().filter(Objects::nonNull)
.map(future -> future.join())
.collect(Collectors.toList())
);
}
CompletableFuture<List<Result>> allResponse = finishAllQuery(futures);
allResponse.get(5, milliseconds)
The problem is that among all the futures, some of them can be slow, I want that after the expiration time, the get method return with all completed results. Is there a way to do that?
Thanks a lot.
This should be handled by finishAllQuery itself. E.g., starting with Java 9, you can use
public static <T> CompletableFuture<List<T>> finishAllQuery(
List<CompletableFuture<T>> futures, long timeOut, TimeUnit unit) {
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.completeOnTimeout(null, timeOut, unit)
.thenApply(v -> futures.stream()
.filter(CompletableFuture::isDone)
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
With completeOnTimeout, we can force the completion of the future with a predefined value after the timeout elapsed. Here, we just use null, as the result value of allOf doesn’t matter anyway.
We just have to add a filter condition, to skip all futures which are not completed yet, as otherwise, join would block the thread.
This can be used like
CompletableFuture<List<Result>> allResponse
= finishAllQuery(futures, 5, TimeUnit.MILLISECONDS);
List<Result> list = allResponse.join(); // will wait at most 5 milliseconds
For Java 8, we can use
static <T> CompletableFuture<T> completeOnTimeout(
CompletableFuture<T> cf, T value, long timeOut, TimeUnit unit) {
ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<Boolean> job = e.schedule(() -> cf.complete(value), timeOut, unit);
return cf.whenComplete((x,y) -> { job.cancel(false); e.shutdown(); });
}
for the missing feature, which requires a small rewrite:
public static <T> CompletableFuture<List<T>> finishAllQuery(
List<CompletableFuture<T>> futures, long timeOut, TimeUnit unit) {
return completeOnTimeout(
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])),
null, timeOut, unit)
.thenApply(v -> futures.stream()
.filter(CompletableFuture::isDone)
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
The way, the caller uses the method, doesn’t change.
For production use, it’s worth rewriting the completeOnTimeout method to reuse the ScheduledExecutorService, but this also requires adding shutdown code or a thread factory creating daemon threads. With Java 9 or newer, you get that for free.
If you don't necessarily want all futures to complete, then you should not use allOf(). You can instead use CompletionService and then use the poll() method with timeout. So the flow will be:
Wrap your ExcecutorService in a CompletionService
Submit N tasks to CompletionService. Then poll for individual tasks with timeout. Something like:
elapsedTime = 0;
availableTime = 5ms;
completedFutures = List();
while (elapsedTime < availableTime):
remainingTime = availableTime - elapsedTime;
startTime = currentTime();
completedFutures.add(CompletionService.poll(timeout=remainingTime));
elapsedTime += (currentTime() - startTime)
Related
Trying to compare implementations of futures vs completed futures and check if the non blocking nature of completable futures is a better use case for my problem. Having an issue getting my completable impl to work correctly.
Collection<Future<ArrayList<MeterTariffValues>>> futures = new ArrayList<>();
List<CompletableFuture<GetSiteMeterTariffValues>> completableFutures = new ArrayList<>();
for (Site site : sites) {
completableFutures.add(
CompletableFuture.supplyAsync(new Supplier<GetSiteMeterTariffValues>() {
#Override
public GetSiteMeterTariffValues get() {
return new GetSiteMeterTariffValues(
site.getSite_id(),
methaneConversionVal,
nitrogenConversionVal,
bu_id,
region_id,
facility_id,
status,
resource_id,
metertype_id,
currentCalendarYear,
currentFiscalYear,
fiscalYearStartMonth,
dates,
meterMapper,
sqlSessionTemplate);
}
}, taskExecutor)
);
futures.add(
taskExecutor.submit(
new GetSiteMeterTariffValues(
site.getSite_id(),
methaneConversionVal,
nitrogenConversionVal,
bu_id,
region_id,
facility_id,
status,
resource_id,
metertype_id,
currentCalendarYear,
currentFiscalYear,
fiscalYearStartMonth,
dates,
meterMapper,
sqlSessionTemplate)));
}
The issue with the Completable implementation, is that it wont recognize that GetSiteMeterTariffValues returns a different type after the task is complete.
It returns
public ArrayList<MeterTariffValues> call()
But task executor is fine with this
for (Future<ArrayList<MeterTariffValues>> future : futures) {
long start = System.currentTimeMillis();
ArrayList<MeterTariffValues> siteMeterTariffMonthlyValues = future.get();
long end = System.currentTimeMillis();
long totalMS = end - start;
total += totalMS;
meterTariffValues.addAll(siteMeterTariffMonthlyValues);
}
So im wondering how i can do similar the above with completablefutures.
Note:GetSiteMeterTariffValues implements Callable<ArrayList>
CompletableFuture.supplyAsync() expects a Supplier which is the equivalent of Callable for ExecutorService.
The way you do it currently, you are just providing a Supplier that creates a Callable, but of course your Callable (GetSiteMeterTariffValues) is not called.
You just need to make GetSiteMeterTariffValues implement Supplier and use it directly in CompletableFuture.supplyAsync():
CompletableFuture.supplyAsync(
new GetSiteMeterTariffValues(
site.getSite_id(),
methaneConversionVal,
nitrogenConversionVal,
bu_id,
region_id,
facility_id,
status,
resource_id,
metertype_id,
currentCalendarYear,
currentFiscalYear,
fiscalYearStartMonth,
dates,
meterMapper,
sqlSessionTemplate),
taskExecutor)
Note that Supplier does not allow throwing checked exceptions, so you cannot just use a method reference to Callable.call().
I have a method (included below) to return the values of a list of CompletableFutures.
The method is supposed to:
be able to timeout after a given time.
be able to cancel all futures if there are more than n amount of exceptions.
The first point works well and indeed bombs out after it passed the timeout limit. (I still need to call exectuorService.shutdownNow() afterwards to return to the caller). The problem I'm having is with the second thing I'm trying to accomplish.
Lets say i have a list of 20,000 futures and all of them will have an exception, then why let all of them execute, if I see that there are too many exceptions then i assume that something is wrong with all of the futures andI want to cancel them.
In addition i would love to have a timeout on each future individually how long it may take, but this also would'nt work, unassuming for the same reason outlined below.
It seems that the reason is, because when I call allDoneFuture.thenApply(), at this point it waits and lets all the futures complete, either successfully or exceptionally. Only after all of them completed does it go through each future and fetches its result. At that point what good does it do to cancel, when they have completed already.
I would much appreciate if someone can show me how to accomplish this specific need: "Monitor the exceptions, and the individual timeouts, and based on that cancel all others".
Thanks.
Below is the method I wrote:
/**
* #param futures a list of completable futures
* #param timeout how long to allow the futures to run before throwing exception
* #param timeUnit unit of timeout
* #param allowedExceptions how many of the futures do we tolerate exceptions,
* NOTE: if an exception is thrown from the futures it will return null, until it reaches the allowedExceptions threshold
* */
public static <T> List<T> extractFromFutures(List<CompletableFuture<T>> futures, int timeout, TimeUnit timeUnit, int allowedExceptions) {
CompletableFuture<Void> allDoneFuture = CompletableFuture
.allOf(futures.toArray(new CompletableFuture[futures.size()]));
try {
AtomicInteger exceptionCount = new AtomicInteger(0);
return allDoneFuture.thenApply(v ->//when all are done
futures.stream().
map(future -> {
try {
//if only I could set an individual timeout
return future.get(timeout, timeUnit);
} catch (Exception e) {
future.cancel(true);
int curExceptionCnt = exceptionCount.incrementAndGet();
if(curExceptionCnt >= allowedExceptions){
//I would've hoped that it will throw it to the calling try-catch
//and then cancel all futures, but it doesn't
throw new RuntimeException(e);
}
else{
return null;
}
}
}).
collect(Collectors.<T>toList())
).get(timeout, timeUnit);
} catch (Exception e) {
allDoneFuture.cancel(true);
throw new RuntimeException(e);
}
}
To cancel all of the remaining futures after a certain number of exceptions you can call exceptionally on each of them and increment the exception count and possibly cancel them inside of that.
For individual timeouts you could create a class that holds the future with its timeout then sort them based on the timeout and call get with the timeout minus the elapsed time.
static class FutureWithTimeout<T> {
CompletableFuture<T> f;
long timeout;
TimeUnit timeUnit;
FutureWithTimeout(CompletableFuture<T> f, long timeout, TimeUnit timeUnit) {
this.f = f;
this.timeout = timeout;
this.timeUnit = timeUnit;
}
}
public static <T> List<T> extractFromFutures(List<FutureWithTimeout<T>> futures, int allowedExceptions) {
AtomicInteger exceptionCount = new AtomicInteger(0);
futures.forEach(f -> f.f.exceptionally(t -> {
if(exceptionCount.getAndIncrement() == allowedExceptions){
futures.forEach(c -> c.f.cancel(false));
}
return null;
}));
long t = System.nanoTime();
return futures.stream()
.sorted(Comparator.comparingLong(f -> f.timeUnit.toNanos(f.timeout)))
.map(f -> {
try {
return f.f.get(Math.max(0, f.timeUnit.toNanos(f.timeout) - (System.nanoTime() - t)),
TimeUnit.NANOSECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
f.f.cancel(false);
return null;
}
})
.collect(Collectors.toList());
}
Note that this may return the list in a different order than it was passed in. If you need it in the same order then you could change the map().collect() to a forEachOrdered and then re map them into their results after without sorting.
Also the mayInterruptIfRunning parameter to cancel has no effect on CompletableFuture so I changed it to false.
CompletableFuture completely ignores any call to cancel(true). I don't know why (presumably to simplify the API), but it sucks. If you want to make futures actually cancelable (where you can either manually check for interruption, or accept cancellation by blocking on a lock), then you have to use Future, not CompletableFuture.
Is there any way to schedule CompletableFuture in Java?
What I wanted to do is to schedule a task to be executed with some delay, and chain it with other operations to be performed asynchronously when it completes. So far I didn't find any way to do this.
For good ol' Futures we have e.g. ScheduledExecutorService, where we can schedule a task to be executed with some delay like this:
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Future<String> future = scheduledExecutorService.schedule(() -> "someValue", 10, TimeUnit.SECONDS);
Is there any similar way for CompletableFutures?
If you're using Java 9+ then CompletableFuture#delayedExecutor(long,TimeUnit) may fit your needs:
Returns a new Executor that submits a task to the default executor after the given delay (or no delay if non-positive). Each delay commences upon invocation of the returned executor's execute method.
Executor delayed = CompletableFuture.delayedExecutor(10L, TimeUnit.SECONDS);
CompletableFuture.supplyAsync(() -> "someValue", delayed)
.thenAccept(System.out::println)
.join();
There's also an overload where you can specify the Executor to use in place of the "default executor".
As said, there is support in Java 9.
But it’s not hard to create a similar feature under Java 8; you already named the necessary elements:
// prefer this constructor with zero core threads for a shared pool,
// to avoid blocking JVM exit
static final ScheduledExecutorService SCHEDULER = new ScheduledThreadPoolExecutor(0);
static Executor delayedExecutor(long delay, TimeUnit unit)
{
return delayedExecutor(delay, unit, ForkJoinPool.commonPool());
}
static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
{
return r -> SCHEDULER.schedule(() -> executor.execute(r), delay, unit);
}
which can be used similarly to the Java 9 feature:
Executor afterTenSecs = delayedExecutor(10L, TimeUnit.SECONDS);
CompletableFuture<String> future
= CompletableFuture.supplyAsync(() -> "someValue", afterTenSecs);
future.thenAccept(System.out::println).join();
Care must be taken to avoid that the shared scheduled executor’s threads prevent the JVM from terminating. The alternative to a zero core pool size is to use daemon threads:
static final ScheduledExecutorService SCHEDULER
= Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
Is it ever correct to not return or join on a CompletableFuture?
That is, is this code ever correct?
void noJoin() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
}
In the above code, we never know if the Futures complete successfully, or at all. (And I don't even know that the Futures created in noJoin are not eligible for garbage collection; but presumably the ForkJoinPool.commonPool() holds onto those references?)
In any case, if it truly doesn't matter to our program whether or not those Futures succeed, a better implementation of noJoin is just a no-op:
void noJoin() {}
If on the other hand, it does matter, we need to either join on or return both Futures, by composing them (either serially with thenCompose, or in parallel with thenCombine or allOf) and then join the composed Future or return it to our caller.
By joining, we block and throw an exception if either Future is exceptional; or better, by returning a Future, we remain asynchronous while ensuring that our caller gets a Future that holds any exceptional result:
Future<Void> returnBothAsync() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
return CompletableFuture.allOf(cfa, cfb);
}
or
void joinBothBlocking() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
CompletableFuture.allOf(cfa, cfb).get(50L, TimeUnit.MILLISECONDS);
}
I think this is true even if we arrange to handle all exceptions:
void noJoin() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect)
.exceptionally(e -> {
Logger.log(e);
return DEFAULT;
});
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
}
because even if the exceptional are handled / "can't happen", we still don't know if the Future ever completed at all.
Or am I wrong, and there are cases where code like that in noJoin is correct?
This is not a complete answer to your question. It probably depends on the exact use case to be able to tell how to handle CompletableFuture and their results.
If you choose to not wait for the results of the CompletableFutures, you will likely have to make sure that the used executor finishes all tasks. In your case the used executor is ForkJoinPool.commonPool() whose documentation says:
[...] However this pool and any ongoing processing are automatically terminated upon program System.exit(int). Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.
I am new Completable Future. I am trying to call a method parallel for a list of elements (which are arguments) and then combine the results to create a final response. I am also trying to set up timeout of 50 ms so that if the call doesn't return in 50 ms I will return a default value.
So far I ve tried this :
{
List<ItemGroup> result = Collections.synchronizedList(Lists.newArrayList());
try {
List<CompletableFuture> completableFutures = response.getItemGroupList().stream()
.map(inPutItemGroup ->
CompletableFuture.runAsync(() -> {
final ItemGroup itemGroup = getUpdatedItemGroup(inPutItemGroup); //call which I am tryin to make parallel
// this is thread safe
if (null != itemGroup) {
result.add(itemGroup); //output of the call
}
}, executorService).acceptEither(timeoutAfter(50, TimeUnit.MILLISECONDS),inPutItemGroup)) //this line throws error
.collect(Collectors.toList());
// this will wait till all threads are completed
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]))
.join();
} catch (final Throwable t) {
final String errorMsg = String.format("Exception occurred while rexecuting parallel call");
log.error(errorMsg, e);
result = response.getItemGroupList(); //default value - return the input value if error
}
Response finalResponse = Response.builder()
.itemGroupList(result)
.build();
}
private <T> CompletableFuture<T> timeoutAfter(final long timeout, final TimeUnit unit) {
CompletableFuture<T> result = new CompletableFuture<T>();
//Threadpool with 1 thread for scheduling a future that completes after a timeout
ScheduledExecutorService delayer = Executors.newScheduledThreadPool(1);
String message = String.format("Process timed out after %s %s", timeout, unit.name().toLowerCase());
delayer.schedule(() -> result.completeExceptionally(new TimeoutException(message)), timeout, unit);
return result;
}
But I keep getting error saying :
error: incompatible types: ItemGroup cannot be converted to Consumer<? super Void>
[javac] itemGroup))
incompatible types: inference variable T has incompatible bounds
[javac] .collect(Collectors.toList());
[javac] ^
[javac] equality constraints: CompletableFuture
[javac] lower bounds: Object
[javac] where T is a type-variable:
Can some one please tell me what I am doing wrong here ? And please correct me if I am going in the wrong direction.
Thanks.
Instead of
acceptEither(timeoutAfter(50, TimeUnit.MILLISECONDS), inPutItemGroup))
you’d need
applyToEither(timeoutAfter(50, TimeUnit.MILLISECONDS), x -> inPutItemGroup)
to compile the code. “accept” is an action consuming a value without returning a new value, “apply” is an action that produces a new value.
However, there’s still a logical error. The future returned by timeoutAfter will be completed exceptionally, so dependent stages will get completed exceptionally as well, without evaluating functions, so this chaining method is not suitable for replacing an exception with a default value.
Even worse, fixing this would create a new future which gets completed by either source future, but that does not affect the result.add(itemGroup) action performed in one of the source futures. In your code, the resulting future is only used to wait for the completion, but not for evaluating the result. So when your timeout elapses, you would stop waiting for the completion, whereas there still might be background threads modifying the list.
The correct logic is to separate the steps of fetching the value, which can get superseded by a default value on timeout, and the step of adding the result, either the fetched value or default value, to the result list. Then, you can wait for the completion of all add actions. On a timeout, there might be still ongoing getUpdatedItemGroup evaluations (there is no way to stop their execution), but their result would be ignored, so it doesn’t affect the result list.
It’s also worth pointing out that creating a new ScheduledExecutorService for every list element (that is not shut down after use, to make matters worse), is not the right approach.
// result must be effectively final
List<ItemGroup> result = Collections.synchronizedList(new ArrayList<>());
List<ItemGroup> endResult = result;
ScheduledExecutorService delayer = Executors.newScheduledThreadPool(1);
try {
CompletableFuture<?>[] completableFutures = response.getItemGroupList().stream()
.map(inPutItemGroup ->
timeoutAfter(delayer, 50, TimeUnit.MILLISECONDS,
CompletableFuture.supplyAsync(
() -> getUpdatedItemGroup(inPutItemGroup), executorService),
inPutItemGroup)
.thenAccept(itemGroup -> {
// this is thread safe, but questionable,
// e.g. the result list order is not maintained
if(null != itemGroup) result.add(itemGroup);
})
)
.toArray(CompletableFuture<?>[]::new);
// this will wait till all threads are completed
CompletableFuture.allOf(completableFutures).join();
} catch(final Throwable t) {
String errorMsg = String.format("Exception occurred while executing parallel call");
log.error(errorMsg, e);
endResult = response.getItemGroupList();
}
finally {
delayer.shutdown();
}
Response finalResponse = Response.builder()
.itemGroupList(endResult)
.build();
private <T> CompletableFuture<T> timeoutAfter(ScheduledExecutorService es,
long timeout, TimeUnit unit, CompletableFuture<T> f, T value) {
es.schedule(() -> f.complete(value), timeout, unit);
return f;
}
Here, the supplyAsync produces a CompletableFuture which will provide the result of the getUpdatedItemGroup evaluation. The timeoutAfter invocation will schedule a completion with a default value after the timeout, without creating a new future, then, the dependent action chained via thenAccept will add the result value to the result list.
Note that a synchronizedList allows adding elements from multiple threads, but adding from multiple threads will result in an unpredictable order, unrelated to the order of the source list.
The signature of acceptEither looks like this:
public CompletableFuture<Void> acceptEither(
CompletionStage<? extends T> other,
Consumer<? super T> action
) {
And the line which is throwing the error looks like this:
.acceptEither(
timeoutAfter(50, TimeUnit.MILLISECONDS),
inPutItemGroup
)
So you see that you try to pass an ItemGroup as a Consumer<? super T> where T was infered to be Void and hence you get the expected error:
error: incompatible types: ItemGroup cannot be converted to Consumer<? super Void>