# `Outboxer.Relay`
[🔗](https://github.com/epinault/outboxer/blob/v0.1.0/lib/outboxer/relay.ex#L1)

The behaviour for a relay: the thing that takes messages out of the outbox
and hands them to a broker, a queue or anything else.

Outboxer ships relays for Kafka (`Outboxer.Relay.Kafka`), SQS
(`Outboxer.Relay.SQS`), Oban (`Outboxer.Relay.Oban`) and plain functions
(`Outboxer.Relay.Function`). Each of the first three is compiled only when
its dependency is in your project. Writing your own is a matter of
implementing this behaviour; see the "Custom relays" guide.

## Lifecycle

  1. `c:init/1` runs once when the Outboxer supervisor starts. Validate the
     options and return the state that later calls receive. Raise or return
     `{:error, reason}` to refuse to start with a bad configuration.
  2. `c:children/1` (optional) returns child specs to supervise next to the
     outbox workers, for example a broker client.
  3. `c:deliver/2` is called with a batch of envelopes, in delivery order.

## What `deliver/2` returns

  * `:ok` - every message in the batch was delivered.
  * `{:error, reason}` - nothing was delivered.
  * `{:partial, results}` - a map of envelope id to `:ok` or
    `{:error, reason}`. Ids left out of the map count as "not attempted"
    and stay in the outbox untouched.

## The ordering rule

For ordering to hold, a relay must not deliver a message after an earlier
message **of the same group** has failed. Leave it out of the results (or
report an error for it). `sequential/2` implements this rule for relays that
send one message at a time.

An exception raised from `deliver/2` is treated as `{:error, exception}` for
the whole batch.

# `deliver_result`

```elixir
@type deliver_result() ::
  :ok | {:error, term()} | {:partial, %{optional(pos_integer()) =&gt; result()}}
```

# `result`

```elixir
@type result() :: :ok | {:error, term()}
```

# `state`

```elixir
@type state() :: term()
```

# `children`
*optional* 

```elixir
@callback children(state()) :: [Supervisor.child_spec() | {module(), term()} | module()]
```

Child specs to supervise alongside the outbox workers.

# `deliver`

```elixir
@callback deliver(envelopes :: [Outboxer.Envelope.t(), ...], state()) :: deliver_result()
```

Delivers a batch of envelopes, given in delivery order.

# `init`

```elixir
@callback init(opts :: keyword()) :: {:ok, state()} | {:error, term()}
```

Validates the relay options and builds the relay state.

# `sequential`

```elixir
@spec sequential([Outboxer.Envelope.t()], (Outboxer.Envelope.t() -&gt; result())) ::
  deliver_result()
```

Delivers envelopes one by one with `fun`, honouring the ordering rule.

`fun` receives an envelope and returns `:ok` or `{:error, reason}`. Once a
message of a group fails, later messages of that group are skipped. Messages
without a group never block anything.

---

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