# `Resiliency.Race`
[🔗](https://github.com/yoavgeva/resiliency/blob/v0.6.0/lib/resiliency/race.ex#L1)

Run all functions concurrently and return the first successful result.

## When to use

  * Querying multiple replicas or services in parallel and using whichever
    responds first — e.g., hitting a primary and a read-replica
    simultaneously for a latency-sensitive endpoint.
  * Sending the same request to multiple regions or providers and taking
    the fastest response.

## How it works

All functions are spawned concurrently as monitored processes. The caller
enters a receive loop: the first task to send a successful result wins,
all remaining tasks are killed via `Process.exit(pid, :kill)`, and
`{:ok, result}` is returned. If a task crashes (`:DOWN` message), it is
removed from the active set and the race continues with the survivors.
If all tasks crash, `{:error, :all_failed}` is returned.

## Algorithm Complexity

| Time | Space |
|---|---|
| O(n) spawns + O(n) monitor cleanup where n = number of functions | O(n) — one monitored process per function |

## Examples

    iex> Resiliency.Race.run([fn -> :hello end])
    {:ok, :hello}

    iex> Resiliency.Race.run([
    ...>   fn -> Process.sleep(100); :slow end,
    ...>   fn -> :fast end
    ...> ])
    {:ok, :fast}

    iex> Resiliency.Race.run([fn -> raise "boom" end])
    {:error, :all_failed}

    iex> Resiliency.Race.run([])
    {:error, :empty}

Crashed tasks are skipped — the race continues:

    iex> Resiliency.Race.run([
    ...>   fn -> raise "primary down" end,
    ...>   fn -> :backup end
    ...> ])
    {:ok, :backup}

With a timeout:

    Resiliency.Race.run([
      fn -> fetch_from_slow_service() end,
      fn -> fetch_from_another_service() end
    ], timeout: 5_000)

## Telemetry

All events are emitted in the caller's process via `:telemetry.span/3`. See
`Resiliency.Telemetry` for the complete event catalogue.

### `[:resiliency, :race, :run, :start]`

Emitted before tasks are spawned.

**Measurements**

| Key | Type | Description |
|-----|------|-------------|
| `system_time` | `integer` | `System.system_time()` at emission time |

**Metadata**

| Key | Type | Description |
|-----|------|-------------|
| `count` | `integer` | Number of functions in the race |

### `[:resiliency, :race, :run, :stop]`

Emitted after the first result is received.

**Measurements**

| Key | Type | Description |
|-----|------|-------------|
| `duration` | `integer` | Elapsed native time units (`System.monotonic_time/0` delta) |

**Metadata**

| Key | Type | Description |
|-----|------|-------------|
| `count` | `integer` | Number of functions in the race |
| `result` | `:ok | :error` | Outcome of the winning result |

### `[:resiliency, :race, :run, :exception]`

Emitted if `run/2` raises or exits (e.g., an unexpected error in the collection logic).

**Measurements**

| Key | Type | Description |
|-----|------|-------------|
| `duration` | `integer` | Elapsed native time units |

**Metadata**

| Key | Type | Description |
|-----|------|-------------|
| `count` | `integer` | Number of functions in the race |
| `kind` | `atom` | Exception kind (`:error`, `:exit`, or `:throw`) |
| `reason` | `term` | The exception or exit reason |
| `stacktrace` | `list` | Stack at the point of the exception |

# `task_fun`

```elixir
@type task_fun() :: (-&gt; any())
```

# `run`

```elixir
@spec run(
  [task_fun()],
  keyword()
) :: {:ok, any()} | {:error, :all_failed | :timeout | :empty}
```

Run all functions concurrently. Return the first successful result and cancel the rest.

Spawns all functions as concurrent tasks. The first task to complete
successfully wins — its result is returned and all remaining tasks are
shut down. If a task crashes (raise, exit, or throw), it is skipped and
the race continues with the remaining tasks.

Returns `{:ok, result}` from the first function that completes successfully.
If all functions fail, returns `{:error, :all_failed}`.
If no function succeeds within the timeout, returns `{:error, :timeout}`.
An empty list returns `{:error, :empty}`.

## Parameters

* `funs` -- a list of zero-arity functions to race concurrently.
* `opts` -- keyword list of options. Defaults to `[]`.
  * `:timeout` -- milliseconds or `:infinity`. Defaults to `:infinity`.

## Returns

`{:ok, result}` from the first function that completes successfully, `{:error, :all_failed}` if all functions fail, `{:error, :timeout}` if no function succeeds within the timeout, or `{:error, :empty}` if the input list is empty.

## Examples

    iex> Resiliency.Race.run([fn -> :hello end])
    {:ok, :hello}

    iex> Resiliency.Race.run([
    ...>   fn -> Process.sleep(100); :slow end,
    ...>   fn -> :fast end
    ...> ])
    {:ok, :fast}

    iex> Resiliency.Race.run([fn -> raise "boom" end])
    {:error, :all_failed}

    iex> Resiliency.Race.run([])
    {:error, :empty}

---

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