# `Mailglass.SuppressionStore.ETS`
[🔗](https://github.com/szTheory/mailglass/blob/v1.0.0/lib/mailglass/suppression_store/ets.ex#L1)

ETS-backed implementation of `Mailglass.SuppressionStore` (D-28).

Test-speed + narrow production use case (single-node, read-heavy,
sub-100-entry lists). Default impl remains
`Mailglass.SuppressionStore.Ecto` — adopters override via:

    config :mailglass, :suppression_store, Mailglass.SuppressionStore.ETS

## Key shape

ETS keys: `{tenant_id, address, scope, stream_or_nil}` (mirrors
Ecto UNIQUE constraint `(tenant_id, address, scope, COALESCE(stream, ''))`).

## Lookup algorithm

`check/2` tries three scopes in order (matching Ecto's OR-union):

1. `{tenant_id, address, :address, nil}`
2. `{tenant_id, domain(address), :domain, nil}`
3. `{tenant_id, address, :address_stream, stream}` (only when stream is set)

First hit wins. Expiry filter at read time (expired entries not returned).

## UPSERT behaviour

`record/2` with the same `{tenant_id, address, scope, stream}` key
overwrites the existing entry (equivalent to Ecto's `on_conflict: {:replace, [...]}`).

## Test override pattern (RESEARCH §5.3)

Tests scope by unique `tenant_id` to avoid cross-test interference — no
per-pid ownership needed (ETS table is global). Tests that need a clean
slate between runs call `Mailglass.SuppressionStore.ETS.reset/0`.

# `reset`
*since 0.1.0* 

```elixir
@spec reset() :: :ok
```

Clears the ETS suppression table. Test-only helper.

Use this in `setup` blocks to ensure a clean slate between tests.

---

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