如何在 Scala 中实现不可变缓存?
How to implement immutable cache in Scala?
假设我有一个服务器,它根据客户端请求调用一个耗时的函数slow: Int => String
。如果 slow
不在 timeout
内 return,则服务器 return 向客户端发送一个错误。
def trySlow(timeout: Duration)(id: Int): Try[String] = {
val fut = Future(slow(id))
try {
Await.ready(fut, timeout).value match {
case Some(r) => r
case None => Failure(new TimeoutException()) // should not happen
}
} catch {
case e: TimeoutException => Failure(e)
}
}
现在我想缓存未来,以便使用相同 id
调用 trySlow
的多个线程将等待相同的未来。
我将使用可变并发TrieMap来实现单例缓存。
case class CacheEntry (
future: Future[String],
start: Long = System.currentTimeMillis() // need it to clean the cache
)
type Cache = TrieMap[Int, CacheEntry]
def trySlow(timeout: Duration, cache: Cache)(id: Int): Try[String] = {
val fut = cache.getOrElseUpdate(id, CacheEntry(Future(slow(id))))
... // as in above
}
有意义吗?如何使用 immutable non-singleton cache ?
如果您只想使用 scala 集合中的东西,scala.collection.concurrent.TrieMap 是一个不错的选择。但是,请注意 TrieMap#getOrElseUpdate 有一个 thread safety bug 最近才在 2.11.6 中修复。
如果你能负担得起额外的依赖,guava cache 非常适合编写此类缓存。特别是如果您希望缓存中的条目以某种方式过期。
关于缓存的 API:假设您谈论的是纯函数,缓存生成器应该只是一个接受函数 T => U 和 returns 函数 T = 的东西> 你
所以按照这些思路:
object Cached {
def apply[T,U](f: T=>U): T=>U = { ??? }
}
用法:
def slow(id: Int): Try[String] = ??? // something complex including futures, timeouts etc.
val fast: Int => Try[String] = Cached(slow)
缓存 API 不需要知道任何有关被缓存的函数的信息,除了您希望它是纯函数之外。
一般情况下,我建议您使用 guava 库。
(https://code.google.com/p/guava-libraries/)
就像 Rüdiger Klaehn 提到的那样,缓存 是一个很好的起点。
假设我有一个服务器,它根据客户端请求调用一个耗时的函数slow: Int => String
。如果 slow
不在 timeout
内 return,则服务器 return 向客户端发送一个错误。
def trySlow(timeout: Duration)(id: Int): Try[String] = {
val fut = Future(slow(id))
try {
Await.ready(fut, timeout).value match {
case Some(r) => r
case None => Failure(new TimeoutException()) // should not happen
}
} catch {
case e: TimeoutException => Failure(e)
}
}
现在我想缓存未来,以便使用相同 id
调用 trySlow
的多个线程将等待相同的未来。
我将使用可变并发TrieMap来实现单例缓存。
case class CacheEntry (
future: Future[String],
start: Long = System.currentTimeMillis() // need it to clean the cache
)
type Cache = TrieMap[Int, CacheEntry]
def trySlow(timeout: Duration, cache: Cache)(id: Int): Try[String] = {
val fut = cache.getOrElseUpdate(id, CacheEntry(Future(slow(id))))
... // as in above
}
有意义吗?如何使用 immutable non-singleton cache ?
如果您只想使用 scala 集合中的东西,scala.collection.concurrent.TrieMap 是一个不错的选择。但是,请注意 TrieMap#getOrElseUpdate 有一个 thread safety bug 最近才在 2.11.6 中修复。
如果你能负担得起额外的依赖,guava cache 非常适合编写此类缓存。特别是如果您希望缓存中的条目以某种方式过期。
关于缓存的 API:假设您谈论的是纯函数,缓存生成器应该只是一个接受函数 T => U 和 returns 函数 T = 的东西> 你
所以按照这些思路:
object Cached {
def apply[T,U](f: T=>U): T=>U = { ??? }
}
用法:
def slow(id: Int): Try[String] = ??? // something complex including futures, timeouts etc.
val fast: Int => Try[String] = Cached(slow)
缓存 API 不需要知道任何有关被缓存的函数的信息,除了您希望它是纯函数之外。
一般情况下,我建议您使用 guava 库。 (https://code.google.com/p/guava-libraries/)
就像 Rüdiger Klaehn 提到的那样,缓存 是一个很好的起点。