Kargo servisi çöktü, sipariş alamıyoruz: dış servis kesintisine dayanıklı uygulama nasıl yazılır?
Kargo entegrasyonu yanıt vermiyor, sipariş ekranı dönüp duruyor, müşteri hizmetlerine "sitede sipariş veremiyorum" aramaları geliyor. Sizin sunucularınızda bir arıza yok. Yine de bu kesinti sizin kesintiniz olarak yaşanıyor, çünkü akışın ortasında duran bir dış servis, kodunuz izin verdiği sürece bütün uygulamayı bekletir. Dış servislerin çökmesini engelleyemezsiniz. Engelleyebileceğiniz şey, onlar çöktüğünde ürününüzün de durup durmayacağı ve bu üç karara bağlı: her dış çağrının bir zaman aşımı olacak mı, hata birikince istek göndermeyi bırakacak mısınız, o servis yokken kullanıcıya ne göstereceksiniz.
Bu üç karar çoğu projede entegrasyon yazılırken değil, ilk büyük kesintiden sonra alınır. Geçen yılın iki olayı, beklemenin neden pahalı olduğunu iyi anlatıyor.
Kesinti nadir değil, 2025 iki kez hatırlattı
19 Ekim 2025 gecesi 23:48'de (PDT) AWS'nin us-east-1 bölgesinde DynamoDB'nin genel uç noktasına ait DNS kayıtları boşaldı. Sebep bir saldırı ya da donanım arızası değildi: DNS kayıtlarını otomatik güncelleyen iki bileşen arasındaki gizli bir yarış durumu, biri eski bir planı uygularken diğerinin aynı planı silmesine yol açtı ve uç noktanın bütün IP adresleri kayboldu. AWS'nin kendi olay özetine göre DNS sorunu 20 Ekim 02:40'ta çözüldü, ama hikaye orada bitmedi. Yeni EC2 örneği başlatmak 13:50'ye, Lambda çağrıları 14:15'e kadar aksadı; tüm sistemlerin normale dönmesi 14:20'yi buldu. Asıl arıza yaklaşık üç saat, kuyruğu on beş saat sürdü.
18 Kasım 2025'te sıra Cloudflare'e geldi. 11:05 UTC'de yapılan bir veritabanı izin değişikliği, bot yönetimi sisteminin okuduğu özellik dosyasını üreten sorgunun mükerrer satır döndürmesine sebep oldu. Dosya iki katına çıktı, 200 özellik için önceden bellek ayıran modül bu dosyayı işleyemedi ve çöktü. Cloudflare arkasındaki siteler 11:20'den itibaren HTTP 5xx almaya başladı, çekirdek trafik 14:30'da düzeldi, her şeyin normale dönmesi 17:06'yı buldu. Olayın en öğretici ayrıntısı ise şu: Cloudflare'in tamamen kendi altyapısı dışında barındırılan durum sayfası da aynı anda erişilemez oldu ve ekip bir süre saldırı altında olduğunu düşündü.
İki olayın ortak dersi "daha büyük sağlayıcı seçin" değil, ikisi de zaten en büyükleriydi. Ders şu: ürününüzün çalışması kontrol etmediğiniz bir sistemin o anki durumuna bağlıysa, o bağın nerede ve nasıl kopacağını önceden siz tarif etmezseniz, kopma şeklini kesinti günü öğrenirsiniz.
Önce envanter: hangi servis hangi akışı durduruyor
Dayanıklılık çalışması kodla değil, tek sayfalık bir listeyle başlar. Ürününüzün konuştuğu bütün dış servisleri yazın: sanal POS ve ödeme kuruluşu, kargo firmaları, e-fatura entegratörü, SMS ve OTP sağlayıcısı, e-posta servisi, adres doğrulama, kimlik sağlayıcısı (SSO), ERP, CDN, DNS, obje depolama, arama servisi, analitik ve sitedeki sohbet widget'ı.
Her satır için üç soruyu cevaplayın. Bu servis şu anda cevap vermese hangi kullanıcı akışı durur? O akış bir saat durursa şirkete maliyeti nedir? Servis yokken kabul edilebilir bir "yarım çalışma" var mı?
Cevaplar genelde üç kovaya düşüyor. Para ya da giriş akışını durduranlar (ödeme, kimlik doğrulama, OTP) birinci öncelik. Akışı yavaşlatan ama tamamen kesmeyenler (kargo fiyatı, adres doğrulama, e-fatura) ikinci. Kimsenin fark etmeyeceği olanlar (analitik, öneri motoru, sohbet widget'ı) üçüncü, ama bu üçüncü grup senkron çağrıldığında birinci grup gibi davranır ki en sinir bozucu kesintiler buradan çıkar.
Bu tablo yoksa kesinti anında tartışma "kapatalım mı, bekleyelim mi" üzerinden yürür ve karar telaşla verilir. Tablo varsa karar zaten alınmıştır.
Zaman aşımı koymadıysanız, onların kesintisi sizin kesintinizdir
Dış çağrıya zaman aşımı koymamak, yaygın olduğu kadar sessiz bir hata. Kütüphanelerin varsayılanları da yardımcı olmuyor: Python'da requests varsayılan olarak zaman aşımı uygulamaz, yani sunucu cevap vermezse süresiz bekler. Go'da http.Client yapısının sıfır değeri "zaman aşımı yok" demektir ve http.Get çağrısı bu varsayılanı kullanır. Node tarafında fetch ve altındaki undici, başlık ve gövde için 300 saniyelik sınırlarla gelir; bir web isteği için beş dakika pratikte sonsuzdur.
Sonuç şu olur: karşı taraf yavaşlar, sizin süreçleriniz o çağrıların üzerinde birikir, bağlantı havuzu ve iş parçacıkları dolar, sonra dış servisle hiç ilgisi olmayan sayfalar da açılmaz. Kullanıcı "site çökmüş" der ve haklıdır.
Süreyi seçerken yuvarlak sayı atmak yerine ölçülen gecikmeye bakın. Amazon'un mühendislik kütüphanesinde anlatılan pratik açık: önce kabul edilebilir bir yanlış zaman aşımı oranı belirleyin (örneğin binde bir), sonra aşağı akış servisinin buna karşılık gelen gecikme persentilini (bu örnekte p99.9) zaman aşımı olarak alın. İnternet üzerinden konuşan servislerde ağ gecikmesi için pay eklemek gerekir. Çok düşük bir değer de zararlıdır: p99 gecikmesi 400 ms'ye çıkmış bir servise 200 ms sınır koyarsanız, biten işleri iptal edip yenilerini başlatır, kimsenin beklemediği işe kapasite yakarsınız.
İki ayrı süreyi ayrı ayrı belirleyin: bağlantı kurma ve yanıt bekleme. Bir de isteğin toplam bütçesini düşünün. Sayfa üç saniyede cevap vermek zorundaysa tek bir bağımlılığa on saniye veremezsiniz.
Yavaş bağımlılık, çöken bağımlılıktan daha tehlikelidir
Tamamen çöken servis aslında kolay vakadır: bağlantı anında reddedilir, hata hemen döner, siz de yedek planı çalıştırırsınız. Asıl zor olan yarı çalışan servistir. İsteklerin %5'i hata veriyor, kalanı normalde 200 ms sürerken 9 saniyede dönüyor, sağlık kontrolü uç noktası ise hâlâ 200 OK diyor. Panolar yeşil, ürün bozuk.
Bu senaryonun klasik ilacı bölmeli tasarım. Azure mimari kılavuzunun "bulkhead" adıyla anlattığı yaklaşım, gemilerin su geçirmez bölmelerinden geliyor: her bağımlılığa ayrı bağlantı havuzu ve ayrı eşzamanlılık sınırı verirsiniz, böylece kargo API'sine giden yavaş çağrılar giriş ekranını besleyen havuzu tüketemez. Bir bağımlılık için ayırdığınız 20 bağlantı dolduğunda yeni çağrılar beklemeden hata alır ve akış, sizin belirlediğiniz yedek yola girer.
İkinci ilaç, işi istek yolundan çıkarmak. Kullanıcının cevabı beklemek zorunda olmadığı her çağrı (fatura kesme, bildirim gönderme, ERP'ye sipariş yazma) arka plana taşınmalı. Bunun mekaniğini arka plan işleri ve kuyruk mimarisi yazısında ayrıntılı anlatmıştık.
Yeniden deneme yardımcı olur, bütçesi yoksa saldırıya dönüşür
Geçici hatalarda yeniden denemek doğrudur, ama düzenlenmemiş yeniden deneme zaten zorlanan bir servisi yere serer. Google'ın SRE kitabındaki iki sınır iyi bir başlangıç: bir istek için en fazla üç deneme, bir istemci için ise yeniden denemelerin toplam isteklere oranının %10'u geçmemesi. Aynı kitaptaki hesap da katmanlı yeniden denemenin nasıl büyüdüğünü gösteriyor: her katman 3 kez yeniden deniyorsa (yani 4 deneme), üç katman üst üste geldiğinde tek bir kullanıcı eylemi veritabanına 64 istek olarak iner.
Pratik kurallar kısa. Denemeler arasına üstel artan bir bekleme ve rastgele sapma (jitter) koyun, yoksa bütün istemciler aynı anda geri döner ve ikinci dalga ilkinden beter olur. 4xx hatalarını yeniden denemeyin, çünkü isteğiniz yanlış, servis değil. Karşı taraf Retry-After gönderiyorsa ona uyun. Yeniden denenen her yazma işlemi idempotent olmalı ya da bir idempotency anahtarı taşımalı; bu olmadan "cevap gelmedi, tekrar deneyelim" refleksi iki kez çekilen para ya da iki kez kesilen fatura demektir. Ödeme tarafındaki detayları ödeme entegrasyonu, webhook ve mutabakat yazısında toplamıştık.
Devre kesici: ısrar etmeyi bırakan kod
Devre kesici, hata oranı eşiği aştığında bir bağımlılığa istek göndermeyi geçici olarak durduran küçük bir durum makinesi. Azure mimari kılavuzunun tarif ettiği üç durum var: kapalı durumda istekler normal geçer, hatalar belirlenen süre içinde eşiği aşınca devre açılır ve çağrılar karşı tarafa hiç gitmeden anında hata döner, sayaç dolunca devre yarı açık duruma geçip sınırlı sayıda deneme isteği gönderir. Bu denemeler başarılıysa devre kapanır, değilse yeniden açılır.
İki faydası var. Birincisi sizi korur: kullanıcı 30 saniye bekleyip hata alacağına 50 milisaniyede yedek yola düşer. İkincisi karşı tarafı korur, çünkü toparlanmaya çalışan bir servisin en istemediği şey, kesinti biter bitmez üzerine yığılan birikmiş istek dalgasıdır.
Çöktüğünde kullanıcı ne görecek?
Asıl tasarım kararı burada. Her bağımlılık için "yok" halinin karşılığını önceden yazın.
- Okuma tarafında eskisini gösterin. Kargo fiyatı, döviz kuru, ürün listesi gibi verilerde son başarılı cevabı önbellekte tutup servis yokken onu sunabilirsiniz. HTTP tarafında bunun adı
stale-if-errorvestale-while-revalidatedirektifleri; ayrıntısı önbellek stratejisi yazısında. - Yazma tarafında kabul edip kuyruğa alın. Siparişi alın, e-faturayı sonra kesin, ERP'ye aktarımı servis geri geldiğinde yapın. Kullanıcıya "siparişiniz alındı, fatura e-postayla gelecek" demek, "bir hata oluştu" demekten iyidir.
- Sabit bir yedek değer tanımlayın. Kargo fiyatı hesaplanamıyorsa marjı koruyan sabit bir ücretle devam etmek, sepeti kaybetmekten ucuz olabilir. Bu bir ürün kararıdır, geliştirici kararı değil.
- Giriş akışına ikinci bir yol bırakın. SMS sağlayıcısı çöktüğünde kimse giremiyorsa kesinti toplam demektir. İkinci bir SMS sağlayıcısı ya da uygulama tabanlı kod gibi alternatifleri çok faktörlü kimlik doğrulama yöntemleri yazısında karşılaştırmıştık.
- Özelliği kapatabilir olun. Bağımlılık başına bir kapatma anahtarı, kesinti anında dağıtım beklemeden özelliği devre dışı bırakmanızı sağlar. Bunun altyapısı özellik bayrakları ve kademeli yayın yazısında anlatılıyor.
Hiçbiri mümkün değilse en azından dürüst olun. Ne olduğunu söyleyen, ne zaman tekrar denemesi gerektiğini yazan bir ekran, sonsuza kadar dönen bir yükleniciden hem daha az destek talebi hem daha az terk edilen sepet üretir.
Tarayıcıdaki üçüncü taraf betikler de bir bağımlılıktır
Dayanıklılık konuşmaları genelde sunucu tarafında kalıyor, oysa kullanıcının gördüğü sayfa da dışarıdan yüklenen dosyalara bağlı. Etiket yöneticisi, sohbet widget'ı, analitik, harita, yazı tipi, A/B test aracı. Bunlardan biri yavaşladığında sayfanız da yavaşlar; senkron yüklenen bir betik ise sayfanın çizilmesini tümden bloke eder.
Kritik olmayan her şeyi async ya da defer ile yükleyin, yazı tiplerini kendi alan adınızdan sunun, üçüncü taraf betiğin yüklenememesi durumunda arayüzün çalışmaya devam ettiğini test edin. Bu betiklerin ölçülen etkisi ve hız tarafındaki karşılığı için Core Web Vitals ve site hızı yazısına bakabilirsiniz.
İkinci sağlayıcı her yerde mantıklı değil
"Yedek sağlayıcı ekleyelim" cümlesi kulağa basit gelir, faturası ise iki entegrasyon, iki mutabakat süreci, iki sözleşme ve iki kez test demektir. Üstelik hiç kullanılmayan yedek yol, kullanılması gereken gün çalışmaz. Kimsenin çalıştırmadığı kod, çalıştığı bilinmeyen koddur.
Ölçüt basit: o akışın bir saatlik kesintisi, ikinci entegrasyonun yıllık maliyetinden pahalıysa yedek sağlayıcı mantıklıdır. SMS, e-posta ve ödeme tarafında bu hesap çoğu zaman tutar. Harita, adres doğrulama, öneri motoru gibi yerlerde genelde tutmaz, orada düşerek çalışma yeterlidir. Yedek yolu kurduysanız da düzenli olarak gerçek trafiğin küçük bir yüzdesini oradan geçirin; ayda bir çalışan yol, kesinti günü çalışan yoldur.
Kendi tarafınızdan ölçmüyorsanız haberi müşteriden alırsınız
Bağımlılık başına üç sayıyı izleyin: hata oranı, gecikme persentilleri ve devre kesicinin durumu. Bunlar uygulama loglarınızda değil, panonuzda ve alarmlarınızda olmalı. Sağlayıcının durum sayfası ilk kaynağınız olmasın; hem geç güncellenir hem de Cloudflare örneğinde görüldüğü gibi aynı anda erişilemez olabilir.
Bir de dışarıdan bakan sentetik kontrol kurun: kritik akışı (giriş, sepete ekleme, ödeme adımına kadar) dakikada bir çalıştıran bir kontrol, hem kendi hatanızı hem bağımlılığınızın hatasını müşteriden önce yakalar. Eşiklerin ve alarmların nasıl kurulacağını izlenebilirlik, SLO ve hata bütçesi yazısında anlatmıştık. Kesinti gerçekleştiğinde kimin ne yapacağı ise ayrı bir hazırlık konusu ve olay müdahale planında ilk 24 saat yazısında duruyor.
SLA kredisi zararınızı karşılamaz
Sözleşmedeki %99,9 çalışma süresi taahhüdü, 30 günlük bir ayda yaklaşık 43 dakikalık kesinti hakkı demek. Kulağa az geliyor, ama bağımlılıklar zincirleme çalışır: ödeme akışınız dört ayrı dış servise bağlıysa ve dördü de taahhüdünü harfiyen tutuyorsa, beklenen toplam kesinti ayda yaklaşık üç saate çıkar. Hiç kimse sözü bozmadan.
SLA ihlalinde alacağınız kredi de genellikle o ay o servise ödediğiniz ücretin bir yüzdesidir, kaybedilen siparişin değil. Sözleşmede peşine düşülmesi gereken şey kredinin oranı değil, olay bildirimi ve iletişim taahhüdü: kesinti kaç dakika içinde bildirilecek, kök neden raporu ne zaman gelecek, planlı bakım kaç gün önce duyurulacak. Tedarikçi sözleşmelerinde hangi maddelerin pazarlıktan çıkması gerektiğini yazılım bakım ve destek sözleşmesi yazısında derlemiştik.
Tatbikat yapılmamış plan, plan değildir
Yukarıdaki kararların hepsi kod ve yapılandırma olarak var olabilir, yine de kesinti günü çalışmayabilir. Tek doğrulama yolu denemek.
Test ortamında bağımlılığın alan adını engelleyin ve uygulamanın ne yaptığını izleyin. Sonra daha zor senaryoyu kurun: servisi kapatmak yerine yanıtlarına beş saniye gecikme ekleyin, isteklerin %20'sine 500 döndürün. Yük testlerinize dış servisin yavaşladığı bir senaryo ekleyin; yöntemi yük testi ve kapasite planlaması yazısında anlatmıştık. Kapatma anahtarlarını üç ayda bir gerçekten çevirin, çünkü denenmemiş anahtar da denenmemiş yedek yol kadar güvenilmezdir.
Bu hafta atabileceğiniz adımlar
- Dış bağımlılıkların listesini çıkarın, her birinin hangi akışı durdurduğunu ve saatlik maliyetini yazın.
- Zaman aşımı olmayan çağrıları arayın. Kod tabanında dış çağrı yapan yerleri tarayıp varsayılana bırakılmış olanları bulmak genelde yarım gün sürer.
- Birinci gruptaki en kritik bağımlılık için devre kesici ve düşerek çalışma senaryosunu tanımlayın, ürün tarafıyla birlikte karara bağlayın.
- Test ortamında o bağımlılığı kapatıp sonucu izleyin. Gördüğünüz şey beklediğiniz şey değilse, asıl iş listeniz o farktan çıkar.
Wedevit olarak bu çalışmayı bağımlılık envanteriyle başlatıp, zaman aşımı ve yeniden deneme politikalarını, devre kesicileri ve düşerek çalışma senaryolarını ürünün içine yerleştiriyor, ardından test ortamında arıza enjeksiyonuyla doğruluyoruz. Entegrasyon uçlarınızı aynı çalışma içinde API güvenliği kontrolleriyle birlikte gözden geçiriyoruz. Tüm süreç uzaktan yürütülür.
Bu konuda yardıma mı ihtiyacınız var?