# `Chronicle.Connections.Connection`
[🔗](https://github.com/Cratis/Chronicle.Elixir/blob/main/lib/chronicle/connections/connection.ex#L4)

Manages a resilient Chronicle gRPC channel with automatic reconnection.

`Connection` is a `GenServer` that maintains a gRPC channel to a Chronicle
kernel. It handles connection failures with exponential backoff and notifies
callers waiting for the connection to become ready.

On every connect/reconnect attempt it re-resolves the connection string's
addresses — a static multi-host list, or a fresh DNS SRV lookup for
`chronicle+srv://` (see `Chronicle.Connections.DnsResolver`) — and picks one
via the configured load-balancer strategy (see `Chronicle.Connections.LoadBalancer`)
before dialing. Re-resolving on every attempt means a `chronicle+srv://`
record change, or a host coming back up, is picked up automatically without
a separate background refresh loop.

## Usage

Start it as part of your supervision tree, typically via `Chronicle.Client`:

    {Chronicle.Client,
      connection_string: "chronicle://localhost:35000",
      ...}

Or start it directly for lower-level use:

    {:ok, conn} = Chronicle.Connections.Connection.start_link(
      connection_string: "chronicle://localhost:35000",
      name: :my_conn
    )
    :ok = Chronicle.Connections.Connection.connect(:my_conn)
    {:ok, channel} = Chronicle.Connections.Connection.channel(:my_conn)

## Options

  * `:connection_string` — a `Chronicle.Connections.ConnectionString` struct or
    a connection string binary. Defaults to `ConnectionString.default/0`.
  * `:server_address` — alternative to `:connection_string`; a `"host:port"` string.
  * `:skip_tls_validation` — overrides the connection string's `skipTlsValidation`
    query option. When `true`, the gRPC channel and the OAuth2 token fetch skip TLS
    certificate chain validation instead of validating against the system trust store.
  * `:load_balancer` — overrides the connection string's `loadBalancer` query
    option (`:least_connections`, `:round_robin`, or `:random`).
  * `:grpc_options` — additional options passed to `GRPC.Stub.connect/2`.
  * `:retry_attempts` — accepted for backward compatibility and ignored. The
    connection keeps reconnecting, with backoff, for as long as it runs.
  * `:reconnect_base_delay` — base reconnect delay in milliseconds (default: 1000).
  * `:reconnect_max_delay` — maximum reconnect delay in milliseconds (default: 10000).
  * `:auto_connect` — whether to connect immediately on start (default: `true`).
  * `:resolve_fun` — resolves a `chronicle+srv://` host to candidate addresses.
    Defaults to `Chronicle.Connections.DnsResolver.resolve/2`. Test-only seam,
    mirroring `:connect_fun`/`:disconnect_fun` below.
  * `:probe_fun` — performs the `:least_connections` HTTP probe. Defaults to
    `Chronicle.Connections.LoadBalancer.default_probe/3`. Test-only seam.
  * `:connect_fun` — test-only seam replacing `GRPC.Stub.connect/2`.
  * `:disconnect_fun` — test-only seam replacing `GRPC.Stub.disconnect/1`.
  * `:name` — registered name for the GenServer process.

# `option`

```elixir
@type option() ::
  {:connection_string, String.t() | Chronicle.Connections.ConnectionString.t()}
  | {:server_address, String.t()}
  | {:skip_tls_validation, boolean()}
  | {:load_balancer,
     Chronicle.Connections.ConnectionString.load_balancer_strategy()}
  | {:grpc_options, keyword()}
  | {:retry_attempts, non_neg_integer()}
  | {:reconnect_base_delay, non_neg_integer()}
  | {:reconnect_max_delay, non_neg_integer()}
  | {:resolve_fun,
     (String.t(), String.t() | nil -&gt;
        {:ok, [Chronicle.Connections.ConnectionString.ServerAddress.t()]}
        | {:error, term()})}
  | {:probe_fun, Chronicle.Connections.LoadBalancer.probe_fun()}
  | {:connect_fun, (String.t(), keyword() -&gt; {:ok, term()} | {:error, term()})}
  | {:disconnect_fun, (term() -&gt; any())}
  | {:name, GenServer.name()}
  | {:auto_connect, boolean()}
  | {:dedicated_session_channel, boolean()}
```

# `channel`

```elixir
@spec channel(GenServer.server()) :: {:ok, term()} | {:error, :not_connected}
```

Returns `{:ok, channel}` when connected, or `{:error, :not_connected}`.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `connect`

```elixir
@spec connect(GenServer.server(), timeout()) :: :ok | {:error, :timeout}
```

Waits until the connection is ready, or returns `{:error, :timeout}`.

Blocks the caller until the gRPC channel is established or `timeout`
milliseconds elapse.

# `connected?`

```elixir
@spec connected?(GenServer.server()) :: boolean()
```

Returns `true` if the channel is currently connected.

# `disconnect`

```elixir
@spec disconnect(GenServer.server()) :: :ok
```

Disconnects the active channel and stops reconnect attempts.

The process exits normally after this call.

# `reconnect`

```elixir
@spec reconnect(GenServer.server()) :: :ok
```

Drops the current channel and dials a fresh one.

For callers with independent evidence that the channel is unusable — such as
the session watchdog detecting missed keepalives. A channel can die in ways
this process cannot observe: the gRPC adapter reports transport death to the
process that dialed (a completed connect task, not this server), and an
expired auth token fails every new RPC while the transport stays healthy.
Redialing re-resolves addresses and re-fetches authentication headers.
No-op while a connect attempt is already in progress.

# `session_channel`

```elixir
@spec session_channel(GenServer.server()) :: {:ok, term()} | {:error, :not_connected}
```

Returns `{:ok, channel}` for the session (the Connect stream and its keepalive
answers) when connected, or `{:error, :not_connected}`.

With `dedicated_session_channel: true` this is a second HTTP/2 connection to
the same server as `channel/1`, dialed and torn down together with it. Large
observer results queued on the main connection then cannot hold back the
keepalive answers: the gRPC adapter queues request bodies for a whole
connection in one queue, so a send waiting on flow control delays every send
behind it. Without the option, or when the second dial fails, it is the main
channel.

# `start_link`

```elixir
@spec start_link([option()]) :: GenServer.on_start()
```

Starts a Chronicle connection process linked to the current process.

---

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