将未来放在 OnFailure 中

Put future inside OnFailure

我是 scala 的新手,所以要温柔!

我正在尝试创建一个 "logical transaction",这是一个代码示例:

val f1:Future[Int] = dao.insertIntoDB
f1.flatmap{
  x => {
    val f2 = sendHttpRequestFuture
    f.onFailure{
       case t => dao.revertDbChangeFuture
    }
 }

 f1.onFailure{
    logger.error("error)
 }

所以我认为会发生的是先执行外层的onFailure,然后是内层的onFailure。

问题是 onFailure return 单元,所以把 future 放在 onFailure 里面感觉很奇怪,我不确定处理这种用例的正确方法是什么。

我基本上需要还原我的更改,还原操作是异步的。

你能给点建议吗? 谢谢!

你不应该使用 revertDbChangeFuture,因为一旦你有了那个未来,它通常意味着你已经触发了底层计算,即你已经在恢复数据库更改,无论结果如何的插入。 相反,您应该添加 方法 以在 onFailure 回调中恢复数据库更改:

insertIntoDBFuture.onFailure {
    case t => revertDbChange()
}

val f1 = insertIntoDBFuture.flatmap(sendHttpRequestFuture(_))
f1.onFailure{
  case t => logger.error("error", t)
}
f1

如果insertIntoDBFuture失败,val f1 = insertIntoDBFuture.flatmap(sendHttpRequestFuture(_))中的flatMap不会被执行,所以你不用担心。相反,f1 的结果将是相同的失败未来,因此如果没有发生错误,您将记录 insertIntoDBFuturesendHttpRequestFuture - 或 none 的错误。

试用此示例。 它显示了所有的工作原理。 事实上,正如我所见,你已经实施得很好。

有两个变量可以修改 job1Failjob2Fail

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

object FutFut extends App {


  val f1: Future[Int] = Future {
    doJob
  }

  val job1Fail = false
  val job2Fail = true

  def doJob() = {
    if (job1Fail) sys.error("Failed to do job") else {
      println("job 1 done")
      1
    }
  }

  def doOtherJob() {
    if (job2Fail) sys.error("Failed to do other job") else {
      println("other job done")
    }
  }

  def revertingAll() {
    println("reverting all")
  }

  f1.flatMap {
    x => {
      val f2 = Future {
        doOtherJob()
      }
      f2.onFailure {
        case t => revertingAll()
      }
      f2
    }
  }

  f1.onFailure {
    case t => println("f1 failed")
  }

  Thread.sleep(1000)

}

所以结果如下:

db job fail -> output message f1 failed. no revert.

db job ok -> flat map invoked -> http fail -> revert invoked -> f1 does not fail

db job ok -> flat map invoked -> http ok -> all ok -> no revert -> no fail :)

这就是所有用例。

在这种情况下,您可能应该使用 recoverWith API,它允许您将失败的未来转化为另一个未来:

f2.recoverWith({
  case NonFatal(_) => dao.revertDbChangeFuture
})

这将 return 你反对撤消操作的 Future。