From 11f6483d19b39d0242a3598da7e15af383859eb5 Mon Sep 17 00:00:00 2001 From: Simone Filippini Date: Thu, 12 Mar 2026 01:32:26 +0100 Subject: [PATCH] New: Configurable per-indexer query timeout --- src/NzbDrone.Core/Indexers/HttpIndexerBase.cs | 16 ++++++++++++++-- .../Indexers/IndexerBaseSettings.cs | 7 +++++++ src/NzbDrone.Core/Localization/Core/en.json | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index 86f125e45..33cfc4fdb 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -526,7 +526,8 @@ protected virtual async Task FetchReleases(Func FetchIndexerResponse(IndexerReques request.HttpRequest.SuppressHttpError = true; request.HttpRequest.Encoding ??= Encoding; + if (request.HttpRequest.RequestTimeout == TimeSpan.Zero && Settings.BaseSettings != null) + { + request.HttpRequest.RequestTimeout = TimeSpan.FromMilliseconds(Settings.BaseSettings.QueryTimeout); + } + var response = await RetryStrategy .ExecuteAsync(static async (state, _) => await state._httpClient.ExecuteProxiedAsync(state.HttpRequest, state.Definition), (_httpClient, request.HttpRequest, Definition)) .ConfigureAwait(false); @@ -836,8 +842,14 @@ protected virtual async Task TestConnection() return new ValidationFailure(string.Empty, "Unable to connect to indexer connection failure. Check your connection to the indexer's server and DNS." + webException.Message); } + if (webException.Message.Contains("timed out")) + { + var timeoutMs = Settings.BaseSettings?.QueryTimeout ?? 100000; + return new ValidationFailure(string.Empty, "Unable to connect to indexer, request timed out after " + timeoutMs + " ms. Consider increasing the Query Timeout in the indexer advanced settings. " + webException.Message); + } + if (webException.Message.Contains("502") || webException.Message.Contains("503") || - webException.Message.Contains("504") || webException.Message.Contains("timed out")) + webException.Message.Contains("504")) { return new ValidationFailure(string.Empty, "Unable to connect to indexer, indexer's server is unavailable. Try again later. " + webException.Message); } diff --git a/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs b/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs index 5bada9b42..0ee740a48 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs @@ -16,6 +16,10 @@ public IndexerCommonSettingsValidator() .GreaterThan(0) .When(c => c.GrabLimit.HasValue) .WithMessage("Should be greater than zero"); + + RuleFor(c => c.QueryTimeout) + .InclusiveBetween(50, 600000) + .WithMessage("Must be between 50 ms and 600000 ms (10 minutes)"); } } @@ -29,6 +33,9 @@ public class IndexerBaseSettings [FieldDefinition(3, Type = FieldType.Select, Label = "IndexerSettingsLimitsUnit", SelectOptions = typeof(IndexerLimitsUnit), HelpText = "IndexerSettingsLimitsUnitHelpText", Advanced = true)] public int LimitsUnit { get; set; } = (int)IndexerLimitsUnit.Day; + + [FieldDefinition(4, Type = FieldType.Number, Label = "IndexerSettingsQueryTimeout", Unit = "ms", HelpText = "IndexerSettingsQueryTimeoutHelpText", Advanced = true)] + public int QueryTimeout { get; set; } = 100000; } public enum IndexerLimitsUnit diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index a789c6046..10aff3d56 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -428,6 +428,8 @@ "IndexerSettingsPreferMagnetUrlHelpText": "When enabled, this indexer will prefer the use of magnet URLs for grabs with fallback to torrent links", "IndexerSettingsQueryLimit": "Query Limit", "IndexerSettingsQueryLimitHelpText": "The number of max queries as specified by the respective unit that {appName} will allow to the site", + "IndexerSettingsQueryTimeout": "Query Timeout", + "IndexerSettingsQueryTimeoutHelpText": "Query timeout in milliseconds for this indexer. Default is 100000 ms (100s). Increase for slow indexers, decrease for faster failure detection", "IndexerSettingsRssKey": "RSS Key", "IndexerSettingsSeedRatio": "Seed Ratio", "IndexerSettingsSeedRatioHelpText": "The ratio a torrent should reach before stopping, empty uses the download client's default. Ratio should be at least 1.0 and follow the indexers rules",