ScalaCache
Cache Library
ScalaCache
Overview
ScalaCache is a facade for the most popular cache implementations, providing a simple, idiomatic Scala API for adding caching to any Scala app with minimal effort.
Details
ScalaCache provides a unified interface to various caching libraries, allowing you to add caching functionality to Scala applications with minimal effort. It supports a wide range of cache backends including Redis, Memcached, Guava Cache, Caffeine, and EhCache. The memoization feature automatically caches method results, using cached results when the same method is called with the same arguments. It also supports Future-based async APIs for performing cache operations in separate threads. The type-safe, idiomatic Scala API design enhances developer experience.
Pros and Cons
Pros
- Unified API: Operate different cache libraries through a unified interface
- Rich Backends: Supports Redis, Memcached, Guava, Caffeine, EhCache
- Memoization: Easy implementation through automatic method result caching
- Async Support: Future-based asynchronous cache operations
- Scala Idiomatic: Fits functional programming style
- TTL Support: Can set expiration times for cache entries
Cons
- Scala Only: Cannot be used in Java
- Learning Curve: Requires Scala knowledge
- Backend Dependent: Subject to limitations of chosen cache library
- Overhead: Minor performance impact from facade layer
Key Links
- ScalaCache Official GitHub
- ScalaCache Official Documentation
- ScalaCache Getting Started Guide
- Baeldung ScalaCache Tutorial
Usage Examples
Basic Usage
import scalacache._
import scalacache.caffeine._
import scalacache.memoization._
// Initialize cache
implicit val cache: Cache[String] = CaffeineCache[String]
// Basic cache operations
val result: Option[String] = sync.get("key")
sync.put("key")("value")
sync.remove("key")
// Store with TTL
import scala.concurrent.duration._
sync.put("key", ttl = Some(10.minutes))("value")
Memoization Feature
import scalacache.memoization._
class UserService {
implicit val cache: Cache[User] = CaffeineCache[User]
// Automatically cache method results
def getUser(id: Int): User = memoize(None) {
// Heavy processing like database access
fetchUserFromDatabase(id)
}
// Memoization with TTL
def getUserWithTTL(id: Int): User = memoize(Some(1.hour)) {
fetchUserFromDatabase(id)
}
}
Async Cache Operations
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
// Future-based async operations
val futureResult: Future[Option[String]] = async.get("key")
async.put("key")("value").foreach { _ =>
println("Cache save completed")
}
// Async memoization
def fetchDataAsync(id: Int): Future[Data] = memoizeF(Some(30.minutes)) {
// Async data fetching
httpClient.fetchData(id)
}
Using Redis Backend
import scalacache._
import scalacache.redis._
import redis.clients.jedis._
// Redis connection setup
val jedisPool = new JedisPool("localhost", 6379)
implicit val cache: Cache[String] = RedisCache[String](jedisPool)
// Redis-specific operations
val serializer = scalacache.serialization.binary._
class ProductService {
def getProduct(id: String): Product = memoize(Some(1.day)) {
productRepository.findById(id)
}
}
Using Memcached Backend
import scalacache._
import scalacache.memcached._
import net.spy.memcached._
// Memcached connection setup
val memcachedClient = new MemcachedClient(
AddrUtil.getAddresses("localhost:11211")
)
implicit val cache: Cache[String] = MemcachedCache[String](memcachedClient)
// Cache operation example
def expensiveOperation(param: String): String = memoize(Some(2.hours)) {
// Heavy processing
performComplexCalculation(param)
}
Cache Configuration and Customization
import scalacache.caffeine._
import com.github.benmanes.caffeine.cache.Caffeine
import java.util.concurrent.TimeUnit
// Detailed Caffeine configuration
val underlyingCache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(1, TimeUnit.HOURS)
.recordStats()
.build[String, Entry[String]]()
implicit val cache: Cache[String] = CaffeineCache[String](underlyingCache)
// Get statistics
val stats = underlyingCache.stats()
println(s"Hit rate: ${stats.hitRate()}")
Error Handling
import scala.util.{Try, Success, Failure}
class CacheService {
implicit val cache: Cache[String] = CaffeineCache[String]
def safeGet(key: String): Try[Option[String]] = {
Try(sync.get(key)) match {
case Success(value) => Success(value)
case Failure(ex) =>
logger.error(s"Cache access error: $key", ex)
Failure(ex)
}
}
def safeMemoize[T](key: String, ttl: Option[Duration] = None)
(f: => T): T = {
try {
memoize(ttl)(f)
} catch {
case ex: Exception =>
logger.warn(s"Memoization error, executing directly: $key", ex)
f
}
}
}