Caffeine:当 AsyncLoader 刷新失败时使用陈旧的值

Caffeine: Use stale values when AsyncLoader fails to refresh

我想将我的 Caffeine 缓存配置为 return 在加载程序无法刷新缓存时过时的结果。以下 Kotlin 代码演示了这种情况:

    @Test
    fun `completeble future`() = runBlocking {
        val cache = Caffeine.newBuilder()
            .refreshAfterWrite(Duration.ofSeconds(1))
            .expireAfterWrite(Duration.ofSeconds(1))
            .buildAsync<String, String> { key: String, executor ->
                GlobalScope.future(executor.asCoroutineDispatcher()) {
                    throw Exception("== Error ==")
                }
            }

        cache.put("id", CompletableFuture.completedFuture("value"))

        delay(2000)

        assertEquals("value", cache.get("id").await())
    }

我希望这个测试能够通过,但我却收到以下错误:

WARNING: Exception thrown during asynchronous load
java.lang.Exception: == Error ==
    at fsra.manager.TranslationManagerImplTest$completeble future$cache.invokeSuspend(TranslationManagerImplTest.kt:93)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)




java.lang.Exception: == Error ==

    at fsra.manager.TranslationManagerImplTest$completeble future$cache.invokeSuspend(TranslationManagerImplTest.kt:93)
    at |b|b|b(Coroutine boundary.|b(|b)
    at fsra.manager.TranslationManagerImplTest$completeble future.invokeSuspend(TranslationManagerImplTest.kt:101)
Caused by: java.lang.Exception: == Error ==
    at fsra.manager.TranslationManagerImplTest$completeble future$cache.invokeSuspend(TranslationManagerImplTest.kt:93)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

我用 Kotlin 编写了代码,但我认为问题与 Kotlin 协程无关。我想将 Caffeine 配置为在刷新时不抛出异常,而是 return 缓存中的先前结果。

缓存设置为 1 秒后过期条目,测试等待 2 秒。下一次调用会强制重新加载它,因为该条目不可用,并且您抛出异常。

大于等于过期时间刷新无效。小于时,条目过时但可用,因此返回并异步重新加载。这是为了允许流行的项目(如配置)保留在缓存中,而不会定期重新加载。不受欢迎的项目是那些在到期时间间隔内没有被访问并且被允许被驱逐的项目。如果刷新无法成功,则过期将启动并且条目将被删除,因为过期设置了它被认为可用的最长时间。

更大的过期值(如 5 秒)将通过您的测试。如果您的用例是定期盲目地重新加载缓存的所有内容,您可以改为使用常规 MapScheduledExecutorService 来刷新它。