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(key); if (value != null) { return await SetAsync(key, value, expiry); } return false; } catch (Exception ex) { _logger.LogError(ex, "Error setting expiry for key: {Key}", key); return false; } } public async Task ClearAsync() { try { var tasks = new List> { _memoryProvider.ClearAsync() }; if (_distributedProvider != null) { tasks.Add(_distributedProvider.ClearAsync()); } await Task.WhenAll(tasks); return true; } catch (Exception ex) { _logger.LogError(ex, "Error clearing cache"); return false; } } public async Task> GetManyAsync(IEnumerable keys) where T : class { var keyList = keys.ToList(); var result = new Dictionary(); if (!keyList.Any()) return result; try { var tasks = keyList.Select(async key => { var value = await GetAsync(key); return new KeyValuePair(key, value); }); var results = await Task.WhenAll(tasks); return results.ToDictionary(r => r.Key, r => r.Value); } catch (Exception ex) { _logger.LogError(ex, "Error getting multiple cache values"); return result; } } public async Task SetManyAsync(Dictionary keyValuePairs, TimeSpan? expiry = null) where T : class { if (!keyValuePairs.Any()) return true; try { var tasks = keyValuePairs.Select(async kvp => await SetAsync(kvp.Key, kvp.Value, expiry)); var results = await Task.WhenAll(tasks); return results.All(r => r); } catch (Exception ex) { _logger.LogError(ex, "Error setting multiple cache values"); return false; } } public Task GetStatsAsync() { _stats.LastUpdated = DateTime.UtcNow; return Task.FromResult(_stats); } private async Task SetMultiLevelCacheAsync(string key, T value) where T : class { var expiry = _strategyManager.CalculateSmartExpiry(key, value); var memoryExpiry = _strategyManager.CalculateMemoryExpiry(key); await _memoryProvider.SetAsync(key, value, memoryExpiry); if (_distributedProvider != null) { await _distributedProvider.SetAsync(key, value, expiry); } } }