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);
}
}