Roux.Blob.Trace (roux v0.2.1)

Copy Markdown View Source

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/5 keeps a name's keep: 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/3 and find/4 take limit:: they stat a name's traces and read and decode only the limit most 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}.

t()

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

dep()

@type dep() :: {term(), term()}

What computing a value observed: {what was looked at, what it was}.

t()

@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

fetch(store, name, opts \\ [])

@spec fetch(Roux.Blob.t(), term(), keyword()) :: [t()]

The traces kept under name, the most recently used first.

Options

  • :limit — read and decode only the limit most recently used (default: all of them). The others are looked at with one stat each.

find(source, name, observe, opts \\ [])

@spec find(Roux.Blob.t() | [t()], term(), (term() -> term()), keyword()) ::
  {:ok, term()} | :miss

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 the limit most recently used traces of name (fetch/3).

put(store, name, deps, value, opts \\ [])

@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.