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);
}