İLERİ
Client-Side Caching (Tracking)
Redis 6+ RESP3 ile server-assisted client caching: Redis, client'ın cache'lediği key'ler değiştiğinde invalidation bildirimi gönderir. Network roundtrip'i tamamen ortadan kaldırır.
Kod örneği görünümü
Bu sayfadaki eşleşen örnekleri seçilen istemciye göre gösterir.
Nasıl Çalışır?
- Client,
CLIENT TRACKING ONile tracking'i aktifleştirir - Client GET yaptığında Redis hangi key'lerin okunduğunu takip eder
- Başka bir client o key'i değiştirdiğinde Redis invalidation mesajı gönderir
- Client local cache'den ilgili entry'yi siler → sonraki okuma Redis'e gider
# RESP3 ile tracking aktifleştir
CLIENT TRACKING ON REDIRECT <client-id>
# Broadcasting mode (pattern-based, daha az memory kullanır)
CLIENT TRACKING ON BCAST PREFIX user: PREFIX product:
# Tracking durumu
CLIENT TRACKINGINFO
# Hangi client'lar tracking kullanıyor?
CLIENT LIST
# flags=T → tracking aktif
# Manuel invalidate (test/debug)
CLIENT TRACKING ON
GET user:1001
# Başka terminal:
SET user:1001 "updated"
# İlk terminal invalidation alır
// Client-side caching: IMemoryCache + Redis invalidation
// StackExchange.Redis 2.6.100+ RESP3 tracking desteği sunar
// Ancak tam otomatik client-side cache henüz built-in değil.
// En pratik yaklaşım: Keyspace notifications + IMemoryCache hybrid
public class ClientSideCacheService
{
private readonly IDatabase _redis;
private readonly IMemoryCache _localCache;
private readonly ISubscriber _subscriber;
private readonly TimeSpan _localTtl = TimeSpan.FromSeconds(30);
private readonly ILogger<ClientSideCacheService> _logger;
public ClientSideCacheService(
IConnectionMultiplexer mux,
IMemoryCache memoryCache,
ILogger<ClientSideCacheService> logger)
{
_redis = mux.GetDatabase();
_localCache = memoryCache;
_subscriber = mux.GetSubscriber();
_logger = logger;
SubscribeToInvalidations();
}
private void SubscribeToInvalidations()
{
// Keyspace notifications ile invalidation
// redis.conf: notify-keyspace-events KEA
_subscriber.Subscribe(
RedisChannel.Pattern("__keyevent@0__:*"),
(channel, key) =>
{
var eventType = channel.ToString().Split(':').Last();
if (eventType is "set" or "del" or "expire" or "expired")
{
_localCache.Remove(key.ToString());
_logger.LogDebug("Local cache invalidated: {Key} ({Event})",
key, eventType);
}
});
}
// L1 (local) → L2 (Redis) stratejisi
public async Task<T?> GetAsync<T>(string key) where T : class
{
// L1: Memory cache (0 latency)
if (_localCache.TryGetValue(key, out T? local))
return local;
// L2: Redis
var value = await _redis.StringGetAsync(key);
if (!value.HasValue) return null;
var result = JsonSerializer.Deserialize<T>(value!);
if (result is not null)
{
// L1'e yaz — invalidation gelene kadar geçerli
_localCache.Set(key, result, _localTtl);
}
return result;
}
public async Task SetAsync<T>(string key, T value, TimeSpan ttl)
{
await _redis.StringSetAsync(key,
JsonSerializer.Serialize(value), ttl);
// Local cache'i güncelle (kendi yazdığımızı biz biliriz)
_localCache.Set(key, value, _localTtl);
}
}
// DI kayıt
builder.Services.AddMemoryCache();
builder.Services.AddSingleton<ClientSideCacheService>();
// redis.conf'ta keyspace notification aktifleştir:
// notify-keyspace-events KEA
| Yaklaşım | Latency | Tutarlılık | Memory | Kullanım |
|---|---|---|---|---|
| Sadece Redis | ~0.5ms | Güçlü | 0 client | Çoğu uygulama |
| L1+L2 (invalidation) | ~0 (hit) | Eventual (~ms) | Client RAM | Hot key protection |
| L1+L2 (short TTL) | ~0 (hit) | Eventual (TTL kadar) | Client RAM | Basit, notification gerekmez |
| RESP3 Tracking (native) | ~0 (hit) | Güçlü | Server tracks | SE.Redis 2.8+ (gelişmekte) |
Keyspace notifications overhead: Çok sayıda key değişimiyle notification volume artabilir. Pattern ile filtrele veya BCAST mode kullan.
Ne zaman kullan: Saniyede >10K okuma yapılan hot key'ler, read/write oranı >10:1 olan veriler. Her key için kullanma — sadece hot data.