RRedis Handbook

UZMAN

Redis Exception Handling

StackExchange.Redis farklı hata türleri fırlatır. Doğru yakalama ve yönetim kritik.

Kod örneği görünümü Bu sayfadaki eşleşen örnekleri seçilen istemciye göre gösterir.

Exception Tipleri

Exception Ne Zaman Retry? Aksiyon
RedisConnectionException Bağlantı kopuk/kurulamıyor Evet Polly retry + circuit breaker
RedisTimeoutException Komut timeout'a uğradı Dikkatli İlk retry, 3. tekrarda degrade
RedisServerException Redis hata döndü (OOM, READONLY, WRONGTYPE) Genelde hayır Mesajı parse et, uygun handle
RedisCommandException Komut sözdizimi/izin hatası Hayır Kod düzelt, ACL kontrol
ObjectDisposedException Multiplexer dispose edilmiş Hayır Lifecycle hatası — DI kontrol
# Timeout sorunlarını diagnose et
redis-cli --latency          # ortalama latency
redis-cli --latency-history  # tarihsel
redis-cli --bigkeys          # büyük key'ler = yavaş ops

# Yavaş komutları logla
CONFIG SET slowlog-log-slower-than 10000  # 10ms+
SLOWLOG GET 10                              # son 10 yavaş komut
SLOWLOG LEN
SLOWLOG RESET

# Memory sorunları
INFO memory
MEMORY DOCTOR
MEMORY USAGE key           # tek key'in byte maliyeti
public class RedisExceptionHandler
{
    private readonly IDatabase _redis;
    private readonly ILogger _logger;

    public RedisExceptionHandler(IConnectionMultiplexer mux, ILogger<RedisExceptionHandler> logger)
    {
        _redis = mux.GetDatabase();
        _logger = logger;
    }

    public async Task<T?> ExecuteWithHandlingAsync<T>(string key, Func<IDatabase, Task<T>> operation)
    {
        try
        {
            return await operation(_redis);
        }
        catch (RedisConnectionException ex)
        {
            // Bağlantı yok — retry pipeline'a düşür veya graceful degrade
            _logger.LogWarning(ex, "Redis connection failed for {Key}", key);
            return default; // Fallback: DB'den oku
        }
        catch (RedisTimeoutException ex)
        {
            // Komut timeout — Redis yoğun veya big key blokaj
            _logger.LogWarning(ex, "Redis timeout for {Key}. Timeout: {Timeout}ms",
                key, ex.Message);
            return default;
        }
        catch (RedisServerException ex) when (ex.Message.StartsWith("OOM"))
        {
            // Redis memory dolu — eviction policy'ye rağmen yazılamıyor
            _logger.LogError(ex, "Redis OOM! Key: {Key}", key);
            throw; // Bu ciddi — uygulama seviyesinde handle et
        }
        catch (RedisServerException ex) when (ex.Message.StartsWith("READONLY"))
        {
            // Failover sırasında — replica'ya yazma denemesi
            _logger.LogWarning(ex, "Redis READONLY — failover in progress?");
            await Task.Delay(1000); // Kısa bekle, yeni master keşfedilsin
            return await operation(_redis); // 1 retry
        }
        catch (RedisServerException ex) when (ex.Message.StartsWith("WRONGTYPE"))
        {
            // Yanlış veri tipi — kod hatası (SET ile yazılmış, HGET ile okunuyor)
            _logger.LogError(ex, "Redis WRONGTYPE for {Key} — data type mismatch", key);
            throw; // Caller'ın düzeltmesi gereken bug
        }
    }
}
// Polly retry — StackExchange.Redis timeout'lar için önerilen strateji
builder.Services.AddResiliencePipeline("redis", builder =>
{
    builder
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromMilliseconds(200),
            BackoffType = DelayBackoffType.Exponential,
            ShouldHandle = new PredicateBuilder()
                .Handle<RedisConnectionException>()
                .Handle<RedisTimeoutException>()
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            MinimumThroughput = 10,
            SamplingDuration = TimeSpan.FromSeconds(30),
            BreakDuration = TimeSpan.FromSeconds(15),
            ShouldHandle = new PredicateBuilder()
                .Handle<RedisConnectionException>()
        })
        .AddTimeout(TimeSpan.FromSeconds(3));
});

Timeout tuning: SyncTimeout ve AsyncTimeout (varsayılan 5s) çok düşük ayarlanırsa normal SCAN/SORT bile timeout alır. Çok yüksekse thread pool tıkanır. Başlangıç: 3000ms, monitoring ile ayarla.

READONLY hatası genellikle Sentinel failover sırasında olur. StackExchange.Redis otomatik yeniden bağlanır ama 1-2 saniyelik pencerede yazımlar başarısız olabilir.