Determines whether a cached memo entry is still valid.
The validation algorithm recursively checks dependencies to avoid
recomputation when nothing has changed. Early cutoff is integrated:
even after a dependency re-executes, if its value hasn't changed
(changed_at stays old), downstream queries remain valid.
The algorithm
Four cases, checked in order:
- No memo — the query has never been computed. Return
:stale. - Already validated —
verified_at == current_rev. Return:valid.
- Code version — the entry was computed by another version of its
query's code than the registered one (
Roux.Query). Return:stale: it re-executes, and early cutoff keeps itschanged_atwhen the value comes back the same.
- Durability skip — no input at this query's durability level (or
below) has changed since
verified_at. Updateverified_atand return:validwithout walking dependencies. - Walk dependencies — for each dependency, call
ensure_fnto bring it up to date, then check itschanged_at. First stale dep short-circuits to:stale. All clean → updateverified_at, return:valid.
Dependency inversion
Validation accepts an ensure_fn callback rather than depending on
Runtime directly. Runtime provides its ensure_up_to_date/2 as the
callback, breaking the compile-time dependency cycle (see D13).
Summary
Functions
Validates whether a cached memo entry is still current.
Types
@type ensure_fn() :: (Roux.Database.t(), Roux.Memo.query_key() -> :ok)
Functions
@spec validate(Roux.Database.t(), Roux.Memo.query_key(), ensure_fn()) :: :valid | :stale
Validates whether a cached memo entry is still current.
Returns :valid if the cached value can be reused, :stale if the
query needs re-execution. Updates verified_at to the current revision
as a side effect when the entry is valid.
The ensure_fn callback is called for each dependency to bring it up
to date before checking its changed_at. This is typically
Runtime.ensure_up_to_date/2.