Files

62 lines
2.2 KiB
C#

using Microsoft.Extensions.Options;
public sealed class ReconciliationScheduler : BackgroundService
{
private readonly IServiceScopeFactory _scopes;
private readonly ReconciliationJobOptions _opts;
private readonly ILogger<ReconciliationScheduler> _logger;
public ReconciliationScheduler(
IServiceScopeFactory scopes,
IOptions<ReconciliationJobOptions> opts,
ILogger<ReconciliationScheduler> logger)
{
_scopes = scopes;
_opts = opts.Value;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Wait for RabbitMQ topology and migrations before first cycle.
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
_logger.LogInformation(
"ReconciliationScheduler started — interval {IntervalMinutes} min", _opts.IntervalMinutes);
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(_opts.IntervalMinutes));
while (await timer.WaitForNextTickAsync(stoppingToken))
{
_logger.LogInformation("[RECONCILIATION] Starting reconciliation cycle");
await RunCheckAsync<UnacknowledgedAlertsCheck>(
check => check.RunAsync(stoppingToken), stoppingToken);
await RunCheckAsync<PendingOrdersCheck>(
check => check.RunAsync(stoppingToken), stoppingToken);
await RunCheckAsync<DisconnectedMonitorsCheck>(
check => check.RunAsync(stoppingToken), stoppingToken);
_logger.LogInformation("[RECONCILIATION] Cycle complete");
}
}
private async Task RunCheckAsync<T>(Func<T, Task<int>> run, CancellationToken ct)
where T : notnull
{
try
{
await using var scope = _scopes.CreateAsyncScope();
var check = scope.ServiceProvider.GetRequiredService<T>();
var count = await run(check);
_logger.LogInformation(
"[RECONCILIATION] {Check} — new alerts: {Count}", typeof(T).Name, count);
}
catch (Exception ex)
{
_logger.LogError(ex, "[RECONCILIATION] {Check} failed", typeof(T).Name);
}
}
}