Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Future.recover does not catch exceptions that are thrown inside a CompletableFuture #169

Open
mresposito opened this issue Nov 21, 2019 · 1 comment

Comments

@mresposito
Copy link

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?

@mberchon
Copy link

mberchon commented Mar 22, 2022

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 /!\

  1. 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 ?"
  }
  1. 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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants