Bir servis başka bir servisi çağırdığında başarısızlık normaldir.
Network paketi kaybolabilir.
Dependency kısa süreli restart olabilir.
Database connection pool dolabilir.
Bu nedenle retry dağıtık sistemlerin temel araçlarından biridir.
Ama yanlış uygulanan retry sistemi iyileştirmek yerine arızayı büyütebilir.
Beş servis varsa retry sayısını çarpın
Şöyle bir zincir düşünelim:
API → Order → Payment → Fraud → Database
Her katmanın başarısız çağrıyı üç kez retry ettiğini varsayın.
Database sorun yaşadığı anda yukarıdaki katmanların birbirini tekrar tekrar çağırması beklediğinizden çok daha büyük bir trafik yaratabilir.
Sorun artık yalnızca database değildir.
Retry storm oluşmuştur.
Bu yüzden retry kararı en alt seviyeden başlayarak rastgele dağıtılmamalıdır.
Önce timeout
Retry politikasını konuşmadan önce timeout tanımlanmalıdır.
Bir dependency’nin normal response süresi 100 ms ise client’ın 60 saniye beklemesi genellikle anlamsızdır.
Timeout aynı zamanda resource protection mekanizmasıdır.
Bir servisin cevap vermeyen connection’ları sonsuza kadar açık tutmasını engeller.
Bununla birlikte timeout değerini rastgele 5 seconds yapmak da doğru değildir. Servisin gerçek P95/P99 latency değerlerine ve iş akışının toplam latency budget’ına göre belirlenmelidir.
Exponential backoff
İlk başarısız request’ten sonra anında tekrar denemek dependency’nin toparlanmasına zaman tanımaz.
Bu nedenle retry aralıkları artırılabilir:
100 ms
200 ms
400 ms
800 ms
1600 ms
Fakat burada başka bir problem ortaya çıkar.
Bin client aynı anda hata aldıysa hepsi aynı aralıklarla retry yapacaktır.
Yani 800 ms sonra tekrar aynı trafik duvarını oluşturursunuz.
Jitter
Jitter retry süresine randomness ekleyerek client’ların aynı anda tekrar saldırmasını önler.
AWS Well-Architected dokümantasyonu retry çağrılarının sınırlandırılmasını, exponential backoff kullanılmasını ve retry zamanlarının jitter ile dağıtılmasını açıkça öneriyor.
Basit örnek:
base delay = 800 ms
jitter = random(0, 400)
retry = 800–1200 ms
Bu küçük fark büyük sistemlerde ciddi etki yaratır.
Her hata retry edilmez
HTTP 500 bazen retry edilebilir.
429 Too Many Requests için Retry-After dikkate alınabilir.
Connection timeout transient olabilir.
Ama validation hatası retry edilmez.
401 Unauthorized yüz kez tekrar gönderildiğinde authenticated olmaz.
Yanlış retry politikası sistem kaynaklarını boşa harcar.
Bir diğer kritik nokta da side effect oluşturan işlemlerdir.
GET /product/42 retry açısından kolaydır.
POST /payments değildir.
Bu nedenle retry tasarımı idempotency ile birlikte düşünülmelidir.
Dağıtık sistemlerde güvenilirlik “hata olmasın” demek değildir.
Hata olduğunda sistemin panik yapmamasını sağlamaktır.