using Microsoft.Extensions.Options; using SeaHaven.Services.Configuration; using SeaHaven.Services.Interfaces; using Sentry; namespace Api.SeaHavenIndustries.HostedServices { public class UpliftLifecycleHostedService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly ApprovalsOptions _options; private readonly IHub _sentryHub; public UpliftLifecycleHostedService( IServiceScopeFactory scopeFactory, IOptions options, ILogger logger, IHub sentryHub) { _scopeFactory = scopeFactory; _logger = logger; _options = options.Value; _sentryHub = sentryHub; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { { using var sentryScope = _sentryHub.PushScope(); var transaction = _sentryHub.StartTransaction("uplifts.lifecycle-sweep", "task"); _sentryHub.ConfigureScope(scope => scope.Transaction = transaction); try { await SweepAsync(stoppingToken); transaction.Finish(SpanStatus.Ok); } catch (OperationCanceledException) { transaction.Finish(SpanStatus.Cancelled); throw; } catch (Exception ex) { _logger.LogError(ex, "Uplift lifecycle sweep failed; will retry on next interval"); transaction.Finish(ex, SpanStatus.InternalError); } } await Task.Delay(_options.EffectiveSweepInterval, stoppingToken); } } private async Task SweepAsync(CancellationToken cancellationToken) { using var scope = _scopeFactory.CreateScope(); var lifecycle = scope.ServiceProvider.GetRequiredService(); await lifecycle.ExpireDueAsync(cancellationToken); await lifecycle.SendDueInitialNotificationsAsync(cancellationToken); await lifecycle.EscalateDueAsync(cancellationToken); } } }