scala-java8-compat icon indicating copy to clipboard operation
scala-java8-compat copied to clipboard

Execution context being lost after using FutureConverters

Open syzboy opened this issue 5 years ago • 5 comments

https://github.com/scala/scala-java8-compat/blob/05be2b2c0e56bdda0ca0519ff5ff7e7d0f2a17be/src/main/scala/scala/concurrent/java8/FutureConvertersImpl.scala#L35

After converting a scala future to a java future, the synchronous functions are implemented by calling async ones. And it's not using the ones that are taking the extra executor parameter. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenApplyAsync-java.util.function.Function-

In this case, thread local values will be lost including the execution context

syzboy avatar Feb 26 '20 00:02 syzboy

I have the same issue with async caffeine cache (writen in java) used with scala-java8-compat.

valueFuture.whenComplete((value, error) -> {.  <---- Here we shift to ForkJoinPool.commonPool() because of scala-java8-compat
      if (!completed.compareAndSet(false, true)) {
        // Ignore multiple invocations due to ForkJoinPool retrying on delays
        return;

payurgin avatar Sep 29 '20 20:09 payurgin

Same here. How did you guys circumvent that?

rdesgroppes avatar Nov 05 '21 14:11 rdesgroppes

Did any of you look at this in detail? Would it be an easy fix without unintended consequences?

lrytz avatar Nov 05 '21 14:11 lrytz

@lrytz I guess the fix would consist in taking an Executor at conversion time and propagate it down do each async call. That said, backwards compatibility concerns would lead to either make it optional, defaulting to the current behavior, or introduce a parallel code path. Hum.

rdesgroppes avatar Nov 05 '21 14:11 rdesgroppes

One unfortunate feature of ForkJoinPool.commonPool() is that by default, if a Java process observes < 3 CPUs (e.g. if CPU requests in Kubernetes are set this way), then it will use a ThreadPerTaskExecutor for the common pool. This might lead to creating a lot of threads! All of this completely unexpected, because the application code doesn't specifically invoke ...async methods on CompletionStage. Why don't these methods call the synchronous methods in the API?

baltiyskiy avatar Jun 06 '22 15:06 baltiyskiy