diff --git a/PARR.Core/Common/Interfaces/IRedisCacheService.cs b/PARR.Core/Common/Interfaces/IRedisCacheService.cs
index fa5cfe52..f6bf40b2 100644
--- a/PARR.Core/Common/Interfaces/IRedisCacheService.cs
+++ b/PARR.Core/Common/Interfaces/IRedisCacheService.cs
@@ -5,30 +5,31 @@
///
public interface IRedisCacheService
{
- ///
- /// Получить кэшированные данные
- ///
- ///
- ///
- ///
- T? GetCachedData(string key);
+ /////
+ ///// Получить кэшированные данные
+ /////
+ /////
+ /////
+ /////
+ //T? GetCachedData(string key);
///
/// Получить кэшированные данные асинхронно
///
///
///
+ /// Разрешить сжатие данных
///
- Task GetCachedDataAsync(string key);
+ Task GetCachedDataAsync(string key, bool useCompression = false);
- ///
- /// Добавить в кэш данные
- ///
- ///
- ///
- ///
- ///
- void SetCachedData(string key, T data, TimeSpan cacheDuration);
+ /////
+ ///// Добавить в кэш данные
+ /////
+ /////
+ /////
+ /////
+ /////
+ //void SetCachedData(string key, T data, TimeSpan cacheDuration);
///
/// Добавить в кэш данные асинхронно
@@ -38,7 +39,7 @@
///
///
///
- Task SetCachedDataAsync(string key, T data, TimeSpan cacheDuration);
+ Task SetCachedDataAsync(string key, T data, TimeSpan cacheDuration, bool useCompression = false);
///
/// Удалить кэшированные данные
diff --git a/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs b/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs
index e327e39f..6053b5c5 100644
--- a/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs
+++ b/PARR.Core/Services/Workload/Implementations/WorkloadCacheService.cs
@@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
-using NodaTime;
using PARR.Core.Common.Interfaces;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Services.Shortcodes;
@@ -308,7 +307,7 @@ namespace PARR.Core.Services.Workload.Implementations
{
var key = GetReportCacheKey(reportType, dateStart, durationDays, offset, filterParam);
- return await redisCacheService.GetCachedDataAsync(key);
+ return await redisCacheService.GetCachedDataAsync(key, true);
}
@@ -325,7 +324,7 @@ namespace PARR.Core.Services.Workload.Implementations
public async Task SetWorkloadReport(WorkloadReport report, WorkloadReportType reportType, DateOnly dateStart, int durationDays, TimeSpan offset, string? filterParam)
{
var key = GetReportCacheKey(reportType, dateStart, durationDays, offset, filterParam);
- await redisCacheService.SetCachedDataAsync(key, report, cacheWorkloadTtl);
+ await redisCacheService.SetCachedDataAsync(key, report, cacheWorkloadTtl, true);
}
@@ -377,11 +376,5 @@ namespace PARR.Core.Services.Workload.Implementations
return keyStr;
}
-
-
-
-
-
-
}
}
diff --git a/PARR.Infrastructure/Redis/Helpers/CompressionHelper.cs b/PARR.Infrastructure/Redis/Helpers/CompressionHelper.cs
new file mode 100644
index 00000000..3d25aa54
--- /dev/null
+++ b/PARR.Infrastructure/Redis/Helpers/CompressionHelper.cs
@@ -0,0 +1,51 @@
+using System.IO.Compression;
+
+namespace PARR.Infrastructure.Redis.Helpers
+{
+ ///
+ /// Архивация/разарзивация данных
+ ///
+ internal static class CompressionHelper
+ {
+ // Не сжимать маленькие значения
+ private const int MinCompressionSize = 1024; // 1Кб
+
+ ///
+ /// Сжать
+ ///
+ ///
+ ///
+ public static byte[] Compress(byte[] data)
+ {
+ if (data.Length < MinCompressionSize)
+ return data;
+
+ using var output = new MemoryStream();
+ using (var gzip = new GZipStream(output, CompressionLevel.Optimal))
+ {
+ gzip.Write(data, 0, data.Length);
+ }
+
+ return output.ToArray();
+ }
+
+ ///
+ /// Разархивировать если это GZip
+ ///
+ ///
+ ///
+ public static byte[] Decompress(byte[] data)
+ {
+ // Проверяем сигнатуру GZip (0x1F, 0x8B)
+ if (data.Length < 2 || data[0] != 0x1F || data[1] != 0x8B)
+ return data; // Не сжато
+
+ using var input = new MemoryStream(data);
+ using var gzip = new GZipStream(input, CompressionMode.Decompress);
+ using var output = new MemoryStream();
+ gzip.CopyTo(output);
+
+ return output.ToArray();
+ }
+ }
+}
diff --git a/PARR.Infrastructure/Redis/RedisCacheService.cs b/PARR.Infrastructure/Redis/RedisCacheService.cs
index d7a0fff4..fba9f1ae 100644
--- a/PARR.Infrastructure/Redis/RedisCacheService.cs
+++ b/PARR.Infrastructure/Redis/RedisCacheService.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using PARR.Core.Common.Interfaces;
+using PARR.Infrastructure.Redis.Helpers;
using StackExchange.Redis;
using System.Text.Json;
@@ -8,6 +9,11 @@ namespace PARR.Infrastructure.Redis
{
internal class RedisCacheService : IRedisCacheService
{
+ ///
+ /// Минимальный размер для сжатия
+ ///
+ private const int compressionTreshold = 10 * 1024; // 10Kb
+
private readonly IDistributedCache cache;
private readonly IConnectionMultiplexer connectionMultiplexer;
private readonly ILogger logger;
@@ -27,49 +33,87 @@ namespace PARR.Infrastructure.Redis
#region Распределенный кэш IDistributedCache
- public async Task GetCachedDataAsync(string key)
+ public async Task GetCachedDataAsync(string key, bool useCompression = false)
{
- var jsonData = await cache.GetStringAsync(key);
+ if (useCompression)
+ {
+ // хранили в байтах
+ try
+ {
+ var data = await cache.GetAsync(key);
+ if (data == null)
+ return default(T);
- if (jsonData == null)
- return default(T);
+ return DeserializeWithCompression(data);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("Формат данных не байты.", ex);
+ }
+ }
+ else
+ {
+ // хранили в строке
+ try
+ {
+ var jsonData = await cache.GetStringAsync(key);
- return JsonSerializer.Deserialize(jsonData);
+ if (jsonData == null)
+ return default(T);
+
+ return JsonSerializer.Deserialize(jsonData);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("Формат данных не строка.", ex);
+ }
+
+ }
}
- public T? GetCachedData(string key)
- {
- var jsonData = cache.GetString(key);
+ //public T? GetCachedData(string key)
+ //{
+ // var jsonData = cache.GetString(key);
- if (jsonData == null)
- return default(T);
+ // if (jsonData == null)
+ // return default(T);
- return JsonSerializer.Deserialize(jsonData);
- }
+ // return JsonSerializer.Deserialize(jsonData);
+ //}
- public void SetCachedData(string key, T data, TimeSpan cacheDuration)
+ //public void SetCachedData(string key, T data, TimeSpan cacheDuration)
+ //{
+ // var options = new DistributedCacheEntryOptions
+ // {
+ // AbsoluteExpirationRelativeToNow = cacheDuration
+ // };
+
+ // var jsonData = JsonSerializer.Serialize(data);
+ // cache.SetString(key, jsonData, options);
+ //}
+
+
+ public async Task SetCachedDataAsync(string key, T data, TimeSpan cacheDuration, bool useCompression = false)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = cacheDuration
};
- var jsonData = JsonSerializer.Serialize(data);
- cache.SetString(key, jsonData, options);
- }
-
-
- public async Task SetCachedDataAsync(string key, T data, TimeSpan cacheDuration)
- {
- var options = new DistributedCacheEntryOptions
+ if (useCompression)
{
- AbsoluteExpirationRelativeToNow = cacheDuration
- };
-
- var jsonData = JsonSerializer.Serialize(data);
- await cache.SetStringAsync(key, jsonData, options);
+ // храним в байтах, если надо то сжимаем
+ var jsonData = SerializeWithCompression(data);
+ await cache.SetAsync(key, jsonData, options);
+ }
+ else
+ {
+ // храним в строке
+ var jsonData = JsonSerializer.Serialize(data);
+ await cache.SetStringAsync(key, jsonData, options);
+ }
}
@@ -250,5 +294,28 @@ namespace PARR.Infrastructure.Redis
#endregion
+ private byte[] SerializeWithCompression(T data)
+ {
+ var json = JsonSerializer.SerializeToUtf8Bytes(data);
+
+ logger.LogDebug("Сериализовано: {Size} байт, тип: {Type}", json.Length, typeof(T).Name);
+
+ if (json.Length < compressionTreshold)
+ return json;
+
+ var compressed = CompressionHelper.Compress(json);
+ logger.LogDebug("После сжатия: {Size} байт (экономия: {Percent}%)", compressed.Length, Math.Round((1 - (double)compressed.Length / json.Length) * 100, 1));
+
+ return compressed;
+ }
+
+ private T? DeserializeWithCompression(byte[] data)
+ {
+ var json = CompressionHelper.Decompress(data);
+
+ return JsonSerializer.Deserialize(json);
+ }
+
+
}
}