using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace ZB.MOM.WW.MxGateway.Server.Galaxy; /// Background service that periodically refreshes the Galaxy Repository hierarchy cache off the request path. public sealed class GalaxyHierarchyRefreshService( IGalaxyHierarchyCache cache, IOptions options, ILogger logger, TimeProvider? timeProvider = null) : BackgroundService { private readonly TimeProvider _timeProvider = timeProvider ?? TimeProvider.System; /// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { TimeSpan interval = TimeSpan.FromSeconds(Math.Max(1, options.Value.DashboardRefreshIntervalSeconds)); try { await cache.RefreshAsync(stoppingToken).ConfigureAwait(false); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { return; } catch (Exception exception) { // A transient first-load failure (e.g. a TimeoutException or // Win32Exception from connection establishment, or a DbException // subtype the cache does not catch) must not fault this // BackgroundService and stop the whole gateway. The cache records // its own Unavailable/Stale status; the periodic tick below retries. logger.LogWarning(exception, "Initial Galaxy hierarchy cache load failed; will retry on the refresh interval."); } using PeriodicTimer timer = new(interval, _timeProvider); try { while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false)) { try { await cache.RefreshAsync(stoppingToken).ConfigureAwait(false); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { return; } catch (Exception exception) { logger.LogWarning(exception, "Galaxy hierarchy cache refresh tick failed."); } } } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { } } }