using Microsoft.EntityFrameworkCore; using Watcher.Data; namespace Watcher.Services; public class MetricCleanupService : BackgroundService { private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly int _retentionDays; private readonly int _checkIntervalHours; private readonly bool _enabled; public MetricCleanupService( ILogger logger, IServiceProvider serviceProvider, IConfiguration configuration) { _logger = logger; _serviceProvider = serviceProvider; // Konfiguration aus Environment Variablen laden _retentionDays = int.TryParse( configuration["DataRetention:MetricRetentionDays"] ?? Environment.GetEnvironmentVariable("METRIC_RETENTION_DAYS"), out var days) ? days : 30; // Default: 30 Tage _checkIntervalHours = int.TryParse( configuration["DataRetention:CleanupIntervalHours"] ?? Environment.GetEnvironmentVariable("METRIC_CLEANUP_INTERVAL_HOURS"), out var hours) ? hours : 24; // Default: 24 Stunden _enabled = bool.TryParse( configuration["DataRetention:Enabled"] ?? Environment.GetEnvironmentVariable("METRIC_CLEANUP_ENABLED"), out var enabled) ? enabled : true; // Default: aktiviert _logger.LogInformation( "MetricCleanupService konfiguriert: Enabled={Enabled}, RetentionDays={Days}, IntervalHours={Hours}", _enabled, _retentionDays, _checkIntervalHours); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (!_enabled) { _logger.LogInformation("MetricCleanupService ist deaktiviert."); return; } // Warte 1 Minute nach Start, bevor der erste Cleanup läuft await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); var timer = new PeriodicTimer(TimeSpan.FromHours(_checkIntervalHours)); while (await timer.WaitForNextTickAsync(stoppingToken)) { try { await CleanupOldMetricsAsync(); } catch (Exception ex) { _logger.LogError(ex, "Fehler beim Cleanup alter Metriken."); } // Offset nach Cleanup await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); } } private async Task CleanupOldMetricsAsync() { _logger.LogInformation("Starte Metric Cleanup für Daten älter als {Days} Tage...", _retentionDays); using var scope = _serviceProvider.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); var cutoffDate = DateTime.UtcNow.AddDays(-_retentionDays); try { // Anzahl der zu löschenden Einträge ermitteln var countToDelete = await context.Metrics .Where(m => m.Timestamp < cutoffDate) .CountAsync(); if (countToDelete == 0) { _logger.LogInformation("Keine alten Metriken zum Löschen gefunden."); return; } _logger.LogInformation("Lösche {Count} Metriken vor {Date}...", countToDelete, cutoffDate); // Metriken löschen var deletedCount = await context.Metrics .Where(m => m.Timestamp < cutoffDate) .ExecuteDeleteAsync(); _logger.LogInformation( "Metric Cleanup abgeschlossen: {DeletedCount} Einträge gelöscht.", deletedCount); // Optional: ContainerMetrics auch bereinigen var containerMetricsCount = await context.ContainerMetrics .Where(cm => cm.Timestamp < cutoffDate) .CountAsync(); if (containerMetricsCount > 0) { var deletedContainerMetrics = await context.ContainerMetrics .Where(cm => cm.Timestamp < cutoffDate) .ExecuteDeleteAsync(); _logger.LogInformation( "ContainerMetrics Cleanup: {DeletedCount} Einträge gelöscht.", deletedContainerMetrics); } } catch (Exception ex) { _logger.LogError(ex, "Fehler beim Löschen alter Metriken aus der Datenbank."); throw; } } }