Observe (a) Caffeine AsyncCache::get
signature:
public interface AsyncCache<K, V> {
CompletableFuture<V> get(K key,
BiFunction<? super K, Executor, CompletableFuture<V>> mappingFunction);
}
and (b) Kotlin coroutines signatures:
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R
public fun <T> CoroutineScope.future(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
) : CompletableFuture<T>
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> //Java
public suspend fun <T> CompletionStage<T>.await(): T
Suppose you have a suspending mapping function myCreate
. You can use CoroutineScope.future()
to convert it to a CompletableFuture, pass the future into AsyncCache::get
, and call await()
to make it suspending, such that you can leverage structured concurrency.
An example:
import com.github.benmanes.caffeine.cache.AsyncCache
import com.github.benmanes.caffeine.cache.Caffeine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.future.await
import kotlinx.coroutines.future.future
import java.util.concurrent.TimeUnit
import javax.inject.Named
@Named
class CacheStore
{
class Entry (val value: Double)
val cache: AsyncCache<String, Entry> = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(1, TimeUnit.HOURS)
.buildAsync()
suspend fun get( key: String,
create: suspend CoroutineScope.() -> Entry
): Entry = coroutineScope {
val fut = cache.get(key) { _, _ -> future { create() } }
fut.await()
}
}
...
suspend fun invoke(): CacheStore.Entry {
val entry = cacheStore.get(key) {
// logging or other logic
myCreate(arg)
}
return entry
}
suspend fun myCreate(arg: Double): CacheStore.Entry {
...
}
Ref: the official KEEP coroutines proposal on how to convert between callbacks, futures, and suspending functions:
https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#asynchronous-programming-styles
AsyncCache
with conversions betweenDeferred
andCompletableFuture
is the most correct translation. – RegelationCompletableFuture
but I am not sure if know the next step.. – DecennaryDeferred.asCompletableFuture
andCompletionStage.asDeferred
converters. – Regelation