# `Ankusa.Health.SinkErrors`
[🔗](https://github.com/jamescarr/ankusa/blob/ankusa-v0.7.0/packages/ankusa/lib/ankusa/health/sink_errors.ex#L1)

The last failed publish of every sink under `wal: :none`, for
`Ankusa.Health.Check.Sinks`.

The direct ack path (`Ankusa.Edge.Publish`) has no queue and no retry, so a
broker that refuses every publish shows nowhere but in the `503`s. It calls
`record/3` with each sink's outcome: a failure stores the monotonic time under
the sink's identity (its module and options, hashed), a success removes the
entry, so a sink that came back reads healthy at once instead of after the
window. Both are one ETS write in the request process, no message to a
singleton.

One process per instance owns the table and sweeps entries older than
`listener.ready_sink_error_window_ms` every window, so the table holds at most the
sinks that failed within it. It is also capped at `@max_entries`: a failure of
a sink beyond the cap is not recorded (a node with that many failing sinks is
unready already, and the sweep makes room as they age out).

# `child_spec`

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

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `failing`

```elixir
@spec failing(atom(), pos_integer()) :: non_neg_integer()
```

How many sinks failed their last publish within `window_ms`.

# `record`

```elixir
@spec record(atom(), {module(), keyword()}, :ok | :error) :: :ok
```

Note that the sink `{mod, opts}` just published with `outcome` (`:ok` or
`:error`). `:ok` when no tracker runs (an instance that is not `wal: :none`).

# `start_link`

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

# `table`

```elixir
@spec table(atom()) :: atom()
```

The table of one instance (the instance name is operator config, never request input).

---

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