How do I implement an immutable cache in Scala?

Suppose I have a server that calls a time function slow: Int => String

when requested by a client. If slow

not returned within 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 would like to cache futures so that multiple threads calling trySlow

with the same id

will wait for the same future.

I am going to use mutable concurrent TrieMap to implement 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 do I do this with a non-singleton immutable cache?

+3


source to share


2 answers


If you only want to use things in scala collections, scala.collection.concurrent.TrieMap is a decent choice. However, remember that TrieMap # getOrElseUpdate had a thread safety bug that was only recently fixed in version 2.11.6.

If you can afford the extra dependency, guava cache is a good fit for writing caches like this. Especially if you want the cache entries to expire in some way.

As for the API of your cache: if you're 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 like this:



object Cached {
  def apply[T,U](f: T=>U): T=>U = { ??? }
}

      

Using:

def slow(id: Int): Try[String] = ??? // something complex including futures, timeouts etc.
val fast: Int => Try[String] = Cached(slow)

      

The caching API doesn't need to know anything about the function being cached other than that you expect it to be clean.

+3


source


I recommend that you use guava libraries in general. ( https://code.google.com/p/guava-libraries/ )



Like Rüdiger Klaehn, the mentioned cache is a good place to start.

+1


source







All Articles