# Connection warming

Connection warming <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.2.0">since 1.2.0</a> keeps a small number of pre-connected, authenticated NNTP sockets standing by on each pooled provider. Playback and queue work borrow one instantly instead of paying the TCP + TLS + login handshake first — which is most of the wait between pressing play and the first bytes moving after an idle period.

## How it works

Each pooled provider gets a **warm floor**: a target count of idle, ready-to-use connections. By default the floor is **2** (1 when the **Provider Connection Limit** is 1) <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.5.0">since 1.5.0</a>; releases before 1.5.0 derived roughly one sixth of the limit, clamped to 1–8. An explicit `usenet.warm-connections.floor` value is unchanged.

<Flow
	numbered={false}
	core={['Warm floor']}
	steps={[
		{
			title: 'In the background',
			nodes: [
				['Sweeper', 'Runs every half idle timeout, 30 seconds by default'],
				['DATE ping', 'Keeps each warm socket alive before the provider drops it'],
				['Refill', 'Replaces stale sockets and closes idle ones above the floor'],
			],
		},
		{
			title: 'When playback or the queue needs a connection',
			nodes: [
				['Playback, queue', 'Ask the pool for a connection'],
				['Warm floor', 'Hands over a socket that is already logged in'],
				['Usenet provider', 'Gets the first request with no TCP, TLS or login wait'],
			],
		},
	]}
/>

- A background sweeper runs every half of the [idle connection timeout](https://www.infinidysk.com/v1/configuration/streaming/) (default: every 30 seconds). It refills the floor when sockets were lost and reaps idle connections **only above the floor** — warm connections are never closed just for being idle.
- Before a provider's own idle timeout can drop a warm socket, the sweeper pings it with a lightweight NNTP `DATE` command. A failed ping means the socket went stale; it is disposed and the floor refills.
- Warm connections never hold download permits, so the full configured connection width stays available to real work.
- Disabled providers are never warmed <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.5.0">since 1.5.0</a>: infinidysk opens no connections to a disabled account and sends no keep-alives until the provider is enabled again.
- Backup-only providers are demand-only <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.6.1">since 1.6.1</a>: they keep no warm floor and receive no keep-alives, so a last-resort article-fill account is not dialed in the background. Failover still opens a connection when a higher-priority provider misses an article; that first fallback pays the handshake, and the connection is reused until it goes idle. **Backup + Stats** providers keep their floor.
- If a provider is unreachable at startup, warming does not spin or block startup — retries resume once the provider circuit permits new opens. When infinidysk learns that a provider's real connection limit is lower than configured, the floor shrinks with it.
- Pending warm-up work has a 15-second acquisition budget and stops when its provider circuit trips. Factories already opening keep their own socket-open deadline. Warm-up never claims the half-open recovery probe.
- Warm-up opens are judged differently from foreground opens <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.5.0">since 1.5.0</a>. While a provider still has at least one established connection, a warm-up open that fails or times out never trips the circuit: the pool backs off its own handshakes (500 ms doubling to 60 seconds), the floor refills on a later sweep, and a warning naming the reason is logged at most once every five minutes per provider. Only a warm-up failure on a provider with **no** established connections reaches the circuit, so an unreachable provider is still benched before the first request needs it. Floor refills use a connection-open budget of at least 10 seconds (the configured [fresh connection open timeout](https://www.infinidysk.com/v1/configuration/streaming/) when that is longer); read-start warm-up keeps the configured budget because it shares handshake slots with playback. See [provider acquisition waits](https://www.infinidysk.com/v1/configuration/streaming/#provider-acquisition-waits).

## What you see in the header

The **Connections** indicator in the top navigation shows pooled-provider totals, updated live (about five times per second):

- `5/50` — live connections across pooled providers versus their combined maximum.
- `2 active · 3 warm` — connections currently transferring versus idle warm ones. The warm part only appears while at least one warm connection is standing by.
- Hovering explains: "Warm connections are pre-connected to your Usenet providers so playback can start faster."

Other states: a spinner with **Connecting** until the first update arrives, **Reconnecting** while the live UI connection recovers from a brief drop, and `—` / **No providers** when no Usenet provider is configured. The indicator is hidden on narrow screens. Backup-only providers are excluded from the totals.

**Settings → Usenet** shows the same live counts on each provider card.

## Tuning

| Setting | What it does |
|---|---|
| Warm connections<br/>`usenet.warm-connections.enabled` | Keep a small pool of pre-connected sockets per provider. Default on. |
| Warm floor<br/>`usenet.warm-connections.floor` | Idle sockets kept ready per provider, capped at the Provider Connection Limit. Before 1.5.0 the default derived one sixth of the limit, clamped to 1–8. Default `2`. |
| Read-start warm-up <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.3.0">since 1.3.0</a><br/>`usenet.read-start-warmup.enabled` | For a long buffered read, open missing pooled-provider connections in parallel as the first segment starts. Default on. |

There is no settings-UI toggle; set the keys as [headless environment variables](https://www.infinidysk.com/v1/configuration/headless/) (`NZBDAV_CONFIG__USENET__WARM_CONNECTIONS__ENABLED` / `NZBDAV_CONFIG__USENET__WARM_CONNECTIONS__FLOOR`). Changes take effect on the next provider save or restart — connection pools are not rebuilt when these keys change alone.

Read-start warm-up is enabled by default. Each newly opened WebDAV stream
snapshots the value. Reads below
about 8.4 MB, reads needing fewer than two connection batches, backup-only providers,
providers with an open circuit, and providers that are backing off after a failed
connection open <a class="since" href="https://github.com/infinidysk/infinidysk/releases/tag/v1.5.0">since 1.5.0</a> are skipped. The target is bounded by the
stream's article window, download limit, provider connection limits, and any
per-provider transfer cap. Opened sockets return to the normal idle pool; the
warm floor still decides how many remain connected later. Set
`NZBDAV_CONFIG__USENET__READ_START_WARMUP__ENABLED=false` and restart to restore
demand-only expansion.

## Cost and trade-offs

- Warm sockets are real connections and count against the provider plan's connection limit, even while idle.
- Read-start warm-up can create up to three TCP/TLS/AUTHINFO sessions at once and pauses by itself while a provider is backing off after a failed open. Turn it off if a provider rejects legal connection bursts.
- Keepalive traffic is negligible: one tiny `DATE` exchange per warm socket per sweep.
- On very small plans or [memory-constrained hosts](https://www.infinidysk.com/v1/operations/memory-constrained-hosts/), lower the floor or disable warming entirely.

[Usenet](https://www.infinidysk.com/v1/configuration/usenet/) · [NNTP pipelining](https://www.infinidysk.com/v1/features/nntp-pipelining/) · [Streaming and seeking](https://www.infinidysk.com/v1/features/streaming-seeking/)