scala - 缓存并重新加载将在以后变为无效的未来

时间:2015-05-16 14:30:08

标签: scala concurrency future

  private class FutMemorizer[T](valid: T => Boolean)(f: () => Future[T]) {

    private val ref = new AtomicReference[Promise[T]]

    @scala.annotation.tailrec
    final def get(): Future[T] = {
      val nullableRef = ref.get()
      val valid = checkPromise(ref.get())
      if(valid) {
        nullableRef.future
      } else {
        val p = Promise[T]
        val success = ref.compareAndSet(nullableRef, p)
        if(success) {
          p.completeWith(f())
          p.future
        } else {
          get()
        }
      }
    }

    private def checkPromise(nullable: Promise[T]) = {
      nullable != null && {
        nullable.future.value match {
          case None => true // future is not complete all caller should wait
          case Some(Success(v)) => valid(v)
          case _ => false
        }
      }
    }
  }

我正在实施一个Future存储器,只能缓存valid未来值。

  

必须符合以下要求

     
      由Futures创建的
  1. f从未执行并行
  2.   
  3. get永远不会返回无效值(一旦invalid召回f()重新加载)
  4.   

我的实施是否正确?

是否有更多functional或更简单的方法来执行此操作(因为我几乎不能证明mime的正确性)?

2 个答案:

答案 0 :(得分:0)

据我所知,这是错误的:

p.completeWith(f())

调用者获得的未来的值是{或者有时会是f()返回的未来的值,但是在该值满足或将满足valid(...)的任何地方都不会被检查;如果需要时间,f()返回的结果未来正在进行中的其他呼叫者也是如此。只有当f()的结果完成时,下一个调用者才可能开始“修复”它。

我可能会通过以下方式解决此问题(请参阅fixed方法),并进行一些风格更改:

class FutMemorizer[T](valid: T => Boolean)(f: () => Future[T]) {

  private val ref = new AtomicReference[Future[T]]

  @tailrec
  final def get: Future[T] = {
    val current = ref.get
    if (current != null && isValid(current)) current
    else {
      val p = Promise[T]
      val pf = p.future
      if (ref.compareAndSet(current, pf)) {
        p.completeWith(fixed(f))
        pf
      } else get
    }
  }

  private def fixed(f: () => Future[T]): Future[T] =
    f() flatMap { t =>
      if (valid(t)) Future.successful(t) else fixed(f)
    }

  private def isValid(future: Future[T]) = 
    future.value match {
      case None => true // future is not complete all caller should wait
      case Some(Success(v)) => valid(v)
      case _ => false
    }
}

至于你关于更实用的方法的问题,我猜测 fvalid对外部状态产生影响并将其计算基于它(我是猜测是否存在失效的存储器会严重阻碍它。

答案 1 :(得分:0)

找到spray-cache已经有此功能