using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Data;
using DramaLing.Api.Models.Entities;
using System.Text.Json;
using System.Text;
using System.Security.Cryptography;
namespace DramaLing.Api.Services.Caching;
///
/// 混合快取服務實作,支援記憶體快取和分散式快取的多層架構
///
public class HybridCacheService : ICacheService
{
private readonly IMemoryCache _memoryCache;
private readonly IDistributedCache? _distributedCache;
private readonly DramaLingDbContext _dbContext;
private readonly ILogger _logger;
private readonly CacheStats _stats;
private readonly JsonSerializerOptions _jsonOptions;
public HybridCacheService(
IMemoryCache memoryCache,
DramaLingDbContext dbContext,
ILogger logger,
IDistributedCache? distributedCache = null)
{
_memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
_distributedCache = distributedCache;
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_stats = new CacheStats { LastUpdated = DateTime.UtcNow };
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
_logger.LogInformation("HybridCacheService initialized with Memory Cache and {DistributedCache}",
_distributedCache != null ? "Distributed Cache" : "No Distributed Cache");
}
#region 基本快取操作
public async Task GetAsync(string key) where T : class
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
try
{
// L1: 記憶體快取 (最快)
if (_memoryCache.TryGetValue(key, out T? memoryResult))
{
_stats.HitCount++;
_logger.LogDebug("Cache hit from memory for key: {Key}", key);
return memoryResult;
}
// L2: 分散式快取
if (_distributedCache != null)
{
var distributedData = await _distributedCache.GetAsync(key);
if (distributedData != null)
{
var distributedResult = DeserializeFromBytes(distributedData);
if (distributedResult != null)
{
// 回填到記憶體快取
var memoryExpiry = CalculateMemoryExpiry(key);
_memoryCache.Set(key, distributedResult, memoryExpiry);
_stats.HitCount++;
_logger.LogDebug("Cache hit from distributed cache for key: {Key}", key);
return distributedResult;
}
}
}
// L3: 資料庫快取 (僅適用於分析結果)
if (key.StartsWith("analysis:"))
{
var dbResult = await GetFromDatabaseCacheAsync(key);
if (dbResult != null)
{
// 回填到上層快取
await SetMultiLevelCacheAsync(key, dbResult);
_stats.HitCount++;
_logger.LogDebug("Cache hit from database for key: {Key}", key);
return dbResult;
}
}
_stats.MissCount++;
_logger.LogDebug("Cache miss for key: {Key}", key);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting cache for key: {Key}", key);
_stats.MissCount++;
return null;
}
}
public async Task SetAsync(string key, T value, TimeSpan? expiry = null) where T : class
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
if (value == null)
throw new ArgumentNullException(nameof(value));
try
{
var smartExpiry = expiry ?? CalculateSmartExpiry(key, value);
// 同時設定記憶體和分散式快取
var tasks = new List>();
// L1: 記憶體快取
tasks.Add(Task.Run(() =>
{
try
{
var memoryExpiry = TimeSpan.FromMinutes(Math.Min(smartExpiry.TotalMinutes, 30)); // 記憶體快取最多30分鐘
_memoryCache.Set(key, value, memoryExpiry);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error setting memory cache for key: {Key}", key);
return false;
}
}));
// L2: 分散式快取
if (_distributedCache != null)
{
tasks.Add(SetDistributedCacheAsync(key, value, smartExpiry));
}
var results = await Task.WhenAll(tasks);
var success = results.Any(r => r);
if (success)
{
_stats.TotalKeys++;
_logger.LogDebug("Cache set for key: {Key}, expiry: {Expiry}", key, smartExpiry);
}
return success;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error setting cache for key: {Key}", key);
return false;
}
}
public async Task RemoveAsync(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
try
{
var tasks = new List>();
// 從記憶體快取移除
tasks.Add(Task.Run(() =>
{
try
{
_memoryCache.Remove(key);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error removing from memory cache for key: {Key}", key);
return false;
}
}));
// 從分散式快取移除
if (_distributedCache != null)
{
tasks.Add(Task.Run(async () =>
{
try
{
await _distributedCache.RemoveAsync(key);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error removing from distributed cache for key: {Key}", key);
return false;
}
}));
}
var results = await Task.WhenAll(tasks);
var success = results.Any(r => r);
if (success)
{
_logger.LogDebug("Cache removed for key: {Key}", key);
}
return success;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error removing cache for key: {Key}", key);
return false;
}
}
public async Task ExistsAsync(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
try
{
// 檢查記憶體快取
if (_memoryCache.TryGetValue(key, out _))
{
return true;
}
// 檢查分散式快取
if (_distributedCache != null)
{
var distributedData = await _distributedCache.GetAsync(key);
return distributedData != null;
}
return false;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking cache existence for key: {Key}", key);
return false;
}
}
public async Task ExpireAsync(string key, TimeSpan expiry)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
try
{
// 重新設定過期時間(需要重新設定值)
var value = await GetAsync