feat(core, dal): WorkloadCacheService - методы для работы с кэшем отчетности workload

This commit is contained in:
Mikhail Trubnikov
2026-05-05 14:52:17 +10:00
parent a89605d02a
commit 7d42a80556
10 changed files with 380 additions and 27 deletions

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using PARR.Core.Common.Interfaces;
using StackExchange.Redis;
using System.Text.Json;
@@ -8,14 +9,19 @@ namespace PARR.Infrastructure.Redis
internal class RedisCacheService : IRedisCacheService
{
private readonly IDistributedCache cache;
private readonly IConnectionMultiplexer connectionMultiplexer;
private readonly ILogger<RedisCacheService> logger;
private readonly IDatabase redis;
public RedisCacheService(
IDistributedCache cache,
IConnectionMultiplexer connectionMultiplexer
IConnectionMultiplexer connectionMultiplexer,
ILogger<RedisCacheService> logger
)
{
this.cache = cache;
this.connectionMultiplexer = connectionMultiplexer;
this.logger = logger;
this.redis = connectionMultiplexer.GetDatabase();
}
@@ -80,6 +86,61 @@ namespace PARR.Infrastructure.Redis
#endregion
public async Task<int> DeleteKeysByPatternAsync(string pattern, int batchSize = 100)
{
if (string.IsNullOrEmpty(pattern))
return 0;
logger.LogDebug("Начало удаление ключей по маске '{Pattern}' из кэш", pattern);
var deletedCount = 0;
var keysToDelete = new List<RedisKey>();
// Получаем сервер для выполнения SCAN
//todo: Для кластера нужно обрабатывать каждый узел отдельно
var server = connectionMultiplexer.GetEndPoints()
.Select(t => connectionMultiplexer.GetServer(t))
.FirstOrDefault();
if (server == null)
{
logger.LogError("Не удалось получить сервер Redis для операции SCAN");
return 0;
}
// server.Keys() лениво сканирует ключи через SCAN (не блокирует!)
// Важно: не вызывай .ToList() — это загрузит всё в память!
foreach (var key in server.Keys(pattern: pattern, pageSize: batchSize))
{
keysToDelete.Add(key);
// Удаляем пакетом, чтобы не делать лишний сетевой запрос на каждый ключ
if (keysToDelete.Count >= batchSize)
{
await redis.KeyDeleteAsync(keysToDelete.ToArray());
deletedCount += keysToDelete.Count;
logger.LogDebug("Удалено {Count} ключей по масске '{Pattern}'", keysToDelete.Count, pattern);
keysToDelete.Clear();
}
}
// Удаляем остаток, если остался
if (keysToDelete.Count > 0)
{
await redis.KeyDeleteAsync(keysToDelete.ToArray());
deletedCount += keysToDelete.Count;
}
logger.LogInformation("Завершено удаление ключей по маске '{Pattern}'. Удалено: {DeletedCount}", pattern, deletedCount);
return deletedCount;
}
#region Нативные операции Redis, Redis Hash
public async Task SetHashFieldAsync<T>(string hashKey, string field, T value, TimeSpan? ttl = null)