我的目标是向现有的喷雾缓存实例添加元素。没有任何明显的原因或错误就不会发生这种情况。
我的方法是使用可用的scala-future api来获得中间结果和可用的scala-futures映射方法(map,foreach)到" unpack"期货产生的数据。
这里的代码显示了我的方法(它已经完成,可以立即测试):
import akka.actor.ActorSystem
import spray.caching.LruCache
import scala.util.{Failure, Success}
case class Cached(elements: List[Int])
object TestApp extends App {
//ignore these, just so that cache gets some execution context
implicit val system = ActorSystem("TestApp")
implicit def dispatcher = system.dispatcher
val cache = LruCache[Cached]()
val cacheKey = "test"
cache(cacheKey) {
new Cached(List(1, 2, 3, 4))
}
def merge(key: String, mergeList: List[Int]) = {
// get the existing cache content
cache.get(key).foreach { cachedFuture =>
// wait until it is actually returned
cachedFuture.onComplete {
// merge extracted elements with mergeList
case Success(cached) =>
val mergedCache = cached.elements ++ mergeList
// before re-constructing cache element, remove it from spray-cache
cache.remove(key).foreach { removeFuture =>
// as soon as remove is complete
removeFuture.onComplete {
// re-construct cache element
case Success(_) =>
// actually reconstructing cache element with the key
cache(key) { new Cached(mergedCache) }
case Failure(ex) => println(ex)
}
}
case Failure(ex) => println(ex)
}
}
}
// merge new element 5
merge(cacheKey, List(5))
cache.get(cacheKey).map(_.onComplete {
case Success(cached) =>
// new cache should contain 1, 2, 3, 4, 5
println(s"Cache content is: ${cached.elements.mkString("; ")}")
sys.exit(0)
case Failure(ex) => println(ex); sys.exit(0)
})
}
答案 0 :(得分:0)
以下是解决上述代码问题的有效解决方案。
我特别改编了jrudolph的一些建议
我无法解决的是使用 Await.result(...)使 def merge 与"同步显示外面的世界"。
关于jrudolph关于使用 Map 而不是 LruCache 的评论,他主要是正确的。在我的特定用例中,我选择了 LruCache ,因为实际的应用程序只在Cache中放置了几个键,但元素:List [Int] 包含在缓存中实际上是一个庞大的复杂集合,如果应用程序在某些时候添加了太多密钥,它应该被驱逐。
import akka.actor.ActorSystem
import spray.caching.LruCache
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.util.{Failure, Success}
case class Cached(elements: List[Int])
object TestApp extends App {
//ignore these, just so that cache gets some execution context
implicit val system = ActorSystem("TestApp")
implicit def dispatcher = system.dispatcher
val cache = LruCache[Cached]()
val cacheKey = "test"
cache(cacheKey) {
new Cached(List(1, 2, 3, 4))
}
def merge(key: String, mergeList: List[Int]) = {
// get the existing cache content
val mergeResult: Future[Cached] = cache.get(key) match {
case Some(cacheFuture) => cacheFuture.flatMap { case cached =>
recreateCacheKey(key, Cached(cached.elements ++ mergeList))
}
case None => recreateCacheKey(key, Cached(mergeList))
}
// await result so that users of the def merge get impression that this def is synchronous
Await.result(mergeResult, 5 seconds)
}
// synchronized should eliminate issue with thread-safety of cache modifications
private def recreateCacheKey(cacheKey: String, newCached: Cached) = synchronized {
val cacheRecreationFuture = cache.remove(cacheKey)
.fold(cache(cacheKey) { newCached })(_.flatMap(removedFuture => cache(cacheKey, () => Future { newCached })))
cacheRecreationFuture
}
private def printCacheContent = {
cache.get(cacheKey).map(_.onComplete {
case Success(cached) =>
// new cache should contain 1, 2, 3, 4, 5
println(s"Cache content is: ${cached.elements.mkString("; ")}")
sys.exit(0)
case Failure(ex) => println(ex); sys.exit(0)
})
}
/*****************/
/* EXECUTE STUFF */
/*****************/
// merge new element 5
merge(cacheKey, List(5))
printCacheContent
}