Echo Support / SRE • • 1.173 kelime • 6 dk okuma
Retry Storm Krizi: Masum Bir Yenileme Sistemi Nasıl Çökertir?
Ufak bir sunucu yavaşlaması milyonlarca istekle nasıl dev bir çöküşe dönüşür? Retry storm dinamiğini, thundering herd krizini ve jitter çözümünü keşfedin.
Kısa Özet (TL;DR — Doğrudan Yanıt): Retry storm (yeniden deneme fırtınası), yavaşlayan sunucuya istemcilerin aynı anda tekrar istek göndermesiyle oluşur. Sistem nefes alamaz. Trafik katlanır, kuyruklar taşar, altyapı çöker. Çözüm: İstek aralığını katlayarak açmak (exponential backoff), rastgele gecikme eklemek (jitter) ve aşırı yükte trafiği kesen devre kesici (circuit breaker) kullanmaktır.
Otobüs Kapısı Analojisi: Basit Bir Sıkışma Nasıl Felaket Olur?
Tıklım tıklım dolu bir belediye otobüsü düşünün. Durakta kapı hafifçe takılır. İki saniye gecikir.
Arkadaki yolcular hemen sabırsızlanır. Kapıyı öne doğru iter. Arkadan gelen itme basıncı mekanizmayı daha da sıkıştırır. Kapı motoru yanar. Basit bir sürtünme, yüzlerce kişinin aynı anda yüklenmesi yüzünden kalıcı bir mekanik kırılmaya dönüşür.
Dağıtık sistemler (birbirine bağlı çalışan sunucu kümeleri) aynı fizik kuralıyla çöker. Veritabanında tek bir sorgu 50 milisaniye yerine 2 saniye sürer. İstemci "cevap gelmedi" der, hemen yeni istek yollar. Eski istek hala içeride çalışırken yenisi gelir. Sunucu yükü ikiye katlanır. Kapı sıkışır, sistem tamamen donar.
Kriz Döngüsü: Adım Adım Retry Storm Felaketi
Retry storm tek bir anda patlamaz. Belirli bir zinciri takip eder:
- Sunucu mikro bir yavaşlama yaşar. Ağda kısa bir paket kaybı veya geçici bellek temizliği (garbage collection) tetiklenir.
- İstemci uygulamasındaki zaman aşımı (timeout) süresi dolar. İstemci isteği başarısız kabul eder.
- İstemci otomatik olarak isteği tekrarlar. Eski istek arka planda CPU harcarken yeni istek sıraya girer.
- İstek kuyrukları (request queues) taşar. Bellek tükenir.
- Pozitif geri besleme (positive feedback loop) başlar. Yanıtlar geciktikçe tekrarlar artar, tekrarlar arttıkça yanıtlar daha çok gecikir.
Bu tabloya Thundering Herd (gürleyen sürü) problemi eşlik eder. Aynı anda hata alan binlerce cihaz, aynı milisaniyede yeniden dener. Sunucu iş yapamaz, sadece gelen istekleri çöpe atmaya çalışırken enerjisini tüketir. Çöküş dinamiklerini canlı gözlemlemek için İnteraktif Simülasyon Araçları dağıtık mimari davranışlarını somutlaştırır.
Strateji Karşılaştırması: Sabit Aralık, Üstel Geri Çekilme ve Jitter
Yeniden deneme davranışı doğru tasarlanmazsa sistemin kendi ayağına sıktığı bir silaha dönüşür. Üç temel yaklaşım vardır:
| Yeniden Deneme Yöntemi | Çalışma Mantığı | Çökme Riski | Sunucu Yüküne Etkisi |
|---|---|---|---|
| Sabit Aralık (Fixed) | Her denemede aynı süre beklenir | Çok Yüksek | Darbe dalgaları üretir, sunucuyu boğar |
| Üstel Geri Çekilme (Exponential Backoff) | Bekleme süresi her adımda katlanır | Orta | Dalgayı öteler ancak senkronize kalır |
| Rastgele Gecikme (Jitter) | Katlanan süreye rastgele sapma eklenir | Çok Düşük | İstekleri zamana pürüzsüz yayar |
Sabit aralık kör bir döngüdür. Üstel geri çekilme zaman kazandırır ancak tüm istemciler aynı anda hatta düştüyse hepsi aynı anda bekler ve yine aynı anda hücum eder. Çözüm jitter eklemektir. Bekleme süresine küçük rastgele milisaniyeler serpiştirmek, istemci sürüsünü dağıtır.
Pratik Savunma Deseni: Devre Kesici ve Akıllı Geri Çekilme
Sistemi kurtaran asıl mimari ikili: İstemcide akıllı gecikme, servis girişinde devre kesici (circuit breaker).
[İstemci] ---> (Normal Trafik) ---> [Sunucu: Sağlıklı]
|
v (Hatalar Eşik Değeri Aştı)
[Devre Kesici: AÇIK] ---> İstek Gönderilmez (Hızlı Hata Dönüşü)
|
v (Dinlenme Süresi Sonu: Yarı Açık)
[Test İsteği Gönder] ---> Başarılıysa Devreyi Kapat (Normale Dön)
Devre kesici, aşırı yük algılandığında trafiği bıçak gibi keser. Sunucuya giden musluğu kapatır. İstemciye "şu an sunucu meşgul, boşuna istek yollama" der.
import random
import time
def call_with_backoff(operation, max_retries=3, base_sec=1.0):
for attempt in range(max_retries):
try:
return operation()
except Exception:
if attempt == max_retries - 1:
raise
# ponytail: stdlib random yeterli; dagitik kume buyuyunce tam circuit breaker bagla
sleep_time = (base_sec * (2 ** attempt)) + random.uniform(0.1, 0.6)
time.sleep(sleep_time)
Kod, başarısız her denemede bekleme süresini katlar ve üzerine rastgele gecikme ekleyerek sunucuya eşzamanlı yük binmesini engeller.
call_with_backoff → skipped: Harici durum deposu ve devre kesici metrikleri, add when Sunucu filosu 50 düğüm üzerine çıktığında.
Yaygın Yanılgılar: Kullanıcı Dostu Zannettiğimiz Hatalar
Geliştiriciler genelde iyi niyetle sistemi savunmasız bırakır:
- "Anında tekrar denemek kullanıcıyı bekletmez:" Yanlış. Sunucu zorlanırken yapılan anlık tekrar, çökmeyi kesinleştirir. 500 milisaniye beklemek, servisin tamamen kapanmasından iyidir.
- "Zaman aşımını sonsuz yapmak bağlantıyı korur:" Yanlış. Kapanmayan her HTTP bağlantısı sunucu tarafında soket, bellek ve iş parçacığı (thread) tüketir. En tehlikeli kaynak tüketim yoludur.
- "Sunucuyu büyütürsek fırtına biter:" Yanlış. Kapasite iki katına çıkarsa, kontrolsüz istemcilerin oluşturacağı istek fırtınası da iki katına çıkar. İstek freni konulmadığı sürece hiçbir donanım kurtaramaz.
Bu mekanizmaları test etmek ve dirençli sistem tasarımlarını incelemek için AI Playground simülasyonları gerçekçi senaryolar sunar.
Gerçek Saha Hikayesi: 300 Milisaniyelik Ağ Titremesi Bankayı Nasıl Kapattı?
Büyük ölçekli bir ödeme altyapısında çekirdek ağ anahtarlarından biri 300 milisaniyelik mikro donma yaşadı. Basit bir paket gecikmesiydi.
Mikroservisler arası çalışan 4.000 işlem aynı anda zaman aşımına uğradı. Servislerin üzerinde "hata alırsan gecikmesiz 3 kez dene" kuralı gömülüydü. 4.000 istek anında 12.000 yeni isteğe fırladı. Yanıt gelmedikçe sayı katlandı.
Veritabanı bağlantı havuzu kilitlendi. Sağlık kontrolü (health check) paketleri kuyrukta kaldığı için sunucular "ölü" işaretlendi. Otomasyon sistemi pod'ları öldürüp yeniden başlattı. Yeni ayağa kalkan sunucu, kapıda birikmiş 150 bin isteğin darbesiyle açıldığı saniye tekrar çöktü.
Müdahale sert oldu: Dış ağ trafiği sınır yönlendiriciden tamamen kesildi. Kuyruklar boşaltıldı. Veritabanı nefes aldı. İstemci kütüphanelerine üstel geri çekilme ve jitter parametreleri tanımlanarak sistem kademeli açıldı. 300 milisaniyelik aksaklık 4 saatlik tam duruşa mal oldu.
Sıkça Sorulan Sorular (SSS)
Kullanıcı yenileme (F5) tuşuna arka arkaya basınca sistemde ne olur?
Kullanıcı sayfayı art arda yenilediğinde tarayıcı önceki isteği iptal eder ancak sunucu bu iptali anında algılayamaz. Eski istek veritabanını ve işlemciyi meşgul etmeye devam ederken kuyruğa yeni istekler eklenir. Tek bir kullanıcı saniyeler içinde onlarca ağır iş başlatmış olur. Benzer davranışı binlerce kişi aynı anda gösterdiğinde sunucu yanıt üretemez hale gelir ve kuyruk kilitlenir.
Jitter (rastgele gecikme) tam olarak ne işe yarar?
Jitter, üstel bekleme süresine eklenen ufak bir rastgelelik payıdır. Eğer bin istemci aynı anda ağ hatası alırsa ve hepsi "tam iki saniye bekle" kuralını uygularsa, iki saniye sonra aynı anda tekrar saldırırlar. Bu senkronizasyon dalgası sunucuyu periyodik darbelerle yıpratır. Jitter, bekleme sürelerini hafifçe dağıtır; böylece istekler tek bir vuruş yerine zamana yayılmış dengeli bir akışa dönüşür.
Devre kesici (Circuit Breaker) sistemi tam olarak ne zaman açılır?
Devre kesici, önceden belirlenen hata oranı eşiği aşıldığında devreye girer. Örneğin son on saniyedeki isteklerin yüzde ellisi zaman aşımına uğradıysa mekanizma "Açık" duruma geçer. Bu aşamada gelen hiçbir istek ana sunucuya iletilmez, istemciye anında "servis meşgul" yanıtı verilir. Belirli bir dinlenme süresi ardından sistem "Yarı Açık" moda geçerek durumu test eder; başarı sağlanırsa kapılar yeniden açılır.
Retry storm ile önbellek fırtınası (Cache Stampede) arasındaki fark nedir?
İki kriz farklı katmanlarda başlar. Retry storm doğrudan ağ ve istemci kaynaklıdır; başarısız olan isteklerin istemci tarafındaki algoritmalarla kontrolsüzce çoğaltılması sonucu oluşur. Önbellek fırtınasında ise sorun veri katmanındadır. Sık kullanılan bir verinin önbellek süresi (TTL) dolduğu an, binlerce istek aynı anda veriyi yeniden üretmek için veritabanına yüklenir. Sonuç benzer olsa da çözüldükleri mimari noktalar tamamen farklıdır.
Bir backend servisi kendini yeniden deneme fırtınasından nasıl korur?
İlk savunma hattı hız sınırlama (rate limiting) ve istek elemedir (load shedding). Sunucu kuyruğu dolmaya başladığında yeni istekleri bekletmek yerine HTTP 429 veya 503 kodlarıyla anında reddeder. Yanıt başlıklarına "Retry-After" bilgisi koyarak istemciye ne zaman dönmesi gerektiğini bildirir. İstemciler devre kesici desenine zorlanır ve başarısız sağlık kontrollerinde sunucuların anında öldürülmesi engellenerek yumuşak geçiş sağlanır.