Skip to content

fix(library): widen heavy library-fetch timeout to 30s (issue #41) - #90

Open
ndandan wants to merge 2 commits into
Shoshuo:mainfrom
ndandan:pr/library-timeout-fix
Open

fix(library): widen heavy library-fetch timeout to 30s (issue #41)#90
ndandan wants to merge 2 commits into
Shoshuo:mainfrom
ndandan:pr/library-timeout-fix

Conversation

@ndandan

@ndandan ndandan commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

The full-library endpoints (/api/v3/series, /api/v3/movie) are fetched
via the shared get(), which capped CURLOPT_TIMEOUT at 4s (Sonarr) / 8s
(Radarr). On a large or busy instance the response body doesn't finish
in that window — libcurl reports "Operation timed out after 4002 ms with
0 bytes received" (TCP connected, no body yet), which trips the circuit
breaker and surfaces the service as "unreachable/disconnected".

Give only the two heavy full-library calls a 30s budget; connect timeout
stays 4s so a genuinely-down service still trips the breaker fast. Radarr
get() gains an optional $timeout param (default 8s) so all other call
sites are unchanged. The library route already allows set_time_limit(120)
and the MediaLibraryCache means only the cold fetch pays this cost.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

ndandan and others added 2 commits August 21, 2026 21:20
)

The full-library endpoints (/api/v3/series, /api/v3/movie) are fetched
via the shared get(), which capped CURLOPT_TIMEOUT at 4s (Sonarr) / 8s
(Radarr). On a large or busy instance the response body doesn't finish
in that window — libcurl reports "Operation timed out after 4002 ms with
0 bytes received" (TCP connected, no body yet), which trips the circuit
breaker and surfaces the service as "unreachable/disconnected".

Give only the two heavy full-library calls a 30s budget; connect timeout
stays 4s so a genuinely-down service still trips the breaker fast. Radarr
get() gains an optional $timeout param (default 8s) so all other call
sites are unchanged. The library route already allows set_time_limit(120)
and the MediaLibraryCache means only the cold fetch pays this cost.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… scope the 30s budget to library routes

Review follow-ups to the Shoshuo#41 fix:

- Root cause: get()/multiGet() ran the same markDown branch for a read
  timeout after a successful connect as for a connection failure, so a
  fetch that outlived ANY budget still marked the whole instance down and
  short-circuited every caller for the breaker window — the 30s bump only
  moved that threshold. The two are now distinguished via curl_errno +
  CURLINFO_CONNECT_TIME: slow-payload timeouts fail the call (and still
  short-circuit the remainder of the same request) but never open the
  cross-request breaker; connect-level failures behave as before.
- getRawMovies() had been left on the 8s default while its Sonarr twin
  was widened — the raw sweep paths kept reproducing Shoshuo#41 on Radarr.
- The 30s budget was baked into getMovies()/getSeries(), so dashboard
  widgets, quick-look membership checks and the admin statistics loop all
  inherited it (worst case 30s x N instances in one request). The full-
  library fetchers now take a per-call timeout defaulting to the old
  8s/4s; the library/collections/filter/search-index routes pass the new
  public LIBRARY_TIMEOUT (30) explicitly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant