Skip to content

Instantly share code, notes, and snippets.

@ChrisBlom
Created September 29, 2023 09:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChrisBlom/4fe924de2234dfc868b490a3f7e1f254 to your computer and use it in GitHub Desktop.
Save ChrisBlom/4fe924de2234dfc868b490a3f7e1f254 to your computer and use it in GitHub Desktop.
how to wrap a function in Kotlin so that is invoked at most once in a duration for each argument
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Ticker
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toJavaDuration
class CachedFor {
fun <A : Any, B : Any> cachedFor(
duration: Duration,
ticker: Ticker = Ticker.systemTicker(),
f: (A) -> B
): (A) -> B {
val cache = Caffeine
.newBuilder()
.ticker(ticker)
.expireAfterWrite(duration.toJavaDuration())
.build(f)
return { x -> cache.get(x) }
}
@Test
fun test() {
val time = AtomicLong(0)
val ticker = Ticker { time.get() }
val advanceTicker = { duration: Duration -> time.addAndGet(duration.inWholeNanoseconds) }
val invocations = ConcurrentHashMap<String, Int>()
val cachedAdd = cachedFor(duration = 1.seconds, ticker = ticker) { x: String ->
val c = invocations.compute(x) { _, v -> (v ?: 0) + 1 }
x.repeat(2)
}
repeat(10) { cachedAdd("x") shouldBe "xx" }
invocations shouldBe mapOf("x" to 1)
advanceTicker(1.seconds)
repeat(10) { cachedAdd("x") shouldBe "xx" }
invocations shouldBe mapOf("x" to 2)
repeat(10) { cachedAdd("y") shouldBe "yy" }
invocations shouldBe mapOf("x" to 2, "y" to 1)
advanceTicker(1.seconds)
repeat(10) { cachedAdd("y") shouldBe "yy" }
invocations shouldBe mapOf("x" to 2, "y" to 2)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment