Verifying traces: a value kept with what computing it observed, reused only while every observation still holds.
deps = [{{:file, path}, stamp(path)}, {{:env, "LANG"}, System.get_env("LANG")}]
:ok = Roux.Blob.Trace.put(store, {:parse, path}, deps, parsed)
Roux.Blob.Trace.find(store, {:parse, path}, fn
{:file, path} -> stamp(path)
{:env, name} -> System.get_env(name)
end)A name keeps several traces, one per distinct set of observations: a
value computed before an edit is found again once the edit is undone.
find/4 tries the most recently used first, observing each dependency
at most once, and refreshes the trace it returns (Roux.Blob's "Raw
I/O, and touches that refresh": within the store's refresh interval,
it is left alone), so "recently used" means used, not only written —
and a collection (Roux.Blob.gc/2) keeps traces in use, and the
entries their values name. Recency is known to within that interval.
Bounded history
Every set of observations a name ever met would otherwise stay until a collection, and a lookup reads and decodes them all. So history is bounded at both ends:
put/5keeps a name'skeep:most recently used traces (8 by default) and removes the rest, each renamed aside and then unlinked, so a reader finds a trace whole or not at all: what a lookup costs never grows with how long a name has been in use.fetch/3andfind/4takelimit:: they stat a name's traces and read and decode only thelimitmost recently used.
Several OS processes may put and look up one name at once: a trace removed as a lookup reaches it is passed over, so a lookup is a hit or a miss, never an error.
Summary
Types
What computing a value observed: {what was looked at, what it was}.
A trace: its name, its observations, the value they gave, where it is kept, when it was last used (its modification time, POSIX seconds), and the refresh interval of the store it came from.
Functions
The traces kept under name, the most recently used first.
The value of a trace under name whose observations all still hold —
observe.(what) equal (===) to what was observed — or :miss.
source is a store, or traces already fetched from one (fetch/3).
The trace found is touched: it is now the name's most recently used.
Keeps value under name, with the observations deps it was
computed from. Replaces a trace of the same name and observations,
and removes all but the keep: most recently used traces of the name
(this one among them).
Types
What computing a value observed: {what was looked at, what it was}.
@type t() :: %{ name: term(), deps: [dep()], value: term(), path: Path.t(), mtime: integer(), refresh: non_neg_integer() }
A trace: its name, its observations, the value they gave, where it is kept, when it was last used (its modification time, POSIX seconds), and the refresh interval of the store it came from.
Functions
@spec fetch(Roux.Blob.t(), term(), keyword()) :: [t()]
The traces kept under name, the most recently used first.
Options
:limit— read and decode only thelimitmost recently used (default: all of them). The others are looked at with onestateach.
The value of a trace under name whose observations all still hold —
observe.(what) equal (===) to what was observed — or :miss.
source is a store, or traces already fetched from one (fetch/3).
The trace found is touched: it is now the name's most recently used.
Options
:limit— with a store, look among only thelimitmost recently used traces ofname(fetch/3).
@spec put(Roux.Blob.t(), term(), [dep()], term(), keyword()) :: :ok | {:error, File.posix()}
Keeps value under name, with the observations deps it was
computed from. Replaces a trace of the same name and observations,
and removes all but the keep: most recently used traces of the name
(this one among them).
Options
:keep— how many traces the name keeps: a positive integer (default 8), or:infinity.