diff --git a/src/SQLInsight.Agent/Configuration/AgentRequestService.cs b/src/SQLInsight.Agent/Configuration/AgentRequestService.cs index 6f5d4f8..297fe86 100644 --- a/src/SQLInsight.Agent/Configuration/AgentRequestService.cs +++ b/src/SQLInsight.Agent/Configuration/AgentRequestService.cs @@ -41,6 +41,11 @@ public sealed partial class AgentRequestService( public static readonly TimeSpan RequestTimeout = TimeSpan.FromMinutes(2); + /// The least time between two routine discoveries of one instance, also when the last one could not be stored. + public static readonly TimeSpan RoutineRetry = TimeSpan.FromMinutes(15); + + private readonly Dictionary _routineAttempts = []; + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await readiness.Ready.WaitAsync(stoppingToken).ConfigureAwait(false); @@ -172,8 +177,11 @@ public sealed partial class AgentRequestService( /// The routine discovery of the active instance that waited longest, if one is due. private async Task RefreshOneAsync(DateTimeOffset now, CancellationToken cancellationToken) { + // Also waits on the agent's own last attempt: if storing the result fails (the repository), LastDiscoveryAt is + // not set, and without this the same instance would be listed again every round (every 2 seconds). var due = (await repository.GetConfiguredInstancesAsync(cancellationToken).ConfigureAwait(false)) .Where(i => !i.Paused && i.CredentialError is null && (i.LastDiscoveryAt is null || now.UtcDateTime - i.LastDiscoveryAt.Value >= DiscoveryInterval)) + .Where(i => !_routineAttempts.TryGetValue(i.ConfiguredInstanceId, out var attempted) || now - attempted >= RoutineRetry) .OrderBy(i => i.LastDiscoveryAt ?? DateTime.MinValue) .FirstOrDefault(); if (due is null) @@ -181,6 +189,7 @@ public sealed partial class AgentRequestService( return; } + _routineAttempts[due.ConfiguredInstanceId] = now; using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); timeout.CancelAfter(RequestTimeout); try