# `SquatchMail.Capture.Recorder`
[🔗](https://github.com/Axio-Intelligence/Squatch-Mail/blob/v0.1.0/lib/squatch_mail/capture/recorder.ex#L1)

Persists captured emails off the process that sent them.

`SquatchMail.Capture`'s telemetry handler runs synchronously in the
caller's process — the same process that just called
`Mailer.deliver/2` — so it must return immediately. It hands each captured
email's attrs to this GenServer with `GenServer.cast/2` (never `call/2`,
which would block the caller waiting for a reply) and this process does
the actual `SquatchMail.Tracker.record_email/1` write, off the caller.

## Backpressure

Two independent limits bound the work this GenServer takes on, so a burst
of sends can't turn into unbounded memory growth or a flooded database
connection pool:

  * `SquatchMail.Config.max_queue/0` (default `10_000`) bounds how many
    captures may be *waiting* (queued in this process's own FIFO queue,
    not yet handed to a worker). Once the number of queued-plus-in-flight
    captures reaches this limit, new captures are dropped outright:
    `[:squatch_mail, :capture, :dropped]` fires and a warning logs — at
    most once a minute, so a sustained overload doesn't itself become a
    logging flood.
  * `SquatchMail.Config.max_concurrency/0` (default `50`) bounds how many
    captures are being persisted *at once*. Each in-flight persist checks
    out a connection from the host's `Repo` pool; without this cap, a
    burst large enough to exceed `:max_queue` would also be large enough
    to try to check out thousands of connections simultaneously and
    starve every other query the host app is running. Workers are
    unlinked (`Task.Supervisor.async_nolink/2`) so one crashing persist
    can't take this GenServer down with it.

Queued work is drained into new workers as running ones finish, so the
effective throughput is `:max_concurrency` persists in flight at any given
moment, with up to `:max_queue` more waiting their turn.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `record`

```elixir
@spec record(map()) :: :ok
```

Enqueues an email's attrs for persistence.

Non-blocking: returns immediately regardless of whether the attrs were
accepted, queued, or dropped for being over capacity.

# `start_link`

```elixir
@spec start_link(Keyword.t()) :: GenServer.on_start()
```

Starts the recorder. Only one is expected per application (registered
under this module's name).

---

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