dotnet-cqrs/Svrnty.CQRS.Events/Services/IdempotencyCleanupService.cs

87 lines
2.9 KiB
C#

using System;
using Svrnty.CQRS.Events.Abstractions.Storage;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Svrnty.CQRS.Events.Abstractions;
namespace Svrnty.CQRS.Events.Services;
/// <summary>
/// Background service that periodically cleans up old idempotency records.
/// </summary>
/// <remarks>
/// This service cleans up:
/// - Old processed event records (older than retention period)
/// - Expired idempotency locks (automatically handled by the store)
/// </remarks>
public class IdempotencyCleanupService : BackgroundService
{
private readonly IIdempotencyStore _idempotencyStore;
private readonly ILogger<IdempotencyCleanupService> _logger;
// Configuration - these could be made configurable via options
private readonly TimeSpan _cleanupInterval = TimeSpan.FromHours(1);
private readonly TimeSpan _retentionPeriod = TimeSpan.FromDays(7);
public IdempotencyCleanupService(
IIdempotencyStore idempotencyStore,
ILogger<IdempotencyCleanupService> logger)
{
_idempotencyStore = idempotencyStore ?? throw new ArgumentNullException(nameof(idempotencyStore));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Idempotency cleanup service started (Interval: {Interval}, Retention: {Retention})",
_cleanupInterval,
_retentionPeriod);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(_cleanupInterval, stoppingToken);
if (stoppingToken.IsCancellationRequested)
break;
await PerformCleanupAsync(stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
// Normal shutdown
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during idempotency cleanup");
// Continue running despite errors
}
}
_logger.LogInformation("Idempotency cleanup service stopped");
}
private async Task PerformCleanupAsync(CancellationToken cancellationToken)
{
var olderThan = DateTimeOffset.UtcNow.Subtract(_retentionPeriod);
_logger.LogDebug("Starting idempotency cleanup (deleting records older than {OlderThan})", olderThan);
var deletedCount = await _idempotencyStore.CleanupAsync(olderThan, cancellationToken);
if (deletedCount > 0)
{
_logger.LogInformation("Cleaned up {DeletedCount} old idempotency records", deletedCount);
}
else
{
_logger.LogDebug("No old idempotency records to clean up");
}
}
}