### Perform Basic Cache Operations Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/index.md Demonstrates standard cache interactions including put, get, remove, flush, and caching blocks. ```scala val ericTheCat = Cat(1, "Eric", "tuxedo") val doraemon = Cat(99, "Doraemon", "blue") // Add an item to the cache put("eric")(ericTheCat) // Add an item to the cache with a Time To Live import scala.concurrent.duration._ put("doraemon")(doraemon, ttl = Some(10.seconds)) // Retrieve the added item get("eric") // Remove it from the cache remove("doraemon") // Flush the cache removeAll[String, Cat] // Wrap any block with caching: if the key is not present in the cache, // the block will be executed and the value will be cached and returned caching("benjamin")(ttl = None) { // e.g. call an external API ... Cat(2, "Benjamin", "ginger") } // If the result of the block is wrapped in an effect, use cachingF cachingF("benjamin")(ttl = None) { IO.pure { // e.g. call an external API ... Cat(2, "Benjamin", "ginger") } } ``` -------------------------------- ### Perform Basic Cache Operations Source: https://context7.com/cb372/scalacache/llms.txt Demonstrates standard cache interactions including put, get, remove, and bulk removal with optional TTL support. ```scala import scalacache._ import scalacache.caffeine._ import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ final case class User(id: Int, name: String, email: String) implicit val userCache: Cache[IO, String, User] = CaffeineCache[IO, String, User].unsafeRunSync() // Put a value into the cache val user = User(1, "Alice", "alice@example.com") put("user:1")(user).unsafeRunSync() // Put with TTL (expires after 5 minutes) put("user:2")(User(2, "Bob", "bob@example.com"), ttl = Some(5.minutes)).unsafeRunSync() // Get a value from the cache val result: Option[User] = get("user:1").unsafeRunSync() // result = Some(User(1, "Alice", "alice@example.com")) // Remove a specific key remove("user:1").unsafeRunSync() // Remove all entries from the cache removeAll[String, User].apply[IO].unsafeRunSync() ``` -------------------------------- ### Example of automatic cache key generation Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/memoization.md Demonstrates how scalacache automatically generates a cache key based on the class name, method name, and parameter values. This example shows a method with multiple parameter lists. ```scala object Bar { def baz(a: Int, b: String)(c: String): Int = memoizeF(None) { IO { // Reticulating splines... 123 } } } ``` -------------------------------- ### Typed API Example in ScalaCache Source: https://github.com/cb372/scalacache/blob/master/CHANGELOG.md Example of using the typed API to get a value from the cache with a specified key. ```scala import scalacache._ implicit val scalaCache = ... // Using the typed API val stringsCache = typed[String] val futureOfOptionOfString = stringsCache.get("key") ``` -------------------------------- ### Untyped API Example in ScalaCache Source: https://github.com/cb372/scalacache/blob/master/CHANGELOG.md Example of using the untyped API for caching with a specified key and a function call. ```scala import scalacache._ implicit val scalaCache = ... // Using the untyped API val futureOfString = caching[String]("key")(doSomething()) ``` -------------------------------- ### ScalaCache with Flags Example Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/flags.md Demonstrates how to define and use implicit flags for cache operations. Ensure your memoized method accepts an implicit `Flags` parameter to utilize this functionality. Note that flags affect the cache key by default. ```scala import scalacache._ import scalacache.memcached._ import scalacache.memoization._ import scalacache.serialization.binary._ import cats.effect.IO import cats.effect.unsafe.implicits.global final case class Cat(id: Int, name: String, colour: String) implicit val catsCache: Cache[IO, String, Cat] = MemcachedCache("localhost:11211") def getCatWithFlags(id: Int)(implicit flags: Flags): Cat = memoize(None) { // Do DB lookup here... Cat(id, s"cat ${id}", "black") }.unsafeRunSync() def getCatMaybeSkippingCache(id: Int, skipCache: Boolean): Cat = { implicit val flags = Flags(readsEnabled = !skipCache) getCatWithFlags(id) } ``` -------------------------------- ### Initialize Memcached cache Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/cache-implementations.md Initialize a Memcached cache using either a connection string or a custom client. ```scala import scalacache._ import scalacache.memcached._ import scalacache.serialization.binary._ import cats.effect.IO implicit val memcachedCache: Cache[IO, String, String] = MemcachedCache("localhost:11211") ``` ```scala import scalacache._ import scalacache.memcached._ import scalacache.serialization.binary._ import net.spy.memcached._ val memcachedClient = new MemcachedClient( new BinaryConnectionFactory(), AddrUtil.getAddresses("localhost:11211") ) implicit val customisedMemcachedCache: Cache[IO, String, String] = MemcachedCache(memcachedClient) ``` -------------------------------- ### Create a Memcached Cache Source: https://context7.com/cb372/scalacache/llms.txt Sets up distributed Memcached storage using either simple connection strings or a custom MemcachedClient. ```scala import scalacache._ import scalacache.memcached._ import scalacache.serialization.binary._ import cats.effect.IO import net.spy.memcached._ // Simple Memcached connection implicit val memcachedCache: Cache[IO, String, String] = MemcachedCache("localhost:11211") // Multiple Memcached servers implicit val multiCache: Cache[IO, String, String] = MemcachedCache("host1:11211 host2:11211") // Custom Memcached client val client = new MemcachedClient( new BinaryConnectionFactory(), AddrUtil.getAddresses("localhost:11211") ) implicit val customMemcached: Cache[IO, String, String] = MemcachedCache(client) ``` -------------------------------- ### Initialize Redis cache Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/cache-implementations.md Initialize a Redis cache using a host/port or a Jedis pool. ```scala import scalacache._ import scalacache.redis._ import scalacache.serialization.binary._ import cats.effect.IO implicit val redisCache: Cache[IO, String, String] = RedisCache("host1", 6379) ``` ```scala import scalacache._ import scalacache.redis._ import scalacache.serialization.binary._ import _root_.redis.clients.jedis._ import cats.effect.IO val jedisPool = new JedisPool("localhost", 6379) implicit val customisedRedisCache: Cache[IO, String, String] = RedisCache(jedisPool) ``` -------------------------------- ### Initialize Memcached Cache Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/index.md Configures a Memcached cache instance with binary serialization for a specific data type. ```scala import scalacache.memcached._ // We'll use the binary serialization codec - more on that later import scalacache.serialization.binary._ final case class Cat(id: Int, name: String, colour: String) implicit val catsCache: Cache[IO, String, Cat] = MemcachedCache("localhost:11211") ``` -------------------------------- ### Initialize Caffeine cache Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/cache-implementations.md Initialize a Caffeine cache with default settings or a custom builder. ```scala import scalacache._ import scalacache.caffeine._ import cats.effect.{Clock, IO} import cats.effect.unsafe.implicits.global implicit val clock: Clock[IO] = Clock[IO] implicit val caffeineCache: Cache[IO, String, String] = CaffeineCache[IO, String, String].unsafeRunSync() ``` ```scala import scalacache._ import scalacache.caffeine._ import com.github.benmanes.caffeine.cache.Caffeine import cats.effect.IO import cats.effect.unsafe.implicits.global val underlyingCaffeineCache = Caffeine.newBuilder().maximumSize(10000L).build[String, Entry[String]] implicit val customisedCaffeineCache: Cache[IO, String, String] = CaffeineCache(underlyingCaffeineCache) ``` -------------------------------- ### Import ScalaCache API Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/index.md Essential imports for accessing the ScalaCache API and Cats Effect IO. ```scala import scalacache._ import cats.effect.IO ``` -------------------------------- ### Configure Redis dependency Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/cache-implementations.md Add the Redis module to your SBT build file. ```sbt libraryDependencies += "com.github.cb372" %% "scalacache-redis" % "0.28.0" ``` -------------------------------- ### Import JSON Codec Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/serialization.md Required import to enable Circe-based JSON serialization. ```scala import scalacache.serialization.circe._ ``` -------------------------------- ### Import Binary Codec Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/serialization.md Required import to use ScalaCache's built-in binary codec for primitive types and Java serialization. ```scala import scalacache.serialization.binary._ ``` -------------------------------- ### Memoization return type requirements Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/restrictions.md Demonstrates the difference between valid and invalid method definitions when using memoize. ```scala def getUser(id: Int): Future[User] = memoize { // Do stuff... } ``` ```scala def getUser(id: Int) = memoize { // Do stuff... } ``` -------------------------------- ### JSON Serialization with Circe Source: https://context7.com/cb372/scalacache/llms.txt Implement JSON serialization using the Circe library for human-readable cache entries and better cross-language compatibility. This involves defining Circe encoders and decoders for your case classes. ```scala import scalacache._ import scalacache.redis._ import scalacache.serialization.circe._ import cats.effect.IO import io.circe._ import io.circe.generic.semiauto._ final case class Article(id: Int, title: String, content: String, tags: List[String]) // Semi-automatic derivation (recommended for performance) implicit val articleEncoder: Encoder[Article] = deriveEncoder[Article] implicit val articleDecoder: Decoder[Article] = deriveDecoder[Article] // Circe codec is now implicitly available implicit val articleCache: Cache[IO, String, Article] = RedisCache("localhost", 6379) // Or use automatic derivation (simpler but slower) // import io.circe.generic.auto._ // implicit val articleCache: Cache[IO, String, Article] = RedisCache("localhost", 6379) // Values are stored as JSON in Redis // {"id":1,"title":"Hello World","content":"...","tags":["scala","caching"]} ``` -------------------------------- ### Binary Serialization with GZip Compression Source: https://context7.com/cb372/scalacache/llms.txt Configure ScalaCache to use binary serialization with GZip compression for efficient storage and retrieval of primitive types and Java-serializable objects, particularly useful for distributed caches like Redis. This involves importing specific codecs. ```scala import scalacache._ import scalacache.redis._ import scalacache.serialization.binary._ import cats.effect.IO // Import binary codecs - provides serialization for primitives and Serializable objects // Primitives (Int, Long, String, etc.) have efficient dedicated codecs // Objects use Java serialization final case class Session(userId: Int, token: String, expiresAt: Long) extends Serializable // Binary codec is automatically derived for Serializable classes implicit val sessionCache: Cache[IO, String, Session] = RedisCache("localhost", 6379) // GZip compression for large values import scalacache.serialization.gzip._ // Use GZip-compressed Java serialization implicit val compressedCodec: GZippingJavaAnyBinaryCodec[Session] = new GZippingJavaAnyBinaryCodec[Session] implicit val compressedCache: Cache[IO, String, Session] = RedisCache("localhost", 6379) ``` -------------------------------- ### Create a Redis Cache Source: https://context7.com/cb372/scalacache/llms.txt Configures distributed Redis caching, supporting standard connections, custom Jedis pools, sharding, and Sentinel for high availability. ```scala import scalacache._ import scalacache.redis._ import scalacache.serialization.binary._ import cats.effect.IO import redis.clients.jedis._ // Simple Redis connection implicit val redisCache: Cache[IO, String, String] = RedisCache("localhost", 6379) // Or with a custom Jedis pool val jedisPool = new JedisPool("localhost", 6379) implicit val customRedisCache: Cache[IO, String, String] = RedisCache(jedisPool) // Sharded Redis for horizontal scaling import scalacache.redis.ShardedRedisCache val shardedCache: Cache[IO, String, String] = ShardedRedisCache( ("host1", 6379), ("host2", 6379), ("host3", 6379) ) // Redis Sentinel for high availability import scalacache.redis.SentinelRedisCache val sentinelCache: Cache[IO, String, String] = SentinelRedisCache( clusterName = "mymaster", sentinels = Set("sentinel1:26379", "sentinel2:26379"), password = "secret" ) ``` -------------------------------- ### Custom Cache Key Generation for Memoization Source: https://context7.com/cb372/scalacache/llms.txt Configure cache key generation for memoization. By default, constructor parameters are excluded. Use `CacheConfig` with `MemoizationConfig.includeClassConstructorParams` to include them. ```scala import scalacache._ import scalacache.caffeine._ import scalacache.memoization._ import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ implicit val cache: Cache[IO, String, Int] = CaffeineCache[IO, String, Int].unsafeRunSync() // Default: excludes constructor params // class Bar(a: Int) { def baz(b: Int) } => cache key: "Bar.baz(42)" // Include constructor params in cache key implicit val cacheConfig: CacheConfig = CacheConfig( memoization = MemoizationConfig(MethodCallToStringConverter.includeClassConstructorParams) ) // class Bar(a: Int) { def baz(b: Int) } => cache key: "Bar(10).baz(42)" class Calculator(multiplier: Int) { def compute(value: Int): IO[Int] = memoize(None) { println(s"Computing: $value * $multiplier") value * multiplier } } val calc1 = new Calculator(10) val calc2 = new Calculator(20) calc1.compute(5).unsafeRunSync() // Cache key: "Calculator(10).compute(5)" => 50 calc2.compute(5).unsafeRunSync() // Cache key: "Calculator(20).compute(5)" => 100 (different cache entry) ``` -------------------------------- ### Memoize method results with IO effect using memoizeF Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/memoization.md Use `memoizeF` for methods that return results wrapped in an effect container like `IO`. This ensures that the effect is executed only once and its result is cached. The cache key is automatically generated. ```scala def getCatF(id: Int): IO[Cat] = memoizeF(Some(10.seconds)) { IO { // Retrieve data from a remote API here ... Cat(id, s"cat ${id}", "black") } } getCatF(123) ``` -------------------------------- ### Configure Memcached dependency Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/cache-implementations.md Add the Memcached module to your SBT build file. ```sbt libraryDependencies += "com.github.cb372" %% "scalacache-memcached" % "0.28.0" ``` -------------------------------- ### Add Circe Dependency Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/serialization.md SBT dependency configuration for the scalacache-circe module. ```sbt libraryDependencies += "com.github.cb372" %% "scalacache-circe" % "0.28.0" ``` -------------------------------- ### Caching Synchronous Computations with caching Source: https://context7.com/cb372/scalacache/llms.txt Use `caching` to wrap synchronous computations. The computation block only executes on a cache miss. Specify a TTL (Time To Live) for the cache entry. ```scala import scalacache._ import scalacache.caffeine._ import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ final case class Product(id: Int, name: String, price: BigDecimal) implicit val productCache: Cache[IO, String, Product] = CaffeineCache[IO, String, Product].unsafeRunSync() // caching: wrap a synchronous computation def getProduct(id: Int): IO[Product] = { caching(s"product:$id")(ttl = Some(10.minutes)) { // This block only executes on cache miss println(s"Fetching product $id from database...") Product(id, s"Product $id", BigDecimal(99.99)) } } // First call: fetches from "database" and caches result getProduct(123).unsafeRunSync() // Output: Fetching product 123 from database... // Returns: Product(123, "Product 123", 99.99) // Second call: returns cached result immediately getProduct(123).unsafeRunSync() // No output (cache hit) // Returns: Product(123, "Product 123", 99.99) ``` -------------------------------- ### Configure Caffeine dependency Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/cache-implementations.md Add the Caffeine module to your SBT build file. ```sbt libraryDependencies += "com.github.cb372" %% "scalacache-caffeine" % "0.28.0" ``` -------------------------------- ### Method Memoization with memoize Source: https://context7.com/cb372/scalacache/llms.txt Use `memoize` to automatically cache the results of synchronous methods based on their name and arguments. Specify an optional TTL for the cache entry. ```scala import scalacache._ import scalacache.caffeine._ import scalacache.memoization._ import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ final case class UserProfile(id: Int, name: String, followers: Int) implicit val profileCache: Cache[IO, String, UserProfile] = CaffeineCache[IO, String, UserProfile].unsafeRunSync() object UserService { // memoize: cache the result of this method // Cache key is auto-generated: "UserService.fetchProfile(123)" def fetchProfile(userId: Int): IO[UserProfile] = memoize(Some(5.minutes)) { println(s"Fetching profile for user $userId from API...") UserProfile(userId, s"User$userId", 1000) } // memoizeF: for methods returning effectful computations def fetchProfileAsync(userId: Int): IO[UserProfile] = memoizeF(Some(5.minutes)) { IO { println(s"Async fetching profile for user $userId...") UserProfile(userId, s"User$userId", 2000) } } // Multiple parameter lists are supported def getFollowers(userId: Int)(page: Int, limit: Int): IO[List[Int]] = memoize(Some(1.minute)) { // Cache key: "UserService.getFollowers(123)(1, 10)" println(s"Fetching followers page $page for user $userId...") (1 to limit).map(_ + (page * limit)).toList } } // Usage UserService.fetchProfile(123).unsafeRunSync() // Output: Fetching profile for user 123 from API... // Returns: UserProfile(123, "User123", 1000) UserService.fetchProfile(123).unsafeRunSync() // No output (cached) // Returns: UserProfile(123, "User123", 1000) ``` -------------------------------- ### Dynamically Control Cache Behavior with Flags Source: https://context7.com/cb372/scalacache/llms.txt Utilize the Flags API to dynamically enable or disable cache reads and writes at runtime. Methods must accept an implicit Flags parameter to leverage this feature. This is useful for scenarios like forcing a cache refresh. ```scala import scalacache._ import scalacache.caffeine._ import scalacache.memoization._ import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ final case class Config(id: Int, settings: Map[String, String]) implicit val configCache: Cache[IO, String, Config] = CaffeineCache[IO, String, Config].unsafeRunSync() object ConfigService { // Method must accept implicit Flags parameter def getConfig(id: Int)(implicit flags: Flags): IO[Config] = memoize(Some(5.minutes)) { println(s"Loading config $id from source...") Config(id, Map("key" -> "value")) } } // Normal caching behavior ConfigService.getConfig(1).unsafeRunSync() // Output: Loading config 1 from source... ConfigService.getConfig(1).unsafeRunSync() // No output (cache hit) // Force cache bypass (skip read, but still write) { implicit val flags = Flags(readsEnabled = false) ConfigService.getConfig(1).unsafeRunSync() // Output: Loading config 1 from source... (cache was bypassed) } // Skip both reads and writes { implicit val flags = Flags(readsEnabled = false, writesEnabled = false) ConfigService.getConfig(1).unsafeRunSync() // Output: Loading config 1 from source... (no caching at all) } // Practical use case: force refresh def getConfigWithRefresh(id: Int, forceRefresh: Boolean): IO[Config] = { implicit val flags = Flags(readsEnabled = !forceRefresh) ConfigService.getConfig(id) } getConfigWithRefresh(1, forceRefresh = true).unsafeRunSync() // Always fetches fresh data and updates cache ``` -------------------------------- ### Memoize method results with IO effect Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/memoization.md Use `memoize` for methods returning a result directly. The cache key is automatically generated based on the method and its arguments. Results are cached for a specified duration. ```scala import scalacache._ import scalacache.memcached._ import scalacache.memoization._ import scalacache.serialization.binary._ import scala.concurrent.duration._ import cats.effect.IO final case class Cat(id: Int, name: String, colour: String) implicit val catsCache: Cache[IO, String, Cat] = MemcachedCache("localhost:11211") def getCat(id: Int): IO[Cat] = memoize(Some(10.seconds)) { // Retrieve data from a remote API here ... Cat(id, s"cat ${id}", "black") } getCat(123) ``` -------------------------------- ### Close Cache Connection Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/index.md Internal cleanup logic to close the cache connection. ```scala import cats.effect.unsafe.implicits.global for (cache <- List(catsCache)) { cache.close.unsafeRunSync() } ``` -------------------------------- ### Caching Effectful Computations with cachingF Source: https://context7.com/cb372/scalacache/llms.txt Use `cachingF` to wrap effectful computations (e.g., `IO`). The computation block executes only on a cache miss. Specify a TTL for the cache entry. ```scala import scalacache._ import scalacache.caffeine._ import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ final case class Product(id: Int, name: String, price: BigDecimal) implicit val productCache: Cache[IO, String, Product] = CaffeineCache[IO, String, Product].unsafeRunSync() // cachingF: wrap an effectful computation def getProductAsync(id: Int): IO[Product] = { cachingF(s"product:$id")(ttl = Some(10.minutes)) { IO { // Async operation, e.g., HTTP call or database query println(s"Async fetch for product $id...") Product(id, s"Product $id", BigDecimal(49.99)) } } } getProductAsync(456).unsafeRunSync() // Output: Async fetch for product 456... ``` -------------------------------- ### Configure Caching Write Semantics in ScalaCache Source: https://github.com/cb372/scalacache/blob/master/CHANGELOG.md Use the `waitForWriteToComplete` flag in `CacheConfig` to control whether the Future completes after cache write or after value computation. Default is true. ```scala import scalacache.CacheConfig // Default behavior: Future completes after cache write val config1 = CacheConfig() // Old behavior: Future completes after value computation, cache write in callback val config2 = CacheConfig(waitForWriteToComplete = false) ``` -------------------------------- ### Including class constructor arguments in cache key Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/memoization.md Configures scalacache to include class constructor arguments in the cache key generation for memoized methods within a class. This ensures that results are cached distinctly for different instances of the class. ```scala package foo class Bar(a: Int) { def baz(b: Int): Int = memoizeSync(None) { a + b } } // Usage example: // new Bar(10).baz(42) // cached as "foo.Bar(10).baz(42)" -> 52 // new Bar(20).baz(42) // cached as "foo.Bar(20).baz(42)" -> 62 ``` -------------------------------- ### Excluding parameters from cache key generation Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/memoization.md Shows how to use the `@cacheKeyExclude` annotation to prevent specific parameters (method arguments or class constructor arguments) from being included in the auto-generated cache key for memoization. ```scala def doSomething(userId: UserId)(implicit @cacheKeyExclude db: DBConnection): String = memoize { ... } ``` -------------------------------- ### Derive Circe Encoders and Decoders Source: https://github.com/cb372/scalacache/blob/master/modules/docs/src/main/mdoc/docs/serialization.md Methods for providing Circe encoders and decoders for custom types in implicit scope. ```scala import io.circe.generic.auto._ ``` ```scala import io.circe._ import io.circe.generic.semiauto._ implicit val catEncoder: Encoder[Cat] = deriveEncoder[Cat] implicit val catDecoder: Decoder[Cat] = deriveDecoder[Cat] ``` -------------------------------- ### Specify Serialization Type for Caching in ScalaCache Source: https://github.com/cb372/scalacache/blob/master/CHANGELOG.md When using custom serialization, explicitly specify the serialization type parameter for `caching` and `typed` methods. Use `NoSerialization` for in-memory caches and `Array[Byte]` for Memcached/Redis. ```scala import scalacache._ implicit val scalaCache = ... // Using the untyped API val futureOfString = caching[String, NoSerialization]("key")(doSomething()) // assuming an in-memory cache //val futureOfString = caching[String, Array[Byte]]("key")(doSomething()) // use Array[Byte] if you are using Memcached or Redis // Using the typed API val stringsCache = typed[String, NoSerialization] // assuming an in-memory cache val futureOfOptionOfString = stringsCache.get("key") ``` -------------------------------- ### Exclude Parameters from Cache Key with @cacheKeyExclude Source: https://context7.com/cb372/scalacache/llms.txt Use the @cacheKeyExclude annotation to prevent specific parameters, like a DBConnection object, from being included in the generated cache key. This ensures the cache key is based only on relevant parameters, such as the user ID. ```scala import scalacache._ import scalacache.caffeine._ import scalacache.memoization._ import scalacache.memoization.annotations.cacheKeyExclude import cats.effect.IO import cats.effect.unsafe.implicits.global import scala.concurrent.duration._ final case class DBConnection(url: String) final case class User(id: Int, name: String) implicit val userCache: Cache[IO, String, User] = CaffeineCache[IO, String, User].unsafeRunSync() object UserRepository { // The db connection is excluded from the cache key // Cache key: "UserRepository.findUser(123)" (not "UserRepository.findUser(123)(DBConnection(...))") def findUser(userId: Int)(implicit @cacheKeyExclude db: DBConnection): IO[User] = memoize(Some(10.minutes)) { println(s"Querying database for user $userId...") User(userId, s"User$userId") } } implicit val db = DBConnection("jdbc:postgresql://localhost/mydb") UserRepository.findUser(123).unsafeRunSync() // Cache key is "UserRepository.findUser(123)" regardless of db connection ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.