11

I just wonder if there is any caching solution available in Scala. I'm looking for something like it is provided by Guava in Java.

Should I use Guava too in Scala? Is there a wrapper / pimp in Scalaz or something similar? Any alternative more appropriate for Scala devs?

What Guava provides:

LoadingCache<Key, Graph> CACHE= CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build(
           new CacheLoader<Key, Graph>() {
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           });

Supplier<Animal> singleAnimalCache = Suppliers.memoizeWithExpiration(animalFromDbSupplier(), 365, TimeUnit.DAYS);

I need some basic cache management like in Guava.

Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
  • You unhappy with http://stackoverflow.com/questions/3651313/how-to-cache-results-in-scala ? – om-nom-nom Nov 29 '12 at 23:12
  • @om-nom-nom this looks great, I'm looking for a memoizer (like in Guava with Supplyer), but I need some cache management like expiration, max cache size etc. I don't see that in Scalaz implementation – Sebastien Lorber Nov 29 '12 at 23:20
  • 5
    You wouldn't be the first to use Guava's `Cache` in Scala. – Louis Wasserman Nov 29 '12 at 23:33
  • 2
    Twitter has a [helper](https://github.com/twitter/util/blob/master/util-collection/src/main/scala/com/twitter/util/MapMaker.scala) for MapMaker (now adapted to CacheBuilder). Instead I use CacheBuilder directly and have implicits for converting between Guava and Scala types. – Ben Manes Nov 30 '12 at 01:15
  • Does this help? [Guava caching in scala](http://stackoverflow.com/questions/16196290/scala-2-10-type-mismatch-using-google-guavas-cachebuilder/16204579?noredirect=1#16204579) – adivis May 27 '13 at 07:30

4 Answers4

5

We had the same requirements and ended up building wrappers around Guava. We recently open-sourced parts of the library called Mango. If you don’t mind the extra dependency you can use it like

import org.feijoas.mango.common.cache._
import org.feijoas.mango.common.base.Suppliers._

val MY_LISTENER = (remNot: RemovalNotification[Key, Graph]) => ()
// > MY_LISTENER : RemovalNotification[Key,Graph] => Unit = <function1>

val CACHE = CacheBuilder.newBuilder()
  .maximumSize(1000)
  .expireAfterWrite(10, TimeUnit.MINUTES)
  .removalListener(MY_LISTENER)
  .build { (key: Key) => new Graph() }
// > CACHE : LoadingCache[Key,Graph] = <function1>

val animalFromDbSupplier = () => {
  // load from db
  new Animal()
}
// > animalFromDbSupplier  : () => Animal = <function0>

val singleAnimalCache = memoizeWithExpiration(animalFromDbSupplier, 365, TimeUnit.DAYS)
// > singleAnimalCache  : () => Animal = Suppliers.memoizeWithExpiration(<function0>, 365, DAYS)
Markus
  • 242
  • 3
  • 8
5

Just adding an answer to plug my own project, but I recommend ScalaCache.

  • Supports Guava, Ehcache, Memcached and Redis (or you can plugin your own implementation if you like)
  • Simple, idiomatic Scala API
  • Support for per-element Time To Live (even for Guava, which doesn't provide that out of the box)
  • Support for auto-generation of cache keys using macro magic

https://github.com/cb372/scalacache

Chris B
  • 9,149
  • 4
  • 32
  • 38
3

Is there a wrapper / pimp in Scalaz or something similar?

In Scalaz 7 there's Memo, which I covered a bit in learning Scalaz day 16.

It's the first thing Adam Rosien covered in scalaz "For the Rest of Us" talk, so check that out too. He's using Scalaz 6.

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
3

Using guava caching is straightforward in Scala.

import com.google.common.base._
import com.google.common.cache._

object Simple_Guava_Caches_in_Scala {

   def main(args: Array[String]): Unit = {

      // Simple scala guava cache
      val simpleCache1 =
         CacheBuilder
           .newBuilder()
           .build(new CacheLoader[String, String] {
              def load(key: String): String = {
                 println(s"Simple scala guava cache, heavy work calculating $key")
                 s"It's me $key"
              }
           })
      println(simpleCache1.get("1"))
      println(simpleCache1.get("1"))
      println(simpleCache1.get("2"))
      println(simpleCache1.get("2"))
      println(simpleCache1.get("2"))


      // Simple scala guava supplier cache / factory
      println()
      val supplier_cache: Supplier[String] = Suppliers.memoize(
         () => {
            println("Simple scala guava supplier cache / factory, heavy work creating singleton:")
            "It's me"
         }
      )
      println(supplier_cache.get)
      println(supplier_cache.get)
      println(supplier_cache.get)
   }
}

This is producing

Simple scala guava cache, heavy work calculating 1
It's me 1
It's me 1
Simple scala guava cache, heavy work calculating 2
It's me 2
It's me 2
It's me 2

Simple scala guava supplier cache / factory, heavy work creating singleton:
It's me
It's me
It's me

I've added this to build.sbt:

libraryDependencies += "com.github.cb372" %% "scalacache-guava" % "0.26.0"

Would be interesting to transform the coding above to cb372's scalacache-guava. Could be even simpler/more standardized.

Hartmut Pfarr
  • 5,534
  • 5
  • 36
  • 42