feat(core, infrastructure): RedisCacheService - изменение/добавление полей в кэше списокм. UnitCacheService - оптимизация при получении данных.
This commit is contained in:
@@ -14,6 +14,12 @@ namespace PARR.Infrastructure.Redis
|
||||
/// </summary>
|
||||
private const int compressionTreshold = 10 * 1024; // 10Kb
|
||||
|
||||
/// <summary>
|
||||
/// Размер порции для Redis Hash.
|
||||
/// Делим на порции по 5000, чтобы не перегружать буфер команд Redis.
|
||||
/// </summary>
|
||||
public const int RedisHashBatchSize = 5000;
|
||||
|
||||
private readonly IDistributedCache cache;
|
||||
private readonly IConnectionMultiplexer connectionMultiplexer;
|
||||
private readonly ILogger<RedisCacheService> logger;
|
||||
@@ -212,6 +218,58 @@ namespace PARR.Infrastructure.Redis
|
||||
await SetHashTtlAsync(hashKey, ttl.Value);
|
||||
}
|
||||
|
||||
public async Task SetHashFieldsAsync<T>(List<(string HashKey, string FieldKey, T Value)> items, TimeSpan? ttl = null, bool useCompression = false)
|
||||
{
|
||||
if (items == null || items.Count == 0)
|
||||
return;
|
||||
|
||||
// Множество для отслеживания уникальных HashKey в рамках всей операции,
|
||||
// чтобы обновить для них TTL всего один раз в самом конце.
|
||||
var uniqueHashKeys = new HashSet<string>();
|
||||
|
||||
// 1. Делим входящие данные на порции по 5000 (RedisHashBatchSize)
|
||||
foreach (var chunk in items.Chunk(RedisHashBatchSize))
|
||||
{
|
||||
var tasks = new List<Task<bool>>(chunk.Length);
|
||||
|
||||
// 2. Формируем пайплайн для текущей порции
|
||||
foreach (var item in chunk)
|
||||
{
|
||||
uniqueHashKeys.Add(item.HashKey);
|
||||
|
||||
if (useCompression)
|
||||
{
|
||||
var byteData = SerializeWithCompression(item.Value);
|
||||
tasks.Add(redis.HashSetAsync(item.HashKey, item.FieldKey, byteData));
|
||||
}
|
||||
else
|
||||
{
|
||||
var jsonData = JsonSerializer.Serialize(item.Value);
|
||||
tasks.Add(redis.HashSetAsync(item.HashKey, item.FieldKey, jsonData));
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogDebug("Сформировал {Count} одновременных запросов на запись в кэш", tasks.Count);
|
||||
|
||||
// 3. Отправляем пачку в Redis и ждем завершения
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
// 4. Если указан TTL, обновляем его для всех затронутых хешей
|
||||
if (ttl.HasValue)
|
||||
{
|
||||
var ttlTasks = new List<Task>(uniqueHashKeys.Count);
|
||||
foreach (var hashKey in uniqueHashKeys)
|
||||
{
|
||||
ttlTasks.Add(SetHashTtlAsync(hashKey, ttl.Value));
|
||||
}
|
||||
|
||||
await Task.WhenAll(ttlTasks);
|
||||
}
|
||||
|
||||
logger.LogDebug("Успешно сохранил {Count} элементов в кэш.", items.Count);
|
||||
}
|
||||
|
||||
public async Task<T?> GetHashFieldAsync<T>(string hashKey, string field, bool useCompression = false)
|
||||
{
|
||||
// получить значение поля из Hash
|
||||
@@ -245,7 +303,7 @@ namespace PARR.Infrastructure.Redis
|
||||
var allItems = new List<T>(keys.Count);
|
||||
|
||||
// Делим на порции по 5000 (оптимально)
|
||||
foreach (var chunk in keys.Chunk(5000))
|
||||
foreach (var chunk in keys.Chunk(RedisHashBatchSize))
|
||||
{
|
||||
// Формируем пайплайн ТОЛЬКО для 5 000 элементов
|
||||
var tasks = new List<Task<RedisValue>>(chunk.Length);
|
||||
@@ -383,7 +441,7 @@ namespace PARR.Infrastructure.Redis
|
||||
int deletedCount = 0;
|
||||
|
||||
// Делим на порции по 5000, чтобы не перегружать буфер команд Redis
|
||||
foreach (var chunk in keys.Chunk(5000))
|
||||
foreach (var chunk in keys.Chunk(RedisHashBatchSize))
|
||||
{
|
||||
var tasks = new List<Task<bool>>(chunk.Length);
|
||||
foreach (var key in chunk)
|
||||
@@ -483,6 +541,5 @@ namespace PARR.Infrastructure.Redis
|
||||
return JsonSerializer.Deserialize<T>(json);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user