Reputation: 42100
Suppose I have a server, which calls a time consuming function slow: Int => String
upon a client request. If slow
does not return within the timeout
the server returns an error to the client.
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)
}
}
Now I'd like to cache the futures so that multiple threads calling trySlow
with the same id
will wait for the same future.
I am going to use a mutable concurrent TrieMap to implement a singleton cache.
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
}
Does it make sense ? How to do it with immutable non-singleton cache ?
Upvotes: 4
Views: 1183
Reputation: 12565
If you want to use only things within scala collections, scala.collection.concurrent.TrieMap is a decent choice. However, please be aware that TrieMap#getOrElseUpdate had a thread safety bug that was only fixed recently in 2.11.6.
If you can afford the additional dependency, guava cache is quite good for writing such caches. Especially if you want entries in the cache to expire in some way.
Regarding the API of your cache: assuming you are talking about pure functions, the cache generator should be just a thing that takes a function T => U and returns a function T => U
So something along these lines:
object Cached {
def apply[T,U](f: T=>U): T=>U = { ??? }
}
Usage:
def slow(id: Int): Try[String] = ??? // something complex including futures, timeouts etc.
val fast: Int => Try[String] = Cached(slow)
The caching API should not have to know anything about the function being cached, other than that you expect it to be pure.
Upvotes: 4
Reputation: 20068
I recommend you use guava libraries in general. (https://code.google.com/p/guava-libraries/)
Like Rüdiger Klaehn mentioned cache is a good place to start.
Upvotes: 1