Java CompletableFuture.allOf().whenComplete() with multiple exceptions - java

Problem
In Java Official Doc, it says
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a CompletionException holding this exception as its cause.
The doc doesn't specify the case when multiple given CompletableFutures complete exceptionally. For example, in the following code snippet, what will the exception and its cause be if c1, c2, c3 all complete exceptionally?
CompletableFuture.allOf(c1, c2, c3)
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
My experiment 1
Create a completableFuture signal_1 and signal_2 that both completes exceptionally fast. The output shows signal_1 gets passed to .whenComplete() as the cause of exception.
package com.company;
import java.util.concurrent.*;
public class Main {
private static void runTasks(int i) {
System.out.printf("-- input: %s --%n", i);
CompletableFuture<Void> signal_1 = new CompletableFuture<>();
signal_1.completeExceptionally(new RuntimeException("Oh noes!"));
CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);
CompletableFuture.allOf(signal_1, signal_2)
.thenApplyAsync(justVoid -> {
final int num = signal_2.join();
System.out.println(num);
return num;
})
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
.thenApplyAsync(input -> input * 3)
.thenAccept(System.out::println);
}
public static void main(String[] args) {
runTasks(0);
}
}
Output
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!
Process finished with exit code 0
My experiment 2
Added a 3 second sleep before signal_1 completes exceptionally, so signal_1 should completes after signal_2. However, the output still shows signal_1 gets passed to .whenComplete() as the cause of exception.
package com.company;
import java.util.concurrent.*;
public class Main {
static ExecutorService customExecutorService = Executors.newSingleThreadExecutor();
private static void runTasks(int i) {
System.out.printf("-- input: %s --%n", i);
CompletableFuture<Void> signal_1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException("Oh noes!");
}, customExecutorService);
CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);
CompletableFuture.allOf(signal_1, signal_2)
.thenApplyAsync(justVoid -> {
final int num = signal_2.join();
System.out.println(num);
return num;
})
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
.thenApplyAsync(input -> input * 3)
.thenAccept(System.out::println);
}
public static void main(String[] args) {
runTasks(0);
customExecutorService.shutdown();
}
}
Output
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!
Process finished with exit code 0

This is largely a repeat of what VGR said in the comments, but it is an important rule of thumb that deserves a full write-up.
In Java, there is an important concept called Unspecified Behaviour. In short, if the docs do not explicitly define what happens in a specific scenario, then the implementation is free to do literally whatever it chooses to, within reason and within the bounds of the other rules that are explicitly defined. This is important because there are several different manifestations of that.
For starters, the result could be platform specific. For some machines, leaving the behaviour undefined allows for some special optimizations that still return a "correct" result. And since Java prioritizes both similar/same behaviour on all platforms as well as performance, choosing not to specify certain aspects of execution allows them to stay true to that promise while still getting the optimization benefits that come with specific platforms.
Another example is when the act of unifying behaviour into a specific action is not currently feasible. If I had to guess, this is most likely what Java is actually doing. In certain instances, Java will design a component with the potential for certain functionality, but will stop short of actually defining and implementing it. This is usually done in instances where building out a full blown solution would be more effort than it is worth, amongst other reasons. Ironically enough, CompletableFuture itself is a good example of this. Java 5 introduced Future's, but only as an interface with generic functionality. CompletableFuture, the implmementation which came in Java 8, later fleshed out and defined all the unspecified behaviour left over from the Java 5 interface.
And lastly, they may avoid defining specified behaviour if choosing specified behaviour would stifle the flexibility of possible implementations. Currently, the method you showed does not have any specified behaviour about which exception will be thrown when the futures fail. This allows any class that later extends CompletableFuture to be able to specify that behaviour for themselves while still maintaining Liskov's Substitution Principle. If you don't already know, LSP says that if a Child class extends a Parent class, then that Child class must follow all the rules of the Parent class. As a result, if the rules (specified behaviour) of the class are too restrictive, then you prevent future implementations/extensions of this class from being able to function without breaking LSP. There are likely some extensions for CompletableFuture that allow you to define exactly what type of Exception is thrown when calling the method. But that's the point - they are extensions that you can choose to opt-in to. If they define it for you, then you are stuck with it unless you implement it yourself, or you go outside the languages standard library.

Related

Completable futures. What's the best way to handle business "exceptions"?

I'm just starting to get familiar with the CompletableFuture tool from Java. I've created a little toy application to model some recurrent use case almost any dev would face.
In this example I simply want to save a thing in a DB, but before doing so I want to check if the thing was already saved.
If the thing is already in the DB the flow (the chain of completable futures) should stop and not save the thing. What I'm doing is throwing an exception so eventually I can handle it and give a good message to the client of the service so he can know what happened.
This is what I've tried so far:
First the code that try to save the thing or throw an error if the thing is already in the table:
repository
.query(thing.getId())
.thenCompose(
mayBeThing -> {
if (mayBeThing.isDefined()) throw new CompletionException(new ThingAlreadyExists());
else return repository.insert(new ThingDTO(thing.getId(), thing.getName()));
And this is the test I'm trying to run:
CompletableFuture<Integer> eventuallyMayBeThing =
service.save(thing).thenCompose(i -> service.save(thing));
try {
eventuallyMayBeThing.get();
} catch (CompletionException ce) {
System.out.println("Completion exception " + ce.getMessage());
try {
throw ce.getCause();
} catch (ThingAlreadyExist tae) {
assert (true);
} catch (Throwable t) {
throw new AssertionError(t);
}
}
This way of doing it I took it from this response: Throwing exception from CompletableFuture ( the first part of the most voted answer ).
However, this is not working. The ThingAlreadyExist is being thrown indeed but it's never being handled by my try catch block.
I mean, this:
catch (CompletionException ce) {
System.out.println("Completion exception " + ce.getMessage());
try {
throw ce.getCause();
} catch (ThingAlreadyExist tae) {
assert (true);
} catch (Throwable t) {
throw new AssertionError(t);
}
is never executed.
I have 2 questions,
Is there a better way?
If not, am I missing something? Why can't I handle the exception in my test?
Thanks!
Update(06-06-2019)
Thanks VGR you are right. This is the code working:
try {
eventuallyMayBeThing.get();
} catch (ExecutionException ce) {
assertThat(ce.getCause(), instanceOf(ThingAlreadyExists.class));
}
By unit testing your code wrapped up in a Future, you’re testing java’s Future framework. You shouldn’t test libraries - you either trust them or you don’t.
Instead, test that your code, in isolation, throws the right exceptions when it should. Break out the logic and test that.
You can also integration test your app to assert that your entire app behaves correctly (regardless of implementation).
You have to be aware of the differences between get() and join().
The method get() is inherited from the Future interface and will wrap exceptions in an ExecutionException.
The method join() is specific to CompletableFuture and will wrap exceptions in a CompletionException, which is an unchecked exception, which makes it more suitable for the functional interfaces which do not declare checked exceptions.
That being said, the linked answer addresses use cases where the function has to do either, return a value or throw an unchecked exception, whereas your use case involves compose, where the function will return a new CompletionStage. This allows an alternative solution like
.thenCompose(mayBeThing -> mayBeThing.isDefined()?
CompletableFuture.failedFuture​(new ThingAlreadyExists()):
repository.insert(new ThingDTO(thing.getId(), thing.getName())))
CompletableFuture.failedFuture has been added in Java 9. If you still need Java 8 support, you may add it to your code base
public static <T> CompletableFuture<T> failedFuture(Throwable t) {
final CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(t);
return cf;
}
which allows an easy migration to a newer Java version in the future.

Deadlocks with java.util.concurrent._ in Scala in REPL

I came across the following scenario when studying the book "Functional Programming in Scala" by Paul Chiusano and Runar Bjanarson (Ch. 7 - Purely functional parallelism).
package fpinscala.parallelism
import java.util.concurrent._
import language.implicitConversions
object Par {
type Par[A] = ExecutorService => Future[A]
def run[A](s: ExecutorService)(a: Par[A]): Future[A] = a(s)
def unit[A](a: A): Par[A] = (es: ExecutorService) => UnitFuture(a) // `unit` is represented as a function that returns a `UnitFuture`, which is a simple implementation of `Future` that just wraps a constant value. It doesn't use the `ExecutorService` at all. It's always done and can't be cancelled. Its `get` method simply returns the value that we gave it.
private case class UnitFuture[A](get: A) extends Future[A] {
def isDone = true
def get(timeout: Long, units: TimeUnit) = get
def isCancelled = false
def cancel(evenIfRunning: Boolean): Boolean = false
}
def map2[A,B,C](a: Par[A], b: Par[B])(f: (A,B) => C): Par[C] = // `map2` doesn't evaluate the call to `f` in a separate logical thread, in accord with our design choice of having `fork` be the sole function in the API for controlling parallelism. We can always do `fork(map2(a,b)(f))` if we want the evaluation of `f` to occur in a separate thread.
(es: ExecutorService) => {
val af = a(es)
val bf = b(es)
UnitFuture(f(af.get, bf.get)) // This implementation of `map2` does _not_ respect timeouts. It simply passes the `ExecutorService` on to both `Par` values, waits for the results of the Futures `af` and `bf`, applies `f` to them, and wraps them in a `UnitFuture`. In order to respect timeouts, we'd need a new `Future` implementation that records the amount of time spent evaluating `af`, then subtracts that time from the available time allocated for evaluating `bf`.
}
def fork[A](a: => Par[A]): Par[A] = // This is the simplest and most natural implementation of `fork`, but there are some problems with it--for one, the outer `Callable` will block waiting for the "inner" task to complete. Since this blocking occupies a thread in our thread pool, or whatever resource backs the `ExecutorService`, this implies that we're losing out on some potential parallelism. Essentially, we're using two threads when one should suffice. This is a symptom of a more serious problem with the implementation, and we will discuss this later in the chapter.
es => es.submit(new Callable[A] {
def call = a(es).get
})
def lazyUnit[A](a: => A): Par[A] = fork(unit(a))
def equal[A](e: ExecutorService)(p: Par[A], p2: Par[A]): Boolean =
p(e).get == p2(e).get
}
You can find the original code on Github here. See here for the java.util.concurrent documentation.
I am concerned with the implementation of fork. In particular, allegedly fork can lead to deadlocks when the ThreadPool is too small.
I consider the following example:
val a = Par.lazyUnit(42 + 1)
val es: ExecutorService = Executors.newFixedThreadPool(2)
println(Par.fork(a)(es).get)
I would not expect this example to end up in a deadlock as there are two threads. Yet, it does on my computer when I run it in the Scala REPL. Why is this so?
The output when initializing the ExecutorService is
es: java.util.concurrent.ExecutorService =
java.util.concurrent.ThreadPoolE
xecutor#73a86d72[Running, pool size = 0, active threads = 0, queued tasks =
0, completed tasks = 0]
Is pool size = 0 correct here? In other word, is this a problem of not understanding java.util.concurrent._ or is the issue with not understanding the Scala part?
OK, after a long investigation I believe I have an answer. The full story is long but I'll try to shorten it by simplifying and avoiding many details.
Note: Potentially Scala can be compiled to various different target platforms but this particular issue happened on the Java/JVM as the target so this is what this answer is about.
The deadlock you see has nothing to do with the size of the thread pool. Actually it is the outer fork call that hangs. It is related to a combination of REPL implementation details and multi-threading but it takes learning a few pieces to understand how it happens:
how Scala REPL works
how Scala compiles objects to Java/JVM
how Scala emulates the by-name parameters on Java/JVM
how Java/JVM runs the static initializers of classes
A short(er) version (see also Summary at the end) is that this code hangs under a REPL because when it is being executed by the REPL, it is logically similar to the following code:
object DeadLock {
import scala.concurrent._
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
val foo: Int = Await.result(Future(calc()), Duration.Inf)
def printFoo(): Unit = {
println(s"Foo = $foo")
}
private def calc(): Int = {
println("Before calc")
42
}
}
def test(): Unit = {
println("Before printFoo")
DeadLock.printFoo()
println("After printFoo")
}
or very similar in the Java world:
class Deadlock {
static CompletableFuture<Integer> cf;
static int foo;
public static void printFoo() {
System.out.println("Print foo " + foo);
}
static {
cf = new CompletableFuture<Integer>();
new Thread(new Runnable() {
#Override
public void run() {
calcF();
}
}).start();
try {
foo = cf.get();
System.out.println("Future result = " + cf.get());
} catch (InterruptedException e) {
e.printStackTrace();f
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private static void calcF() {
cf.complete(42);
}
}
public static void main(String[] args) {
System.out.println("Before foo");
Deadlock.printFoo();
System.out.println("After foo");
}
If it is clear to you why this code deadlocks, you already know most of the story and probably can deduce the rest yourself. You might just glance over the Summary section at the end.
How Java static initializer can deadlock?
Let's start from the end of this story: why the Java code hangs? It happens because of the two guarantees of the Java/JVM for the static initializer (for more details see the section 12.4.2. Detailed Initialization Procedure of the JLS):
the static initializer will be run before any other "external" use of the class
the static initializer will be run exactly once and it is done via global locking
The lock used for the static initializer is implicit and managed by the JVM but it is there. It means that the code logically is similar to something like this:
class Deadlock {
static boolean staticInitFinished = false;
// unique value for each thread!
static ThreadLocal<Boolean> currentThreadRunsStaticInit = ThreadLocal.withInitial(() -> Boolean.FALSE);
static CompletableFuture<Integer> cf;
static int foo;
static void enforceStaticInit() {
synchronized (Deadlock.class) {
// is init finished?
if (staticInitFinished)
return;
// are we the thread already running the init?
if(currentThreadRunsStaticInit.get())
return;
currentThreadRunsStaticInit.set(true);
cf = new CompletableFuture<Integer>();
new Thread(new Runnable() {
#Override
public void run() {
calcF();
}
}).start();
try {
foo = cf.get();
System.out.println("Future result = " + cf.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
currentThreadRunsStaticInit.set(false);
staticInitFinished = true;
}
}
private static void calcF() {
enforceStaticInit();
cf.complete(42);
}
public static void printFoo() {
enforceStaticInit();
System.out.println("Print foo " + foo);
}
}
Now it is pretty clear why this code deadlocks: Our static initializer starts a new thread and blocks waiting for result from it. But that new thread tries to access the same class (the calcF method) and being another thread it has to wait for the already running static initializer to finish. Note that if the calcF method was in another class, everything would work just fine.
How Scala REPL works
Now let's get back to the start of the story on how Scala REPL works. This answer is a great simplification of the real deal but it captures the important for this situation details. Luckily for the REPL implementors the Scala compiler is written in Scala. It means the REPL doesn't have to somehow interpret the code, it can run it through the standard compiler and then run the compiled code via Java Reflection API. This still takes some decoration of the code to make the compiler happy and to get the results back.
Simplifying it a bit (or well, a lot), when you type something like
val a = Par.lazyUnit(42 + 1)
into the REPL the code is analyzed and transformed into something like this:
package line3
object read {
val a = Par.lazyUnit(42 + 1)
val res3 = a
}
object eval {
def print() = {
println("a: Par.Par[Int] = " + read.res3)
}
}
and then line3.eval.print() is called via reflection.
A similar story happens for:
val es: ExecutorService = Executors.newFixedThreadPool(2)
and finally when you do
Par.fork(a)(es).get
the things get a bit more interesting because you have a dependency on the previous lines that is cleverly implemented using imports:
package line5
object read {
import line2.read.Par
import line3.read.a
import line4.read.es
val res5 = Par.fork(a)(es).get
}
object eval {
def print() = {
println("res5: Int = " + read.res5)
}
}
The important thing here is that everything you write into REPL is wrapped into a brand new object and then compiled and run as a usual code.
How Scala emulates by-name parameters on Java/JVM
The definition of the fork method uses a by-name parameter:
def fork[A](a: => Par[A]): Par[A] =
Here it is used to evaluate a lazily that is crucial to the whole logic of the fork. The Java/JVM has not standard support for lazy evaluation but it can be emulated and this is what the Scala compiler does. Internally the signature is changed to use a Function0:
def fork[A](aWrapper: () => Par[A]): Par[A] =
and every access to a is substituted with a call to aWrapper.apply(). Another part of the magic happens on the caller-side of a method with a by-name parameter: there the parameter should also be wrapped into a Function0 so code becomes something like
object read {
import line2.read.Par
import line3.read.a
import line4.read.es
val res5 = Par.fork(() => a)(es).get
}
But actually it is a bit different. Naively it would take another class just for this small function and this feels wasteful for such a simple logic. In practice in the Scala 2.12 the magic of the Java 8 LambdaMetafactory is used so the code really becomes something like
object read {
import line2.read.Par
import line3.read.a
import line4.read.es
def aWrapper():Int = a
val res5 = Par.fork(aWrapper _)(es).get
}
where aWrapper _ signifies converting a method to a Funciton0 that is done with the LambdaMetafactory. As you might suspect from the chapter on the Java static initializer deadlock, introduction of the def aWrapper is a crucial difference. You already can see that this code is very similar to the first Scala snippet in the answer that hangs.
How Scala compiles the object on Java/JVM
The final piece of the puzzle is how Scala object is compiled in Java/JVM. Well it is actually compiled to something similar to a "static class" but since you can use object as an object parameter, it has to be a bit more complicated. In reality all the initialization logic is moved to the constructor of the object class and there is simple static initializer that calls it. So our last read object in Java would (ignoring the imports) look like this:
class read$ {
static read$ MODULE$
static {
new read$()
}
private Par[Int] res5;
private read$() {
MODULE$ = this;
res5 = Par.fork(read$::aWrapper)(es).get
}
private static int aWrapper(){
return line3.read$.MODULE$.a;
}
}
here again read$::aWrapper signifies building a Function0 form the aWrapper method using the LambdaMetafactory. In other words the initialization of the Scala object is translated into a code that is run as a part of the Java static initializer.
Summary
To sum up how things get screwed:
REPL turns your code into a new object for each line and compiles it
object initialization logic is translated into a Java static initialization logic
call of a method with a by-name parameter in simple cases is translated into a method that wraps the "return the value" logic and that method is added to the same class or object
Par.fork being executed as a part of the object initialization (i.e. a part of the Java static initializer) tries to evaluate the by-name parameter (i.e. calls the method on the same class) on a different thread and blocks waiting for the result of that thread
Java static initializer is logically executed under a global lock so it blocks that different thread calling the method. But it is itself blocked waiting for that method call to finish.

Closing external process in CompletableFuture chain

I'm looking for better way to "close" some resource, here destroy external Process, in CompletableFuture chain. Right now my code looks roughly like this:
public CompletableFuture<ExecutionContext> createFuture()
{
final Process[] processHolder = new Process[1];
return CompletableFuture.supplyAsync(
() -> {
try {
processHolder[0] = new ProcessBuilder(COMMAND)
.redirectErrorStream(true)
.start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return PARSER.parse(processHolder[0].getInputStream());
}, SCHEDULER)
.applyToEither(createTimeoutFuture(DURATION), Function.identity())
.exceptionally(throwable -> {
processHolder[0].destroyForcibly();
if (throwable instanceof TimeoutException) {
throw new DatasourceTimeoutException(throwable);
}
Throwables.propagateIfInstanceOf(throwable, DatasourceException.class);
throw new DatasourceException(throwable);
});
}
The problem I see is a "hacky" one-element array which holds reference to the process, so that it can be closed in case of error. Is there some CompletableFuture API which allows to pass some "context" to exceptionally (or some other method to achieve that)?
I was considering custom CompletionStage implementation, but it looks like a big task to get rid of "holder" variable.
There is no need to have linear chain of CompletableFutures. Well actually, you already haven’t due to the createTimeoutFuture(DURATION) which is quite convoluted for implementing a timeout. You can simply put it this way:
public CompletableFuture<ExecutionContext> createFuture() {
CompletableFuture<Process> proc=CompletableFuture.supplyAsync(
() -> {
try {
return new ProcessBuilder(COMMAND).redirectErrorStream(true).start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, SCHEDULER);
CompletableFuture<ExecutionContext> result
=proc.thenApplyAsync(process -> PARSER.parse(process.getInputStream()), SCHEDULER);
proc.thenAcceptAsync(process -> {
if(!process.waitFor(DURATION, TimeUnit.WHATEVER_DURATION_REFERS_TO)) {
process.destroyForcibly();
result.completeExceptionally(
new DatasourceTimeoutException(new TimeoutException()));
}
});
return result;
}
If you want to keep the timout future, perhaps you consider the process startup time to be significant, you could use
public CompletableFuture<ExecutionContext> createFuture() {
CompletableFuture<Throwable> timeout=createTimeoutFuture(DURATION);
CompletableFuture<Process> proc=CompletableFuture.supplyAsync(
() -> {
try {
return new ProcessBuilder(COMMAND).redirectErrorStream(true).start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, SCHEDULER);
CompletableFuture<ExecutionContext> result
=proc.thenApplyAsync(process -> PARSER.parse(process.getInputStream()), SCHEDULER);
timeout.exceptionally(t -> new DatasourceTimeoutException(t))
.thenAcceptBoth(proc, (x, process) -> {
if(process.isAlive()) {
process.destroyForcibly();
result.completeExceptionally(x);
}
});
return result;
}
I've used the one item array myself to emulate what would be proper closures in Java.
Another option is using a private static class with fields. The advantages are that it makes the purpose clearer and has a bit less impact on the garbage collector with big closures, i.e. an object with N of fields versus N arrays of length 1. It also becomes useful if you need to close over the same fields in other methods.
This is a de facto pattern, even outside the scope of CompletableFuture and it has been (ab)used long before lambdas were a thing in Java, e.g. anonymous classes. So, don't feel so bad, it's just that Java's evolution didn't provide us with proper closures (yet? ever?).
If you want, you may return values from CompletableFutures inside .handle(), so you can wrap the completion result in full and return a wrapper. In my opinion, this is not any better than manual closures, added the fact that you'll create such wrappers per future.
Subclassing CompletableFuture is not necessary. You're not interested in altering its behavior, only in attaching data to it, which you can do with current Java's final variable capturing. That is, unless you profile and see that creating these closures is actually affecting performance somehow, which I highly doubt.

Testing thread safety fails with Spock

The subject
I have some code that is decidedly not thread safe:
public class ExampleLoader
{
private List<String> strings;
protected List<String> loadStrings()
{
return Arrays.asList("Hello", "World", "Sup");
}
public List<String> getStrings()
{
if (strings == null)
{
strings = loadStrings();
}
return strings;
}
}
Multiple threads accessing getStrings() simultaneously are expected to see strings as null, and thus loadStrings() (which is an expensive operation) is triggered multiple times.
The problem
I wanted to make the code thread safe, and as a good citizen of the world I wrote a failing Spock spec first:
def "getStrings is thread safe"() {
given:
def loader = Spy(ExampleLoader)
def threads = (0..<10).collect { new Thread({ loader.getStrings() })}
when:
threads.each { it.start() }
threads.each { it.join() }
then:
1 * loader.loadStrings()
}
The above code creates and starts 10 threads that each calls getStrings(). It then asserts that loadStrings() was called only once when all threads are done.
I expected this to fail. However, it consistently passes. What?
After a debugging session involving System.out.println and other boring things, I found that the threads are indeed asynchronous: their run() methods printed in a seemingly random order. However, the first thread to access getStrings() would always be the only thread to call loadStrings().
The weird part
Frustrated after quite some time spent debugging, I wrote the same test again with JUnit 4 and Mockito:
#Test
public void getStringsIsThreadSafe() throws Exception
{
// given
ExampleLoader loader = Mockito.spy(ExampleLoader.class);
List<Thread> threads = IntStream.range(0, 10)
.mapToObj(index -> new Thread(loader::getStrings))
.collect(Collectors.toList());
// when
threads.forEach(Thread::start);
threads.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// then
Mockito.verify(loader, Mockito.times(1))
.loadStrings();
}
This test consistently fails due to multiple calls to loadStrings(), as was expected.
The question
Why does the Spock test consistently pass, and how would I go about testing this with Spock?
The cause of your problem is that Spock makes methods it spies on synchronized. Particularly, the method MockController.handle(), through which all such calls go, is synchronized. You'll easily notice it if you add a pause and some output to your getStrings() method.
public List<String> getStrings() throws InterruptedException {
System.out.println(Thread.currentThread().getId() + " goes to sleep");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getId() + " awoke");
if (strings == null) {
strings = loadStrings();
}
return strings;
}
This way Spock inadvertently fixes your concurrency problem. Mockito obviously uses another approach.
A couple of other thoughts on your tests:
First, you don't do much to ensure that all your threads have come to the getStrings() call at the same moment, thus decreasing the probability of collisions. Long time may pass between threads start (long enough for the first one to complete the call before others start it). A better approach would
be to use some synchronization primitive to remove the influence of threads startup time. For instance, a CountDownLatch may be of use here:
given:
def final CountDownLatch latch = new CountDownLatch(10)
def loader = Spy(ExampleLoader)
def threads = (0..<10).collect { new Thread({
latch.countDown()
latch.await()
loader.getStrings()
})}
Of course, within Spock it will make no difference, it's just an example on how to do it in general.
Second, the problem with concurrency tests is that they never guarantee that your program is thread safe. The best you can hope for is that such test will show you that your program is broken. But even if the test passes, it doesn't prove thread safety. To increase chances of finding concurrency bugs you may want to run the test many times and gather statistics. Sometimes, such tests only fail once in several thousands or even once in several hundreds thousands of runs. Your class is simple enough to make guesses about its thread safety, but such approach will not work with more complicated cases.

CompletionStage: Return a CompletionStage within exceptionally block [duplicate]

I don't see an obvious way to handle an exception with an asynchronous result.
For example, if I want to retry an async operation, I would expect something like this:
CompletionStage<String> cf = askPong("cause error").handleAsync((x, t) -> {
if (t != null) {
return askPong("Ping");
} else {
return x;
}
});
Where askPong asks an actor:
public CompletionStage<String> askPong(String message){
Future sFuture = ask(actorRef, message, 1000);
final CompletionStage<String> cs = toJava(sFuture);
return cs;
}
However handleAsync doesn't do what you think it does - it runs the callbacks on another thread asynchronously. Returning a CompletionStage here is not correct.
Jeopardy question of the day: thenApply is to thenCompose as exceptionally is to what?
Is this what you are looking for?
askPong("cause error")
.handle( (pong, ex) -> ex == null
? CompletableFuture.completedFuture(pong)
: askPong("Ping")
).thenCompose(x -> x);
Also, do not use the ...Async methods unless you intend for the body of the supplied function to be executed asynchronously. So when you do something like
.handleAsync((x, t) -> {
if (t != null) {
return askPong("Ping");
} else {
return x;
})
You are asking for the if-then-else to be run in a separate thread. Since askPong returns a CompletableFuture, there's probably no reason to run it asynchronously.
Jeopardy question of the day: thenApply is to thenCompose as exceptionally is to what?
I know this was initially java-8, but, since java-12, the answer would be exceptionallyCompose:
exceptionallyCompose[Async](Function<Throwable,? extends CompletionStage<T>> fn [, Executor executor])
Returns a new CompletionStage that, when this stage completes exceptionally, is composed using the results of the supplied function applied to this stage's exception.
As the JavaDoc indicates, the default implementation is:
return handle((r, ex) -> (ex == null)
? this
: fn.apply(ex))
.thenCompose(Function.identity());
That is, using handle() to call the fallback, and thenCompose() to unwrap the resulting nested CompletableFuture<CompletableFuture<T>> – i.e., what you would have done in previous versions of Java (like in Misha’s answer), except you would have to replace this with completedFuture(r).
After a lot of frustration in trying to figure out the proper way of doing Scala's recoverWith in Java 8, I ended up just writing my own. I still don't know if this is the best approach, but I created something like:
public RecoveryChainAsync<T> recoverWith(Function<Throwable,
CompletableFuture<T>> fn);
With repeated calls to recoverWith, I queue up the functions inside the recovery chain and implement the recovery flow myself with "handle". RecoveryChainAsync.getCompletableFuture() then returns a representative CompletableFuture for the entire chain. Hope this helps.

Categories