# `Mead.Ledger`
[🔗](https://github.com/albertosca/mead/blob/main/lib/mead/ledger.ex#L1)

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}`.

# `child_spec`

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

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `flush`

```elixir
@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`

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

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

# `recent`

```elixir
@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`

```elixir
@spec route_totals(GenServer.server()) :: %{required({atom(), atom()}) =&gt; map()}
```

Returns the totals per `{route, provider}`.

# `session_totals`

```elixir
@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`

```elixir
@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`

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

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
