API Önbellekleme: Gereksiz İstekleri Azaltarak Performansı Artırma
Prodüksiyonda bir API entegrasyonu yönetiyorsanız, muhtemelen şu soruyla karşılaşmışsınızdır: “Neden bu kadar çok istek gidiyor?” Rate limit aşımları, artan maliyet faturaları, yavaşlayan uygulama performansı… Bunların büyük çoğunluğunun arkasında aynı veriyi tekrar tekrar çekme alışkanlığı yatıyor. API önbellekleme, bu problemi çözmenin hem en zarif hem de en etkili yolu. Bu yazıda gerçek dünya senaryolarıyla, pratik kod örnekleriyle API önbelleklemeyi her boyutuyla ele alacağız.
API Önbelleklemenin Temelleri
Önbellekleme basit bir fikir üzerine kuruludur: Bir kez aldığın veriyi, tekrar lazım olana kadar sakla. Ama bu basit fikri doğru uygulamak, sisteminizin ölçeklenebilirliği açısından kritik önem taşıyor.
Bir API isteği yaptığınızda şu maliyet kalemleri devreye girer:
- Network latency: İstek gidip gelene kadar geçen süre
- API rate limit tüketimi: Özellikle üçüncü parti servislerde kotanız azalır
- Maliyet: Çoğu ticari API istek başına ücret alır
- Backend yükü: Kendi API’nızsa sunucu kaynakları tükenir
Önbellekleme bu maliyetlerin tamamını dramatik şekilde azaltır. Ancak hangi veriyi ne kadar süre önbelleğe alacağınızı doğru belirlemek gerekiyor.
TTL (Time To Live) Kavramı
Her önbellekleme stratejisinin merkezinde TTL bulunur. TTL, bir verinin önbellekte ne kadar süre geçerli kalacağını belirler. Çok kısa TTL, önbelleklemenin faydasını ortadan kaldırır. Çok uzun TTL ise eski veri sunma riskini artırır.
Pratik TTL kılavuzu:
- Döviz kurları, hisse fiyatları: 30 saniye ile 5 dakika arası
- Hava durumu verisi: 10-30 dakika
- Kullanıcı profil bilgileri: 5-15 dakika
- Ürün katalog verisi: 1-24 saat
- Statik referans verisi (şehir listesi, kategori vb.): 24 saat ile 7 gün arası
Redis ile Temel API Önbellekleme
Redis, API önbellekleme için endüstri standardı haline gelmiş bir çözüm. Hızı, TTL desteği ve pub/sub özellikleriyle ideal bir araç.
Önce basit bir örnek görelim. Python ile bir dış hava durumu API’sini önbellekleyelim:
# Redis kurulumu (Ubuntu/Debian)
sudo apt update
sudo apt install redis-server
sudo systemctl enable redis-server
sudo systemctl start redis-server
# Python bağımlılıkları
pip install redis requests python-dotenv
# weather_cache.py - Hava durumu API önbellekleme örneği
cat << 'EOF' > weather_cache.py
import redis
import requests
import json
import hashlib
import os
from datetime import datetime
# Redis bağlantısı
r = redis.Redis(
host='localhost',
port=6379,
db=0,
decode_responses=True
)
WEATHER_API_KEY = os.getenv('WEATHER_API_KEY', 'your_api_key')
CACHE_TTL = 600 # 10 dakika
def get_cache_key(city: str) -> str:
"""Şehir adından deterministik cache key üretir"""
normalized = city.lower().strip()
return f"weather:v1:{hashlib.md5(normalized.encode()).hexdigest()}"
def get_weather(city: str) -> dict:
cache_key = get_cache_key(city)
# Önce cache kontrol et
cached_data = r.get(cache_key)
if cached_data:
data = json.loads(cached_data)
data['_cache_hit'] = True
data['_cached_at'] = r.ttl(cache_key)
return data
# Cache miss - API'ye git
url = f"https://api.openweathermap.org/data/2.5/weather"
params = {
'q': city,
'appid': WEATHER_API_KEY,
'units': 'metric',
'lang': 'tr'
}
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
data = response.json()
# Cache'e yaz
data['_fetched_at'] = datetime.utcnow().isoformat()
r.setex(cache_key, CACHE_TTL, json.dumps(data))
data['_cache_hit'] = False
return data
if __name__ == '__main__':
result = get_weather('Istanbul')
print(f"Cache hit: {result.get('_cache_hit')}")
print(f"Sıcaklık: {result['main']['temp']}°C")
EOF
python3 weather_cache.py
Bu basit yapı bile ciddi fark yaratır. 10 dakikalık TTL ile saatte 6 API isteğiyle sınırlanmış olursunuz, 600 istek yerine.
Cache-Aside Pattern ile İleri Seviye Önbellekleme
Cache-aside (lazy loading olarak da bilinir), en yaygın önbellekleme desenidir. Uygulama önce cache’e bakar, bulamazsa kaynak API’yi çağırır ve sonucu cache’e yazar.
# cache_aside.py - Cache-aside pattern implementasyonu
cat << 'EOF' > cache_aside.py
import redis
import requests
import json
import logging
from functools import wraps
from typing import Optional, Callable, Any
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
redis_client = redis.Redis(host='localhost', port=6379, db=1, decode_responses=True)
def api_cache(ttl: int = 300, key_prefix: str = "api"):
"""
API fonksiyonları için cache decorator
Kullanim:
@api_cache(ttl=600, key_prefix="github")
def get_repo_info(owner, repo):
...
"""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
# Cache key oluştur
key_parts = [key_prefix, func.__name__] + [str(a) for a in args]
key_parts += [f"{k}={v}" for k, v in sorted(kwargs.items())]
cache_key = ":".join(key_parts)
# Cache'den oku
try:
cached = redis_client.get(cache_key)
if cached is not None:
logger.info(f"CACHE HIT: {cache_key}")
return json.loads(cached)
except redis.RedisError as e:
logger.warning(f"Redis okuma hatasi: {e}. API'ye geciliyor.")
# API'yi cagir
logger.info(f"CACHE MISS: {cache_key} - API isteği yapılıyor")
result = func(*args, **kwargs)
# Sonucu cache'e yaz
try:
redis_client.setex(cache_key, ttl, json.dumps(result))
logger.info(f"Cache yazıldı: {cache_key} (TTL: {ttl}s)")
except redis.RedisError as e:
logger.warning(f"Redis yazma hatasi: {e}")
return result
# Cache'i temizlemek icin yardimci metod
wrapper.invalidate = lambda *args, **kwargs: _invalidate_cache(
key_prefix, func.__name__, args, kwargs
)
return wrapper
return decorator
def _invalidate_cache(prefix, func_name, args, kwargs):
key_parts = [prefix, func_name] + [str(a) for a in args]
key_parts += [f"{k}={v}" for k, v in sorted(kwargs.items())]
cache_key = ":".join(key_parts)
deleted = redis_client.delete(cache_key)
logger.info(f"Cache invalidated: {cache_key} (deleted: {deleted})")
return deleted
# Kullanim ornegi
@api_cache(ttl=3600, key_prefix="github")
def get_github_repo(owner: str, repo: str) -> dict:
url = f"https://api.github.com/repos/{owner}/{repo}"
response = requests.get(url, timeout=10)
response.raise_for_status()
return response.json()
# Test
if __name__ == '__main__':
# Ilk istek - cache miss
data = get_github_repo("torvalds", "linux")
print(f"Repo: {data['full_name']}, Stars: {data['stargazers_count']}")
# Ikinci istek - cache hit
data2 = get_github_repo("torvalds", "linux")
print(f"Repo: {data2['full_name']} (cache'den)")
EOF
Stale-While-Revalidate Stratejisi
Gerçek dünya senaryolarında en büyük zorluk şu: Cache süresi dolduğunda kullanıcı beklemek zorunda kalmamalı. Stale-while-revalidate bu problemi çözer. Eski veriyi anında dönerken arka planda yeni veriyi çeker.
# swr_cache.py - Stale-While-Revalidate implementasyonu
cat << 'EOF' > swr_cache.py
import redis
import requests
import json
import threading
import time
import logging
from dataclasses import dataclass
from typing import Optional
logger = logging.getLogger(__name__)
r = redis.Redis(host='localhost', port=6379, db=2, decode_responses=True)
@dataclass
class CacheEntry:
data: dict
fetched_at: float
ttl: int
stale_ttl: int # Stale veri kac saniye daha kullanilabilir
def get_with_swr(
cache_key: str,
fetch_func,
ttl: int = 60,
stale_ttl: int = 300
) -> Optional[dict]:
"""
Stale-While-Revalidate pattern
ttl: Fresh veri suresi
stale_ttl: Eski veri kac saniye daha kabul edilir (arka planda yenilenir)
"""
raw = r.get(cache_key)
now = time.time()
if raw:
entry = json.loads(raw)
age = now - entry['fetched_at']
if age < entry['ttl']:
# Fresh veri, direkt dondur
entry['data']['_status'] = 'fresh'
return entry['data']
elif age < entry['ttl'] + entry['stale_ttl']:
# Stale ama kullanilabilir - arka planda yenile
entry['data']['_status'] = 'stale'
def revalidate():
try:
new_data = fetch_func()
cache_entry = {
'data': new_data,
'fetched_at': time.time(),
'ttl': ttl,
'stale_ttl': stale_ttl
}
r.setex(cache_key, ttl + stale_ttl, json.dumps(cache_entry))
logger.info(f"SWR revalidation tamamlandi: {cache_key}")
except Exception as e:
logger.error(f"SWR revalidation hatasi: {e}")
thread = threading.Thread(target=revalidate, daemon=True)
thread.start()
return entry['data']
# Cache yok veya tamamen expired - senkron fetch
logger.info(f"Cache miss, senkron fetch: {cache_key}")
data = fetch_func()
cache_entry = {
'data': data,
'fetched_at': now,
'ttl': ttl,
'stale_ttl': stale_ttl
}
r.setex(cache_key, ttl + stale_ttl, json.dumps(cache_entry))
data['_status'] = 'miss'
return data
# Kullanim
def fetch_exchange_rates():
response = requests.get(
'https://api.exchangerate-api.com/v4/latest/USD',
timeout=5
)
return response.json()
rates = get_with_swr(
cache_key='exchange:USD:v1',
fetch_func=fetch_exchange_rates,
ttl=300, # 5 dakika fresh
stale_ttl=600 # 10 dakika daha stale olarak kullan
)
print(f"Status: {rates.get('_status')}")
EOF
Nginx Seviyesinde API Önbellekleme
Uygulama katmanının önüne Nginx önbelleği koyarak çok daha verimli bir yapı kurabilirsiniz. Bu yaklaşım uygulama sunucusuna hiç ulaşmadan cache’den yanıt verir.
# /etc/nginx/conf.d/api_cache.conf
# Cache zone tanımla - 1GB disk, 10MB metadata
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
# Rate limiting zone
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 80;
server_name api.example.com;
# Cache key - method + host + uri + query string
proxy_cache_key "$request_method$host$request_uri";
location /api/v1/weather {
proxy_pass http://backend:8000;
proxy_cache api_cache;
proxy_cache_valid 200 10m; # Basarili yanit 10 dk cache
proxy_cache_valid 404 1m; # 404 1 dk cache
proxy_cache_valid 500 0; # 500 hatalari cache'leme
# Stale cache kullan - backend down olsa bile
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
proxy_cache_background_update on;
proxy_cache_lock on;
# Cache durumunu header'a ekle
add_header X-Cache-Status $upstream_cache_status;
add_header X-Cache-Date $upstream_http_date;
# Rate limiting
limit_req zone=api_limit burst=20 nodelay;
}
location /api/v1/user {
# Kullanici verisi - Authorization header'a gore cache
proxy_cache_key "$request_method$host$request_uri$http_authorization";
proxy_pass http://backend:8000;
proxy_cache api_cache;
proxy_cache_valid 200 5m;
# Authenticated istekler icin cache bypass
proxy_cache_bypass $http_pragma;
proxy_no_cache $http_pragma;
add_header X-Cache-Status $upstream_cache_status;
}
}
# Nginx cache durumunu izleme
# X-Cache-Status header değerleri:
# HIT - Cache'den servisi edildi
# MISS - Backend'e gidildi, cache'e yazildi
# BYPASS - Cache atlandı
# EXPIRED - Cache suresi doldu, backend'e gidildi
# STALE - Eski cache kullanildi (backend down)
# Cache istatistiklerini izle
watch -n 1 'curl -sI http://api.example.com/api/v1/weather?city=Istanbul | grep -i cache'
# Cache'i temizle (belirli URL pattern için)
find /var/cache/nginx/api -type f -name "*.cache" -mmin +60 -delete
Çok Katmanlı Önbellekleme Mimarisi
Büyük ölçekli sistemlerde tek katman yetmez. L1 (local memory), L2 (Redis), L3 (CDN/Nginx) şeklinde katmanlı bir yapı kurabilirsiniz.
# multilayer_cache.py - Çok katmanlı cache implementasyonu
cat << 'EOF' > multilayer_cache.py
import redis
import requests
import json
import time
import logging
from cachetools import TTLCache
from threading import Lock
logger = logging.getLogger(__name__)
class MultiLayerCache:
"""
L1: In-memory (cachetools TTLCache) - microsaniye erisim
L2: Redis - milisaniye erisim
L3: Origin API - saniye erisim
"""
def __init__(self):
# L1: Maksimum 1000 item, 60 saniye TTL
self._l1_cache = TTLCache(maxsize=1000, ttl=60)
self._l1_lock = Lock()
# L2: Redis
self._redis = redis.Redis(
host='localhost',
port=6379,
db=3,
decode_responses=True,
socket_connect_timeout=1,
socket_timeout=1
)
self.stats = {'l1_hits': 0, 'l2_hits': 0, 'api_calls': 0}
def get(self, key: str, fetch_func, l1_ttl=60, l2_ttl=300) -> dict:
# L1 kontrol
with self._l1_lock:
if key in self._l1_cache:
self.stats['l1_hits'] += 1
logger.debug(f"L1 HIT: {key}")
return self._l1_cache[key]
# L2 kontrol
try:
raw = self._redis.get(key)
if raw:
data = json.loads(raw)
# L1'e de yaz
with self._l1_lock:
self._l1_cache[key] = data
self.stats['l2_hits'] += 1
logger.debug(f"L2 HIT: {key}")
return data
except redis.RedisError as e:
logger.warning(f"Redis erisim hatasi: {e}")
# API'ye git
logger.info(f"API CALL: {key}")
data = fetch_func()
self.stats['api_calls'] += 1
# L2'ye yaz
try:
self._redis.setex(key, l2_ttl, json.dumps(data))
except redis.RedisError:
pass
# L1'e yaz
with self._l1_lock:
self._l1_cache[key] = data
return data
def invalidate(self, key: str):
"""Her iki cache katmanından da sil"""
with self._l1_lock:
self._l1_cache.pop(key, None)
try:
self._redis.delete(key)
except redis.RedisError:
pass
logger.info(f"Cache invalidated: {key}")
def get_stats(self) -> dict:
total = sum(self.stats.values())
if total == 0:
return self.stats
return {
**self.stats,
'l1_hit_rate': f"{self.stats['l1_hits']/total*100:.1f}%",
'l2_hit_rate': f"{self.stats['l2_hits']/total*100:.1f}%",
'api_call_rate': f"{self.stats['api_calls']/total*100:.1f}%"
}
# Kullanim
cache = MultiLayerCache()
def fetch_product(product_id: int) -> dict:
response = requests.get(
f"https://api.example.com/products/{product_id}",
timeout=5
)
return response.json()
# Her cagri icin fetch fonksiyonunu lambda ile wrap et
product = cache.get(
key=f"product:{123}",
fetch_func=lambda: fetch_product(123),
l1_ttl=60,
l2_ttl=3600
)
print(f"İstatistikler: {cache.get_stats()}")
EOF
Cache Invalidation Stratejileri
Cache’in en zor kısmı invalidation, yani ne zaman sileceğinizi bilmek. Phil Karlton’ın meşhur sözü burada geçerli: “Bilgisayar biliminde iki zor şey var: cache invalidation ve isimlendirme.”
Pratik invalidation yaklaşımları:
- TTL tabanlı: En basiti, süre dolunca otomatik siler. Kontrol gerekmez ama stale veri riski var.
- Event tabanlı: Veri değiştiğinde ilgili cache key’lerini sil. Güvenilir ama ekstra kod gerektirir.
- Tag tabanlı: Cache entry’lere tag atayıp tag bazında toplu silme. Esnek ama karmaşık.
# cache_invalidation.py - Event tabanlı invalidation
cat << 'EOF' > cache_invalidation.py
import redis
import json
import logging
from typing import List, Set
logger = logging.getLogger(__name__)
r = redis.Redis(host='localhost', port=6379, db=4, decode_responses=True)
class TaggedCache:
"""
Tag tabanlı cache - örnek:
Bir ürün güncellendiğinde, o ürünü listeleyen tüm cache
entry'lerini tek komutla silebilirsiniz.
"""
TAG_PREFIX = "tag:"
CACHE_PREFIX = "cache:"
def set(self, key: str, value: dict, ttl: int, tags: List[str] = None):
cache_key = f"{self.CACHE_PREFIX}{key}"
# Ana veriyi yaz
r.setex(cache_key, ttl, json.dumps(value))
# Tag ilişkilerini kaydet
if tags:
pipe = r.pipeline()
for tag in tags:
tag_key = f"{self.TAG_PREFIX}{tag}"
pipe.sadd(tag_key, cache_key)
pipe.expire(tag_key, ttl + 3600) # Tag biraz daha uzun yasasin
pipe.execute()
def get(self, key: str) -> dict:
cache_key = f"{self.CACHE_PREFIX}{key}"
raw = r.get(cache_key)
return json.loads(raw) if raw else None
def invalidate_by_tag(self, tag: str) -> int:
"""Bir tag'e ait tum cache entry'lerini sil"""
tag_key = f"{self.TAG_PREFIX}{tag}"
keys = r.smembers(tag_key)
if not keys:
return 0
pipe = r.pipeline()
for key in keys:
pipe.delete(key)
pipe.delete(tag_key)
results = pipe.execute()
deleted = sum(1 for r in results[:-1] if r)
logger.info(f"Tag '{tag}' invalidated: {deleted} key silindi")
return deleted
# Kullanim senaryosu: E-ticaret
cache = TaggedCache()
# Urun listesi ve urun detayini cache'le, her ikisine de "product:123" tag'i ekle
cache.set(
key="product_list:category:elektronik:page:1",
value={"products": [{"id": 123, "name": "Laptop"}]},
ttl=3600,
tags=["product:123", "category:elektronik", "product_list"]
)
cache.set(
key="product_detail:123",
value={"id": 123, "name": "Laptop", "price": 15000},
ttl=3600,
tags=["product:123"]
)
# Urun 123 guncellendi - ilgili tum cache'leri tek komutla temizle
deleted_count = cache.invalidate_by_tag("product:123")
print(f"Urun 123 cache'leri temizlendi: {deleted_count} entry")
EOF
Önbellekleme Metriklerini İzleme
Önbelleklemenin ne kadar etkili çalıştığını ölçmeden yarım bırakmış olursunuz. Hit rate, miss rate ve latency metriklerini izlemek şart.
# cache_metrics.py - Prometheus metrikleri ile cache izleme
cat << 'EOF' > cache_metrics.py
import time
import redis
import logging
from prometheus_client import Counter, Histogram, Gauge, start_http_server
logger = logging.getLogger(__name__)
# Prometheus metrikleri
CACHE_HITS = Counter('api_cache_hits_total', 'Cache hit sayisi', ['endpoint', 'layer'])
CACHE_MISSES = Counter('api_cache_misses_total', 'Cache miss sayisi', ['endpoint'])
CACHE_LATENCY = Histogram(
'api_cache_operation_seconds',
'Cache islem suresi',
['operation'],
buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.5]
)
CACHE_SIZE = Gauge('redis_cache_keys_total', 'Redis toplam key sayisi')
r = redis.Redis(host='localhost', port=6379, db=5, decode_responses=True)
def monitored_get(cache_key: str, endpoint: str) -> dict:
start = time.time()
result = r.get(cache_key)
latency = time.time() - start
CACHE_LATENCY.labels(operation='get').observe(latency)
if result:
CACHE_HITS.labels(endpoint=endpoint, layer='redis').inc()
return {'data': result, 'hit': True, 'latency_ms': latency * 1000}
else:
CACHE_MISSES.labels(endpoint=endpoint).inc()
return {'data': None, 'hit': False, 'latency_ms': latency * 1000}
def update_cache_size_metric():
"""Redis key sayısını Prometheus'a raporla"""
try:
info = r.info('keyspace')
total_keys = sum(
db_info['keys']
for db_info in info.values()
if isinstance(db_info, dict) and 'keys' in db_info
)
CACHE_SIZE.set(total_keys)
except Exception as e:
logger.error(f"Cache size metric hatasi: {e}")
# Redis CLI ile hizli istatistik
# redis-cli info stats | grep -E "keyspace_hits|keyspace_misses"
# redis-cli info memory | grep used_memory_human
if __name__ == '__main__':
# Prometheus metrics endpoint'i baslat
start_http_server(8001)
print("Metrics: http://localhost:8001")
while True:
update_cache_size_metric()
time.sleep(15)
EOF
# Redis cache hit rate hesapla (CLI ile)
redis-cli info stats | grep -E "keyspace_hits|keyspace_misses" | awk -F: '
{
gsub(/r/, "")
if ($1 == "keyspace_hits") hits = $2
if ($1 == "keyspace_misses") misses = $2
}
END {
total = hits + misses
if (total > 0)
printf "Hit Rate: %.2f%%nHits: %dnMisses: %dn", (hits/total*100), hits, misses
}'
Gerçek Dünya Senaryosu: E-Ticaret API Entegrasyonu
Bir e-ticaret platformu düşünün. Günde 500.000 ürün görüntülenme, 50.000 benzersiz ürün, bir ürün detay sayfası için 3-5 harici API çağrısı (fiyat, stok, öneri). Bu hesapla günde 2.5 milyon API isteği. Rate limit aşımı kaçınılmaz.
Önbellekleme stratejisi uygulandıktan sonra:
- Ürün temel bilgisi (isim, açıklama): 24 saat TTL, tag tabanlı invalidation
- Fiyat bilgisi: 5 dakika TTL, yoğun değişimde event tabanlı invalidation
- Stok durumu: 1 dakika TTL, kritik olduğu için kısa tutulur
- Öneri listesi: 30 dakika TTL, kişiselleştirilmemiş öneri
Bu yapıyla günlük 2.5 milyon istek yaklaşık 150.000 gerçek API isteğine düşer. %94 tasarruf. Hem maliyet hem de performans açısından devrim niteliğinde.
Dikkat Edilmesi Gereken Tuzaklar
API önbellekleme uygularken sıkça karşılaşılan hatalar:
- Her şeyi önbellekleme: Kullanıcıya özel, güvenlik kritik verileri asla önbellekleme. Authorization gerektiren veri, kullanıcı bazında izole edilmeli.
- Cache key çakışması: Yeterince benzersiz key üretmezseniz farklı kullanıcıların verileri birbirine karışabilir. Key’e kullanıcı ID, versiyon numarası ve parametre hash’i ekleyin.
- Cache stampede: Aynı anda yüzlerce istek aynı expired key’e gelirse hepsi birden backend’e gider.
proxy_cache_lock onveya mutex ile sadece bir istek geçsin, diğerleri beklesin.
- Sensitive data cache’leme: Token, şifre, kişisel sağlık verisi gibi hassas bilgileri kesinlikle önbellekleme.
- Cache’i tek point of failure yapma: Redis down olduğunda uygulama da çökmesin. Her zaman fallback olarak doğrudan API çağrısına geçebilir olun.
Sonuç
API önbellekleme, sistem yönetimi ve backend geliştirme dünyasında en yüksek ROI’ya sahip tekniklerden biri. Doğru uygulandığında API maliyetlerini dramatik biçimde düşürür, rate limit sorunlarını ortadan kaldırır ve kullanıcı deneyimini iyileştirir.
Başlangıç için karmaşık bir mimari kurmak zorunda değilsiniz. Basit bir Redis TTL önbellekleme bile ciddi fark yaratır. Sonra gereksinimlerinize göre stale-while-revalidate, çok katmanlı cache veya tag tabanlı invalidation ekleyebilirsiniz.
En önemli nokta: Metriklerinizi izleyin. Cache hit rate’iniz %80’in altındaysa TTL’lerinizi veya önbellekleme stratejinizi gözden geçirin. Hit rate’iniz %95+ ise muhtemelen TTL’lerinizi çok uzun tutuyorsunuzdur ve stale veri riski taşıyorsunuzdur. Her sistem farklı, bu yüzden ölçmeden karar vermeyin.
