using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Monitor.Backend.Application; /// 保留清理:原始段文件(持续任务与按需测试保留期不同)、各层指标、事件。保留期来自 settings,可以在线修改。 public sealed partial class RetentionService : BackgroundService { private static readonly TimeSpan s_interval = TimeSpan.FromHours(1); private readonly IRawStore _raw; private readonly IMetricStore _metrics; private readonly IEventStore _events; private readonly IAuthStore _auth; private readonly IHostStore _hosts; private readonly SettingsService _settings; private readonly LoginRateLimiter _limiter; private readonly TimeProvider _time; private readonly ILogger _logger; public RetentionService(IRawStore raw, IMetricStore metrics, IEventStore events, IHostStore hosts, IAuthStore auth, SettingsService settings, LoginRateLimiter limiter, TimeProvider time, ILogger logger) { _raw = raw; _metrics = metrics; _events = events; _hosts = hosts; _auth = auth; _settings = settings; _limiter = limiter; _time = time; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { await RunOnceAsync(stoppingToken); await Task.Delay(s_interval, _time, stoppingToken); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { return; } catch (Exception ex) { // 后台循环不能因为一次失败而退出 LogFailed(ex); await Task.Delay(TimeSpan.FromMinutes(5), _time, stoppingToken); } } } /// 执行一轮清理(也供测试直接调用)。 public async Task RunOnceAsync(CancellationToken ct) { DateTimeOffset now = _time.GetUtcNow(); long nowMs = now.ToUnixTimeMilliseconds(); DateOnly today = DateOnly.FromDateTime(now.UtcDateTime); int rawDays = await _settings.GetIntAsync(SettingsService.RawDays, ct); int testDays = await _settings.GetIntAsync(SettingsService.TestRawDays, ct); await _raw.DeleteExpiredAsync(today.AddDays(-rawDays), today.AddDays(-testDays), ct); await _metrics.DeleteOlderThanAsync(MetricLevel.S10, nowMs - (await _settings.GetIntAsync(SettingsService.Metric10sHours, ct) * Steps.Hour), ct); await _metrics.DeleteOlderThanAsync(MetricLevel.M1, nowMs - (await _settings.GetIntAsync(SettingsService.Metric1mDays, ct) * Steps.Day), ct); await _metrics.DeleteOlderThanAsync(MetricLevel.M5, nowMs - (await _settings.GetIntAsync(SettingsService.Metric5mDays, ct) * Steps.Day), ct); await _events.DeleteOlderThanAsync(nowMs - (await _settings.GetIntAsync(SettingsService.EventDays, ct) * Steps.Day), ct); await _hosts.DeleteOlderThanAsync(nowMs - (await _settings.GetIntAsync(SettingsService.HostDays, ct) * Steps.Day), ct); await _auth.DeleteExpiredSessionsAsync(nowMs, ct); _limiter.Prune(); } [LoggerMessage(Level = LogLevel.Error, Message = "retention pass failed")] private partial void LogFailed(Exception exception); }