Fetch
fetch 操作用来计算某个查询的值。
只要可能,它就会优先复用已经缓存(memoized)的值。
输入查询
输入查询只需要直接从表中读取结果即可。
Interned 查询
Interned 查询会把输入映射到一个 hashmap 中, 查找是否已经存在对应的整数 id。 如果不存在,就创建一个新值。
派生查询
派生查询的逻辑要更复杂一些。 这里先总结高层思路; 如果想继续深入,流程图会很有帮助。 术语一节也同样值得配合阅读; 在某些地方,我们会在术语第一次出现时直接链接过去。
- 如果找到了现有的 memo, 我们先检查它是否已经在当前 revision 中被 verified。 如果是,就可以直接返回缓存值。
- 否则,如果这个 memo 里保存了缓存值,
那么我们就必须检查 dependencies 是否发生了变化:
- 设
R为这个 memo 上一次被验证的修订; 我们想知道自修订R以来,依赖中是否有任何一个发生了变化。 - 首先检查 durability。
对每个 memo,我们都会记录其依赖的最小 durability。
如果某个 memo 的 durability 是
D, 并且自上次验证以来,没有任何 durability 为D的输入发生过变化, 那么无需额外工作,也可以把这个 memo 视为已验证。 - 如果 durability 检查还不够,
那就必须逐个检查依赖。
做法是遍历每个依赖
D, 并调用 maybe changed after 来判断D自修订R之后是否已经变化。 - 如果没有任何依赖变化:
- 那么就可以把这个 memo 标记为已验证,并直接返回它缓存的值。
- 设
- 如果依赖发生了变化,或者 memo 根本没有保存缓存值:
- 那么就执行用户的查询函数。
- 接下来,计算这个缓存值最后一次发生变化的修订:
- Backdate: 如果之前存在旧的缓存值,并且新值与旧值相等,
那么我们就可以对这个 memo 做 backdate,
也就是沿用之前的
changed at修订。- 借助 backdating,
就有可能出现这样的情况:查询的某个依赖在修订
R1中变化了, 但查询本身的输出却是在某个更早的修订R2中最后一次变化的,其中R2 < R1。
- 借助 backdating,
就有可能出现这样的情况:查询的某个依赖在修订
- 否则,就使用当前修订。
- Backdate: 如果之前存在旧的缓存值,并且新值与旧值相等,
那么我们就可以对这个 memo 做 backdate,
也就是沿用之前的
- 然后为这个新值构造一个新的 memo 并返回。