Mead.Ledger (mead v0.1.1)

Copy Markdown View Source

Ledger of what the router sends to providers, kept in memory and journaled to disk.

It listens to [:mead, :router, :route] and keeps:

  • a counter row per session, dropped by forget/2 when the session is deleted;
  • a counter row per {route, provider};
  • a ring of the last 100 turns;
  • a count of the events it could not read.

The process owns the two public ETS tables, so they live as long as it does. The :telemetry handler writes to them in the process that emitted the event, which keeps the ledger off the critical path of every conversation. Because :telemetry detaches a handler that raises, the handler never raises: an event it cannot read is counted as dropped.

With a storage directory (config :mead, :storage, directory: ..., or the :directory option) the handler also casts every event to the ledger, which appends it to ledger.journal (Mead.Ledger.JournalFile) in batches of up to 100 events or 50 milliseconds, with one sync per batch, and again when it stops. A turn never waits for the disk. At boot the ledger replays the journal into the tables before it attaches the handler. A crash of the whole VM loses the events still waiting for the next batch. Without a directory the ledger is memory only and receives no casts.

A counter row is {key, turns, failures, input_tokens, output_tokens, duration_ms}.

Summary

Functions

Returns a specification to start this module under a supervisor.

Writes the events that are pending now and returns :ok, or the error of the write.

Forgets the aggregate of a session; the route totals keep what the session added.

Returns up to limit of the most recent turns, newest first; at most 100 are kept.

Returns the totals per {route, provider}.

Returns the totals of one session, or {:error, :not_found} if it was never seen.

Starts the ledger.

Returns the totals over every route, plus dropped, the events that could not be read.

Functions

child_spec(init_arg)

@spec child_spec(keyword()) :: Supervisor.child_spec()

Returns a specification to start this module under a supervisor.

See Supervisor.

flush(ledger \\ __MODULE__)

@spec flush(GenServer.server()) :: :ok | {:error, term()}

Writes the events that are pending now and returns :ok, or the error of the write.

The ledger does this by itself within 50 milliseconds; this is for the caller that needs the journal to be current, such as a test.

forget(session_id, ledger \\ __MODULE__)

@spec forget(String.t(), GenServer.server()) :: :ok

Forgets the aggregate of a session; the route totals keep what the session added.

recent(limit, ledger \\ __MODULE__)

@spec recent(non_neg_integer(), GenServer.server()) :: [map()]

Returns up to limit of the most recent turns, newest first; at most 100 are kept.

route_totals(ledger \\ __MODULE__)

@spec route_totals(GenServer.server()) :: %{required({atom(), atom()}) => map()}

Returns the totals per {route, provider}.

session_totals(session_id, ledger \\ __MODULE__)

@spec session_totals(String.t(), GenServer.server()) ::
  {:ok, map()} | {:error, :not_found}

Returns the totals of one session, or {:error, :not_found} if it was never seen.

start_link(options \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Starts the ledger.

Options

  • :name - registered name; defaults to Mead.Ledger.
  • :event - the telemetry event to listen to; defaults to [:mead, :router, :route]. Tests give each ledger its own event so they do not see each other's turns.
  • :directory - where the journal lives, or nil for a ledger that is memory only; defaults to Mead.Session.Store.directory/0.
  • :journal_max_bytes - the journal size from which it is rewritten as a checkpoint; defaults to Mead.Session.Store.ledger_journal_max_bytes/0.

totals(ledger \\ __MODULE__)

@spec totals(GenServer.server()) :: map()

Returns the totals over every route, plus dropped, the events that could not be read.