66 lines
2.5 KiB
C#
66 lines
2.5 KiB
C#
namespace QuestionsApi.Services;
|
|
|
|
public sealed class AnswerStatisticsMaintenanceService : BackgroundService
|
|
{
|
|
private readonly AnswerStatisticsStore _statistics;
|
|
private readonly QuestionAttemptStore _attempts;
|
|
private readonly ServiceMetrics _metrics;
|
|
private readonly ILogger<AnswerStatisticsMaintenanceService> _logger;
|
|
private readonly TimeSpan _interval;
|
|
private readonly string _owner = $"{Environment.MachineName}-{Guid.NewGuid():N}";
|
|
|
|
public AnswerStatisticsMaintenanceService(
|
|
AnswerStatisticsStore statistics,
|
|
QuestionAttemptStore attempts,
|
|
ServiceMetrics metrics,
|
|
IConfiguration configuration,
|
|
ILogger<AnswerStatisticsMaintenanceService> logger)
|
|
{
|
|
_statistics = statistics;
|
|
_attempts = attempts;
|
|
_metrics = metrics;
|
|
_logger = logger;
|
|
_interval = TimeSpan.FromSeconds(Math.Max(30, configuration.GetValue("AnswerStatistics:ReconcileSeconds", 300)));
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
try
|
|
{
|
|
if (await _statistics.TryAcquireBackfillLeaseAsync(_owner, TimeSpan.FromMinutes(30)))
|
|
{
|
|
_logger.LogInformation("Backfilling materialized answer statistics");
|
|
await _statistics.RebuildAllAsync();
|
|
await _statistics.MarkBackfillCompleteAsync(_owner);
|
|
_metrics.AnswerStatisticsBackfillCompleted();
|
|
_metrics.Reconciled();
|
|
}
|
|
else if (await _statistics.IsBackfillCompleteAsync())
|
|
{
|
|
_metrics.AnswerStatisticsBackfillCompleted();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Initial answer-statistics backfill failed");
|
|
}
|
|
|
|
using var timer = new PeriodicTimer(_interval);
|
|
while (await timer.WaitForNextTickAsync(stoppingToken))
|
|
{
|
|
try
|
|
{
|
|
if (await _statistics.IsBackfillCompleteAsync())
|
|
_metrics.AnswerStatisticsBackfillCompleted();
|
|
var activeIds = await _attempts.GetActiveQuestionIdsAsync(DateTime.UtcNow.Subtract(_interval).Subtract(_interval));
|
|
await _statistics.RebuildQuestionsAsync(activeIds);
|
|
_metrics.Reconciled();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Answer-statistics reconciliation failed");
|
|
}
|
|
}
|
|
}
|
|
}
|