Future.recover does not catch exceptions that are thrown inside a CompletableFuture
mresposito opened this issue · 1 comments
I've been using the aws dynamodb async sdk, but I find that I cannot catch exceptions after I convert a CompletableFuture
to Scala.
When I run
client
.createTable(tableSchema.createTableRequest(tableName))
.thenApply[Unit](_ => Unit)
.exceptionally {
case _: Throwable => // exception is now property handled
}
I can successfully catch the exception.
However when I run
import scala.compat.java8.FutureConverters.toScala
val cf = client
.createTable(tableSchema.createTableRequest(tableName))
toScala(cf).recover {
case _: Throwable => // never gets executed
}
I cannot actually catch the exception. Any clues on why?
FYI, our team is troubleshooting a similar weird behavior between scala-java8-compat and AWS SDK V2
Context:
- scala 2.12,
- AWS SDK 2.17.138 involving cloudwatch, cloudwatchlogs & aws-crt-client & async http clients (pb occurs with both AwsCrtSdkHttpService/NettySdkAsyncHttpService),
- Java 11 (Correto)
For some unexplained reasons, our code works fine if keeping a CompletableFuture but doesn't work when turning it into a Future with the scala-java8-compat.
/!\ I am not saying there is a bug in scala-java8-compat ... but this issue has been routed to me by the team ... so I am trying to give a bit of reflexion to this issue /!\
- We did the following tests which confirm exceptions are properly catched
test("test future compat recover") {
import scala.compat.java8.FutureConverters.toScala
toScala(CompletableFuture.supplyAsync(() => "Hello")).futureValue shouldBe "Hello"
assertThrows[Exception] {
toScala(CompletableFuture.supplyAsync(() => {
throw new IllegalStateException("Boom")
"Hello" }))
.futureValue
}
//Throwing nonFatal exception
toScala(CompletableFuture.supplyAsync(() => {
throw new IllegalStateException("Boom")
"Hello" }))
.recover { case _:Throwable => "Bim"}
.futureValue shouldBe "Bim"
//Throwing fatal exception (https://stackoverflow.com/questions/29744462/the-difference-between-nonfatal-and-exception-in-scala)
toScala(CompletableFuture.supplyAsync(() => {
throw new LinkageError("Mega Boom")
"Hello" }))
.recover { case _:Throwable => "Bim ?"}
.futureValue shouldBe "Bim ?"
}
- Will update this comment once we flush out the djinn
UPDATE:
Final status in our context:
- our code implementation was sharing the same execution context (scala.concurrent.ExecutionContext.Implicits.global) across several functions (not easily visible during code review since implicitly provided to flatMap, recover ...),
- one function was looping and exhausting the exec ctx,
- so the recover we were expecting to happen in another function ... was never trigger because of no thread available from the exhausted exec ctx.
So definitely not a scala-java8-compat bug but a bug on our side (in our context)