Global revision counter and per-durability-level change tracking.
The revision is a monotonically increasing integer representing a version of the world. It increments every time any input changes. Durability tracking enables skipping validation of subgraphs rooted in high-durability inputs (see D6).
Durability levels
Durability classifies how often an input changes:
:high— almost never (standard library, language definitions):medium— occasionally (project source files not being edited):low— constantly (the file currently open in the editor)
During validation, if a query's entire dependency subgraph has minimum
durability :high and last_changed(rev, :high) < query.verified_at,
the query can skip graph traversal entirely.
Concurrency
All operations are lock-free. A revision becomes current only after its
change is visible: advance/3 makes the change with the revision it is
about to publish, raises the durability slot, and only then publishes the
revision with a compare-and-swap. A reader that captured a revision and
then saw the old value therefore captured one older than the change, and
its result validates as stale. Publishing first and writing after would
let a reader record the old value as current at the new revision. A
concurrent advance that publishes first makes the write run again with
the next revision.
last_changed_at_or_above/2 reads multiple atomics slots without
cross-slot atomicity, and a slot can briefly lead the counter — the worst
case is a spurious validation (conservative, not incorrect).
Summary
Functions
Publishes the next revision, recording that level changed at it, and
returns that revision.
Reads the current global revision. Lock-free.
Returns the revision at which the given durability level last had an input change. Returns 0 if no input at that level has ever changed.
Returns the maximum revision across all durability levels at or above the given level. Used for the durability optimization during validation.
Creates a new revision tracker. Initial revision is 0 (no inputs set yet).
Restores atomics state from a snapshot produced by snapshot/1.
Captures the current state of all atomics for manifest persistence.
Types
@type durability() :: :high | :medium | :low
@type revision() :: non_neg_integer()
@type t() :: %Roux.Revision{ counter: :atomics.atomics_ref(), durability: :atomics.atomics_ref(), untracked: :atomics.atomics_ref() | nil }
Functions
@spec advance(t(), durability(), (revision() -> term())) :: revision()
Publishes the next revision, recording that level changed at it, and
returns that revision.
write receives the revision about to be published and makes the change
visible before it is: a change made after its revision is current could be
missed by a reader at that revision. When a concurrent advance publishes
first, write runs again with the next revision, so it must leave the
change stamped with the last revision it receives. A change already made
before the call (a deletion, say) needs no write.
Reads the current global revision. Lock-free.
@spec last_changed(t(), durability()) :: revision()
Returns the revision at which the given durability level last had an input change. Returns 0 if no input at that level has ever changed.
Used during validation to skip subgraphs.
@spec last_changed_at_or_above(t(), durability()) :: revision()
Returns the maximum revision across all durability levels at or above the given level. Used for the durability optimization during validation.
Higher durability means more stable (changes less often):
:lowincludes:low+:medium+:highchanges (all levels).:mediumincludes:medium+:highchanges.:highincludes only:highchanges.
@spec new() :: t()
Creates a new revision tracker. Initial revision is 0 (no inputs set yet).
@spec restore(t(), %{ counter: revision(), high: revision(), medium: revision(), low: revision() }) :: :ok
Restores atomics state from a snapshot produced by snapshot/1.
Used during manifest loading to resume the revision timeline across VM restarts.
@spec snapshot(t()) :: %{ counter: revision(), high: revision(), medium: revision(), low: revision() }
Captures the current state of all atomics for manifest persistence.
Returns a plain map that can be serialized with :erlang.term_to_binary/1.