scala - 缓存并重新加载未来将变得无效
scala - cache and reload a future which will become invalid later
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
}
}
}
}
我正在实现一个只缓存 valid
未来值的 Future
存储器。
It must meet following requirements
Futures
created by f
never executed paralleled
get
never return a invalid value (once invalid
recall f()
to reload)
我的实现是否正确?
是否有更多functional
或更简单的方法来做到这一点(因为我很难证明 mime 的正确性)?
据我了解这是错误的:
p.completeWith(f())
调用者得到一个未来,它的值是(或有时会是)f()
返回的未来的值,但它不会在任何地方检查这个值满足或将满足 valid(...)
;如果需要时间,则在 f()
返回的结果未来正在进行时,对于其他来电者来说也是如此。只有当 f()
的结果完成时,下一个调用者才可能开始 "fixing" 它。
我可能会通过以下方式解决此问题(请参阅 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
}
}
至于你关于更实用的方法的问题,我 猜测 f
和 valid
对外部状态有影响并基于它们的计算在它上面(我想这是让存储器失效的意义所在)会严重阻碍它。
刚发现spray-cache已经有这个功能
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
}
}
}
}
我正在实现一个只缓存 valid
未来值的 Future
存储器。
It must meet following requirements
Futures
created byf
never executed paralleledget
never return a invalid value (onceinvalid
recallf()
to reload)
我的实现是否正确?
是否有更多functional
或更简单的方法来做到这一点(因为我很难证明 mime 的正确性)?
据我了解这是错误的:
p.completeWith(f())
调用者得到一个未来,它的值是(或有时会是)f()
返回的未来的值,但它不会在任何地方检查这个值满足或将满足 valid(...)
;如果需要时间,则在 f()
返回的结果未来正在进行时,对于其他来电者来说也是如此。只有当 f()
的结果完成时,下一个调用者才可能开始 "fixing" 它。
我可能会通过以下方式解决此问题(请参阅 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
}
}
至于你关于更实用的方法的问题,我 猜测 f
和 valid
对外部状态有影响并基于它们的计算在它上面(我想这是让存储器失效的意义所在)会严重阻碍它。
刚发现spray-cache已经有这个功能