#cache #validity #frp #invalidation

renege

使用快速并发失效传播跟踪缓存有效性

3 个版本

0.1.2 2023年10月19日
0.1.1 2023年10月17日
0.1.0 2023年10月17日

#111 in 缓存

MIT/Apache

64KB
1.5K SLoC

renege

使用快速并发失效传播跟踪缓存有效性。

背景

每当计算机程序需要多次使用相同的数据或计算时,最简单、最易优化的方法通常是缓存它。然而,这引入了一个众所周知的棘手问题:防止过时数据。缓存的有效性取决于一系列 条件。这些是在缓存被填充时为真,并且在构建缓存时假设为真的陈述,但未来不一定为真。例如

  • 数据库的最后修改是在 2023-10-17T20:03:38
  • 用户有 view 权限
  • 文件 config.yaml 由特定的字符串组成
  • 模拟材料的杨氏模量为 200 GPa

如果缓存依赖于不再为真的条件,则缓存是 无效的。使用无效缓存中的数据可能会得到与直接检索/计算数据不同的结果,从而使优化无效。

解决此问题的一般方法分为两大类

  • 基于拉取的:当访问缓存时,请求者重新检查缓存依赖的所有条件。如果有任何条件为假,则缓存无效。

    • 优点:简单且易于实现

    • 缺点:通常较慢,尤其是在有很多条件且/或检查成本较高的情况下。检查的性能惩罚可能会超过缓存的好处

  • 基于推送的:当条件变为假时,依赖于它的缓存被标记为无效。

    • 优点:非常快。当访问缓存时,请求者只需检查它是否被标记为无效

    • 缺点:难以实现。需要主动监视条件,并跟踪依赖于它们的缓存。当缓存依赖于其他缓存时,需要跟踪整个依赖图,以实现有效的失效。

我们的方法

Renegade 通过为您处理所有跟踪和记录工作,简化了基于推送的缓存失效的实现。API非常简单

Condition: 一个缓存可以依赖的条件。在释放时自动失效。

Token: 跟踪一组任意条件的有效性。

use renege::{Condition, Token};

// Create conditions
let pigs_cant_fly = Condition::new();
let water_is_wet = Condition::new();

// Use .token() to create a Token which tracks the validity of a single Condition
// Tokens can be combined using the & operator
let normality = pigs_cant_fly.token() & water_is_wet.token();
assert!(normality.is_valid());

// Conditions are invalidated when dropped
drop(water_is_wet);

// Use .is_valid() to check if all of the Conditions a Token depends on are still valid
assert!(!normality.is_valid());

无运行时依赖