using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using StackExchange.Redis; using Sozsoft.Languages.Localization; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; namespace Sozsoft.Languages.Caching; /// /// Uygulamanın dağıtık önbelleğini boşaltır. FLUSHALL yerine yalnızca bu uygulamanın anahtarlarını /// siler: Redis başka bir uygulama ya da ortamla paylaşılıyorsa onların verisine dokunulmaz. /// Bağlantı süreç başına tektir; her çağrıda yeni bağlantı açmak bağlantı sızdırıyordu. /// public class DistributedCacheCleaner : ISingletonDependency, IDisposable { private const int BatchSize = 500; private readonly Lazy _connection; private readonly string[] _keyPatterns; private readonly IDatabaseResourceLocalizer _localizer; public DistributedCacheCleaner( IConfiguration configuration, IOptions cacheOptions, IDatabaseResourceLocalizer localizer) { _localizer = localizer; _connection = new Lazy( () => ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!)); // ABP iki biçimde yazar: IDistributedCache anahtarı [t:{kiracı},]c:{önbellek},k:{KeyPrefix}{anahtar}, // bellek içi tanım önbelleklerinin damgası ise doğrudan {KeyPrefix}_Abp...Stamp. Host "Platform", // DbMigrator "Platform:" öneki kullanır; ':' atılınca desenler iki sürecin anahtarlarını da kapsar. var prefix = (cacheOptions.Value.KeyPrefix ?? string.Empty).TrimEnd(':'); _keyPatterns = [$"{prefix}*", $"*,k:{prefix}*"]; } /// Silinen anahtar sayısı. public async Task ClearAsync() { var connection = _connection.Value; var database = connection.GetDatabase(); long removed = 0; foreach (var endpoint in connection.GetEndPoints()) { var server = connection.GetServer(endpoint); if (server.IsReplica || !server.IsConnected) { continue; } foreach (var pattern in _keyPatterns) { var batch = new List(BatchSize); await foreach (var key in server.KeysAsync(database.Database, pattern, BatchSize)) { batch.Add(key); if (batch.Count == BatchSize) { removed += await DeleteAsync(database, batch); batch.Clear(); } } removed += await DeleteAsync(database, batch); } } // Dil sözlüğünün süreç içi kopyası Redis'te değildir; temizlik onu da kapsar. _localizer.ClearLocalCache(); return removed; } // Anahtarlar tek tek silinir: çok anahtarlı DEL, cluster'da farklı slot'lara düşen anahtarları reddeder. private static async Task DeleteAsync(IDatabase database, List keys) { var results = await Task.WhenAll(keys.Select(key => database.KeyDeleteAsync(key))); return results.Count(deleted => deleted); } public void Dispose() { if (_connection.IsValueCreated) { _connection.Value.Dispose(); } GC.SuppressFinalize(this); } }