如何用Scaffeine填充Caffeine AsyncCache并无竞争返回Map?
关于Scaffeine AsyncCache填充后的竞争条件问题
我正在使用Scaffeine(Caffeine的Scala封装库),尝试通过异步加载函数填充AsyncCache,之后等待缓存填充完成并返回一个同步的Map,但遇到了竞争条件,我认为自己可能对API做出了错误假设,以下是代码及注释:
import com.github.blemale.scaffeine.{AsyncCache, Scaffeine} import scala.collection.concurrent import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future sealed trait Cacheable[A] { def cacheKey(obj: A): Any def bytes(obj: A): Future[Array[Byte]] } object CacheRaceDebug { def buildCache[A](objs: Seq[A])(implicit cacheable: Cacheable[A]): Future[concurrent.Map[Any, Array[Byte]]] = { val cache: AsyncCache[Any, Array[Byte]] = Scaffeine().buildAsync() Future .sequence(objs.map { o => val key: Any = implicitly[Cacheable[A]].cacheKey(o) val bytes: Future[Array[Byte]] = implicitly[Cacheable[A]].bytes(o) // bytes是异步计算,比如生成图片或下载操作 // 本代码意图填充缓存 // 我们会等待此方法及所有其他加载操作返回的`Future`,以确认缓存已填充完成 cache.getFuture(key, _ => bytes) }) .map((allBytes: Seq[Array[Byte]]) => { // 当上述所有`getFuture`返回的`Future`都已完成时 // 我们(显然错误地)假设缓存已加载完成且所有值就绪 // 目的是返回一个可同步使用的包含所有值的Map cache.synchronous.asMap }) } }
核心疑问
我怀疑这并非Scala或Scaffeine的问题,而是对Caffeine的getFuture方法存在错误假设:该方法返回的Future完成后,是否不保证对应值已经存入缓存?
有没有更好的方法,能等待所有加载操作完成且缓存更新后,再调用cache.synchronous.toMap?
临时解决方案
是否应该直接在Scala中手动构建同步Map,放弃直接从缓存获取?
如下代码可以正常运行(返回键值对元组,再用_.toMap构建Map),但我仍想了解对Scaffeine或Caffeine的假设错误之处:
Future .traverse(objs) { obj => val key = obj.cacheKey cache.getFuture(key, _ => obj.bytes) .map(bytes => key -> bytes) }.map(_.toMap)
在所有对象本地已知的场景下,还可先对键去重,完全无需使用缓存。
直接使用Caffeine也存在相同问题
我尝试不借助Scaffeine直接使用Caffeine,也遇到了同样的问题:
val cache = Caffeine.newBuilder().asInstanceOf[Caffeine[Any, Array[Byte]]].buildAsync[Any, Array[Byte]]() Future .traverse(objs) { obj => cache .get(obj.cacheKey, asJavaBiFunction((_: Any, _: Executor) => obj.bytes.toJava.toCompletableFuture)) .toScala } .map(_ => cache.synchronous.asMap.asScala.toMap)
内容的提问来源于stack exchange,提问作者jon_wu
相关产品推荐
相关产品推荐

