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 marks the trace it returns used (mark_used/1:
within the store's refresh interval, it is left alone — Roux.Blob's
"Raw I/O, and touches that refresh"), 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.
Versions
A trace is never replaced in place: a rename onto a name is not atomic
everywhere, and a reader that meets its moment finds no trace at all.
Each value a trace holds is a version, a file of its own named by the
digest of its observations, the time it was written and the digest of
its bytes, linked into place and never changed — the store's CAS
entries' rule (Roux.Blob's "Entries"). A put of another value under
the same observations adds a version, then removes the versions it
found there that were written more than a second before it: a reader
that listed one a moment ago can still read it. A lookup reads each
set of observations' newest version, by the time in its name (a
modification time, in whole seconds, cannot order two versions of one
second); one removed between its listing and its read is looked for
again. So a reader always finds a complete version, and after a put,
the one it wrote. Putting the bytes of the newest version again writes
nothing, and marks it used.
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 every trace written or used within the store's window (Roux.Blob.window/1, twice its refresh interval: a trace used within the interval was marked within the window), however many: an older one goes, renamed aside and then removed (or put back, if a lookup marked it used meanwhile), so a reader finds a trace whole or not at all.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: each set
of observations' newest version.
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 marked used (mark_used/1): it is now the name's
most recently used. One removed as it was found — taken by a
collection, or superseded — is not returned: with a store, it is
looked for again.
Marks a trace fetched (fetch/3) used, as find/4 marks the one it
returns: its modification time set to now, unless that is within its
store's refresh interval. :gone when the trace was removed since it
was read, which can happen only to one unused for longer than the
interval: a collection may be taking the entries its value names, so
that value is not to be used.
Keeps value under name, with the observations deps it was
computed from: a new version of the trace of those observations (see
"Versions"), and the only one once this returns, unless another
process put one meanwhile. Keeps the keep: most recently used traces
of the name, and every one used within the window (see "Bounded
history").
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: each set
of observations' newest version.
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 marked used (mark_used/1): it is now the name's
most recently used. One removed as it was found — taken by a
collection, or superseded — is not returned: with a store, it is
looked for again.
Options
:limit— with a store, look among only thelimitmost recently used traces ofname(fetch/3).
@spec mark_used(t()) :: :ok | :gone
Marks a trace fetched (fetch/3) used, as find/4 marks the one it
returns: its modification time set to now, unless that is within its
store's refresh interval. :gone when the trace was removed since it
was read, which can happen only to one unused for longer than the
interval: a collection may be taking the entries its value names, so
that value is not to be used.
@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: a new version of the trace of those observations (see
"Versions"), and the only one once this returns, unless another
process put one meanwhile. Keeps the keep: most recently used traces
of the name, and every one used within the window (see "Bounded
history").
Options
:keep— how many traces the name keeps beyond the window: a positive integer (default 8), or:infinity.