sksamuel / aedile

Kotlin Wrapper for Caffeine
Apache License 2.0
170 stars 15 forks source link
cache caffeine coroutines java kotlin

Aedile

main

Aedile is a simple Kotlin wrapper for Caffeine which prefers coroutines rather than Java futures.

See changelog

Features

Usage

Add Aedile to your build:

implementation 'com.sksamuel.aedile:aedile-core:<version>'

Next, in your code, create a cache through the cache builder with the cacheBuilder() function, supplying the key / value types.

val cache = cacheBuilder<String, String>().build()

With this cache we can request values if present, or supply a suspendable function to compute them.

val value1 = cache.getIfPresent("foo") // value or null

val value2 = cache.get("foo") {
   delay(100) // look ma, we support suspendable functions!
   "value"
}

The build function supports a generic compute function which is used if no specific compute function is provided.

val cache = cacheBuilder<String, String>().build {
   delay(1)
   "value"
}

cache.get("foo") // uses default compute
cache.get("bar") { "other" } // uses specific compute function

Configuration

When creating the cache, Aedile supports most Caffeine configuration options. The exception is softValues and weakValues which are not supported with asynchronous operations. Since Aedile's purpose is to support coroutines, these options are ignored.

To configure the builder we supply a configuration lambda:

val cache = cacheBuilder<String, String> {
   maximumSize = 100
   initialCapacity = 10
}.build()

Evictions

Caffeine provides different approaches to eviction:

You can specify a suspendable function to listen to evictions:

val cache = cacheBuilder<String, String> {
   evictionListener = { key, value, cause ->
      when (cause) {
         RemovalCause.SIZE -> println("Removed due to size constraints")
         else -> delay(100) // suspendable for no real reason, but just to show you can!!
      }
   }
}.build()

Specify Dispatchers

By default, Aedile will use the dispatcher from the calling function for executing the compute functions. You can specify your own dispatcher when configuring the builder.

val cacheDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
val cache = cacheBuilder<String, String>() {
   this.dispatcher = cacheDispatcher
}.build()

You can also specify a custom CoroutineScope if required. Note that this scope should not be cancelled or closed while the cache is in use.

Metrics

Aedile provides Micrometer integration which simply delegates to the Caffeine micrometer support. To use this, import the com.sksamuel.aedile:aedile-micrometer module, and bind to a micrometer registry:

CacheMetrics(cache, "my-cache-name").bindTo(registry)
// or
LoadingCacheMetrics(cache, "my-cache-name").bindTo(registry)