Skip to content

fix(search): an indexer's HTTP timeout no longer aborts the whole search - #967

Draft
kevinheneveld wants to merge 1 commit into
Listenarrs:canaryfrom
kevinheneveld:fix/indexer-timeout-escapes-search
Draft

kevinheneveld wants to merge 1 commit into
Listenarrs:canaryfrom
kevinheneveld:fix/indexer-timeout-escapes-search

Conversation

@kevinheneveld

Copy link
Copy Markdown
Contributor

Problem

HttpClient.Timeout surfaces as a TaskCanceledException (inner TimeoutException). Every catch (Exception ex) when (ex is not OperationCanceledException …) clause in the indexer call chain deliberately lets that type through, so one slow indexer escapes the per-indexer catch, tears through Task.WhenAll and SearchService, and turns the request into an unhandled 500 — discarding the results the other indexers had already returned.

Live case: a Torznab indexer that normally answers in ~1s occasionally took 28–60s. On the 60s expiry a Wanted-page search-and-download failed with "The request was canceled due to the configured HttpClient.Timeout of 60 seconds elapsing" although three of four indexers had answered.

Fix

  • HttpClientTimeoutClassifier (application/Common) tells the client-timeout shape (TaskCanceledException + inner TimeoutException) apart from cooperative cancellation.
  • IndexerSearchWorkflow.SearchIndexerAsync and TorznabNewznabSearchProvider.SearchAsync treat that shape as the indexer's own failure (warning + no results) so the fan-out keeps the other indexers' results. A genuine cancellation still propagates.

Tests

IndexerSearchWorkflowTimeoutTests: a timing-out indexer next to a healthy one yields the healthy one's results; a real OperationCanceledException still propagates; the Torznab provider returns empty on an HttpClient.Timeout; classifier shape checks. Plus BackendArchitectureTests and the existing Torznab parsing / SearchService tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WJkHHURUTS2m1QGHwn7xsC

HttpClient.Timeout surfaces as a TaskCanceledException (inner TimeoutException),
which every "when (ex is not OperationCanceledException)" clause in the indexer
call chain deliberately lets through. So one slow indexer escaped the per-indexer
catch, tore through Task.WhenAll and SearchService, and turned the request into an
unhandled 500 — discarding the results the other indexers had already returned.

Live case: a Torznab indexer that usually answers in ~1s occasionally took 28-60s;
on the 60s expiry a Wanted-page "search-and-download" failed with
"The request was canceled due to the configured HttpClient.Timeout of 60 seconds
elapsing" although three of four indexers had answered.

HttpClientTimeoutClassifier tells the client-timeout shape apart from cooperative
cancellation. IndexerSearchWorkflow.SearchIndexerAsync and the Torznab/Newznab
provider now treat that shape as the indexer's own failure (warning + no results)
so the fan-out keeps the other indexers' results; a genuine cancellation still
propagates.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJkHHURUTS2m1QGHwn7xsC
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