缓存API非常特别。由于它们是不可变缓存,任何通常会在可变缓存中更改某些元数据的操作(例如TTL、使用数据等)都必须返回一个新缓存实例。
除了实际的缓存API,还创建了一个“映射式”API,因此可以使用Clojure的get、assoc、dissoc操作,但这个接口可能(并且确实)有一些奇怪的边缘情况,例如在检查项目是否在缓存中以及在查找时(发现它“消失”)之间的竞争条件 - 即使缓存本身是不可变的。这给上游消费者带来了问题,例如core.memoize,它必须包含spin/retry逻辑,以避免错误地返回nil。
这就是为什么我添加了封装命名空间的主要原因,它提供了更直观的“可变缓存”(通过将不可变缓存封装在原子中),但这些缓存的安全API并不真正“像映射一样”:lookup-or-miss是该 API 中最安全的功能,它提供了一种避免竞争条件和重复执行的方法,同时仍然可以在需要时按需查找可以(重新)计算请求的值。
在创建后用新的哈希表值“播种”缓存,以及驱逐操作,本质上都是修改操作。有一个“映射式”get操作很方便,但只有在您愿意接受请求的缓存条目已过期时返回nil(或“缺失”值)的情况下才这样做。
如readme中所指出:“core.cache API难以正确使用。”并且链接到了这篇文章
https://dev.to/dpsutton/exploring-the-core-cache-api-57al,讲述了将缓存视为“仅仅是映射”所导致的野外bug。