using DramaLing.Api.Services.Infrastructure.Caching;
namespace DramaLing.Api.Services.Caching;
///
/// 重構後的混合快取服務,使用組合模式
///
public class RefactoredHybridCacheService : ICacheService
{
private readonly ICacheProvider _memoryProvider;
private readonly ICacheProvider? _distributedProvider;
private readonly IDatabaseCacheManager _databaseCacheManager;
private readonly ICacheStrategyManager _strategyManager;
private readonly ILogger _logger;
private readonly CacheStats _stats;
public RefactoredHybridCacheService(
ICacheProvider memoryProvider,
ICacheProvider? distributedProvider,
IDatabaseCacheManager databaseCacheManager,
ICacheStrategyManager strategyManager,
ILogger logger)
{
_memoryProvider = memoryProvider ?? throw new ArgumentNullException(nameof(memoryProvider));
_distributedProvider = distributedProvider;
_databaseCacheManager = databaseCacheManager ?? throw new ArgumentNullException(nameof(databaseCacheManager));
_strategyManager = strategyManager ?? throw new ArgumentNullException(nameof(strategyManager));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_stats = new CacheStats { LastUpdated = DateTime.UtcNow };
_logger.LogInformation("RefactoredHybridCacheService initialized with {MemoryProvider} and {DistributedProvider}",
_memoryProvider.ProviderName, _distributedProvider?.ProviderName ?? "No Distributed Cache");
}
public async Task GetAsync(string key) where T : class
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
try
{
// L1: 記憶體快取
var memoryResult = await _memoryProvider.GetAsync(key);
if (memoryResult != null)
{
_stats.HitCount++;
return memoryResult;
}
// L2: 分散式快取
if (_distributedProvider != null)
{
var distributedResult = await _distributedProvider.GetAsync(key);
if (distributedResult != null)
{
// 回填到記憶體快取
var memoryExpiry = _strategyManager.CalculateMemoryExpiry(key);
await _memoryProvider.SetAsync(key, distributedResult, memoryExpiry);
_stats.HitCount++;
return distributedResult;
}
}
// L3: 資料庫快取 (僅適用於分析結果)
if (key.StartsWith("analysis:"))
{
var dbResult = await _databaseCacheManager.GetFromDatabaseCacheAsync(key);
if (dbResult != null)
{
// 回填到上層快取
await SetMultiLevelCacheAsync(key, dbResult);
_stats.HitCount++;
return dbResult;
}
}
_stats.MissCount++;
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 ?? _strategyManager.CalculateSmartExpiry(key, value);
var tasks = new List>();
// L1: 記憶體快取
var memoryExpiry = TimeSpan.FromMinutes(Math.Min(smartExpiry.TotalMinutes, 30));
tasks.Add(_memoryProvider.SetAsync(key, value, memoryExpiry));
// L2: 分散式快取
if (_distributedProvider != null)
{
tasks.Add(_distributedProvider.SetAsync(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>
{
_memoryProvider.RemoveAsync(key)
};
if (_distributedProvider != null)
{
tasks.Add(_distributedProvider.RemoveAsync(key));
}
var results = await Task.WhenAll(tasks);
return results.Any(r => r);
}
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 (await _memoryProvider.ExistsAsync(key))
return true;
if (_distributedProvider != null)
return await _distributedProvider.ExistsAsync(key);
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