On September 25th we had our monthly meetup, this time about Java CompletableFutures.
CompletableFuture was introduced in Java 8 as a concurrency API improvement to the Future interface (of Java 5) and further improved in Java 9. This class is used for asynchronous programming in Java.
Asynchronous programming is difficult to explain in one sentence. After searching the internet, I found that the following text best explains it in as little words possible.
“Asynchronous programming is a means of writing non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its progress, completion or failure. This way, your main thread does not block/wait for the completion of the task and it can execute other tasks in parallel.“
We started with watching a talk by Venkat Subramaniam he gave at Devoxx Belgium in 2017. The presentation was on Parallel and Asynchronous Programming with Streams and CompletableFuture and was very interesting. We focused on the part where he covered the CompletableFuture class.
After the talk we started with a real world use case I prepared. In short, your service had to return a list of products by calling a downstream service within your organisation. For each row returned you had to call 5 additional services to enrich the data for each product. This sounds like a simple task but as we soon realised knowing what the CompletableFuture class can do for you and applying it in a real world scenario was a totally different story. Conceptually you first have to figure out how you could best solve this. Basically call your first service to return the 10 products for the first page. Now call each of the 5 services in parallel, per product, to ensure you get the best performance.
Our startup project was setup with a mock server backend to consistently return the data for these 6 endpoints. This ensured that we could compare the speed of our solutions afterwards.
When we started I found that each of us quickly had a basic solution calling some services. But when we started calling multiple services, which could also be relying on each other in some way, we had to rewrite some logic quite a bit.
Eventually we had 3 Java solutions and 2 in Kotlin. This was really nice because now we could compare the performance of CompletableFuture to Coroutines. It was also nice to compare the readability of the solutions done in Java vs Kotlin as I find it quite challenging to keep Java code readable when using the CompletableFuture class. Biggest challenge being that people with little knowledge of CompletableFuture should also be able to read and understand the code easily.
One thing I underestimated was the importance of the executors. The executor provides a thread pool in which you can execute the blocking calls. This is very important as you do not want to block the main thread. But this is a topic for a different meetup.
At the end of the day I was really happy with the outcome, as it was nice to see how different people solve the same problem.