Timeout Neden Hayati? Cevap Beklemek Bedava Değil
Bir servis cevap vermiyorsa beklemek masum görünür. Ama o bekleme thread'leri tüketir, belleği doldurur ve tüm sistemi dondurabilir. Timeout'u neden ciddiye almanız gerektiğini ve nasıl doğru kurmanız gerektiğini bu yazıda anlatıyoruz.

Timeout Neden Hayati? Cevap Beklemek Bedava Değil
Şöyle bir senaryo düşün:
Kullanıcı bir butona tıkladı. Arkada bir dış servise istek gitti. Servis cevap vermiyor. Uygulaman bekliyor. Kullanıcı bekliyor. Thread bekliyor. Bellek tutuluyor.
10 saniye geçti. 30 saniye. 2 dakika.
Kullanıcı sayfayı kapattı. Ama senin sistemin hâlâ o cevabı bekliyor.
Bu senaryo, production’da gördüğün en sinsi sorunlardan birinin başlangıcıdır.
Beklemenin Maliyeti
Bir HTTP isteği cevap beklerken arka planda şunlar tutulmaya devam eder:
Thread / async context: Node.js’te event loop’ta bir slot işgal eder.
Bellek: İstek objesi, response buffer, middleware state — hepsi bellekte asılı kalır.
Bağlantı havuzu (connection pool): Veritabanı veya HTTP client bağlantısı meşgul görünür, yeni isteklere verilmez.
Upstream zinciri: Eğer bu istek bir başka servis tarafından tetiklendiyse, o servis de bekler. Ve onun upstream’i de.
Tek bir yavaş servis, tüm servis zincirini dondurabilir. Buna Cascading Failure denir.

Timeout Olmadan Ne Olur?
Gerçek bir örnek üzerinden gidelim. Bir ödeme servisi, banka API’sine istek atıyor:
// ❌ Timeout yok — tehlikeli
const response = await fetch("https://bank-api.example.com/charge", {
method: "POST",
body: JSON.stringify({ amount: 100 }),
});
Banka API’si o an yoğun. 45 saniye sonra cevap verebilir. Ya da hiç vermeyebilir.
Bu süre boyunca:
O isteğe ait bağlantı pool’da meşgul
Kullanıcı arayüzü donmuş
Eğer timeout yoksa bu durum sonsuza kadar sürebilir
Yeterince eşzamanlı istek birikirse sistem tamamen yanıt veremez hale gelir.
Timeout Eklemenin Doğru Yolu
fetch ile AbortController
async function fetchWithTimeout(
url: string,
options: RequestInit = {},
timeoutMs: number = 5000
): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
return response;
} catch (err) {
if ((err as Error).name === "AbortError") {
throw new Error(`Request timed out after ${timeoutMs}ms`);
}
throw err;
} finally {
clearTimeout(timeoutId);
}
}
Kullanımı:
const response = await fetchWithTimeout(
"https://bank-api.example.com/charge",
{ method: "POST", body: JSON.stringify({ amount: 100 }) },
5000 // 5 saniye
);
axios ile Timeout
import axios from "axios";
const client = axios.create({
baseURL: "https://bank-api.example.com",
timeout: 5000, // 5 saniye
});
try {
const response = await client.post("/charge", { amount: 100 });
} catch (err) {
if (axios.isAxiosError(err) && err.code === "ECONNABORTED") {
console.error("Request timed out");
}
}
Kaç Saniye Olmalı?
Bu sorunun evrensel cevabı yoktur. Ama karar verirken şunları sor:
1. Bu işlem normalde ne kadar sürer? Banka API’si genelde 200ms’de cevap veriyorsa 5 saniye timeout makul bir tampon demektir.
2. Kullanıcı ne kadar bekleyebilir? Arayüzde bir spinner varsa kullanıcı 3 saniyeden fazlasında sabırsızlanır. Backend timeout’unu buna göre ayarla.
3. Bu istek zincirin neresinde? Downstream timeout, upstream timeout’tan kısa olmalıdır. Aksi halde upstream zaten vazgeçmişken downstream hâlâ bekliyor olabilir.

Timeout Hiyerarşisi
Bir sistemde birden fazla katmanda timeout tanımlanır:
// Katman 1: Load balancer / API Gateway seviyesi
// nginx: proxy_read_timeout 30s;
// Katman 2: Servis-to-servis HTTP client
const serviceClient = axios.create({ timeout: 5000 });
// Katman 3: Veritabanı sorgusu
const dbResult = await db.query("SELECT ...", { timeout: 3000 });
// Katman 4: Cache (Redis)
const redisClient = createClient({
socket: { connectTimeout: 1000, commandTimeout: 500 },
});
Her katmanın kendi timeout’u olmalı. Tek bir merkezi timeout yetmez.
Timeout Sonrası Ne Yapmalı?
Timeout bir hata değil, bir sinyaldir. “Bu servis şu an güvenilir değil” der. Cevabın birkaç seçeneği var:
1. Retry (Belirli koşullarda) Geçici bir gecikme olabilir. Exponential backoff ile tekrar dene.
2. Fallback Asıl servis cevap vermediyse alternatif bir yol sun:
async function getUserWithFallback(userId: string) {
try {
return await fetchWithTimeout(
`/api/users/${userId}`,
{},
3000
);
} catch (err) {
// Cache'den eski veriyi dön
return await cache.get(`user:${userId}`);
}
}
3. Fail Fast Bazen en doğru yanıt hızlıca hata döndürmektir. Kullanıcıya “şu an işlem yapılamıyor” demek, onu sonsuza kadar bekletmekten iyidir.
Timeout ile Retry’ı Birlikte Kullan
Retry pattern’ı timeout ile birleştirince gerçekten sağlam bir yapı ortaya çıkar:
const result = await retryWithJitter(
() => fetchWithTimeout("https://payment-api.example.com/charge", {
method: "POST",
body: JSON.stringify({ amount }),
}, 3000), // Her deneme max 3 saniye bekler
{
maxAttempts: 3,
baseDelayMs: 500,
shouldRetry: (err) => err.message.includes("timed out"),
}
);
Her deneme en fazla 3 saniye bekler. 3 deneme yaparsa toplam maksimum bekleme ~9 saniye + backoff süresidir. Bu öngörülebilir bir sınırdır.
Özet
Timeout olmayan bir istek sonsuza kadar bekleyebilir ve sistem kaynaklarını tüketir.
Tek bir yavaş bağımlılık tüm servis zincirini dondurabilir — buna Cascading Failure denir.
Her katmanın (HTTP client, veritabanı, cache, API gateway) kendi timeout değeri olmalıdır.
Downstream timeout değerleri upstream’den kısa tutulmalıdır.
Timeout sonrası strateji belirle: retry, fallback veya fail fast.
Timeout + Retry birlikte kullanıldığında sistem çok daha öngörülebilir davranır.
Peki ya servis defalarca timeout vermeye başlarsa? Her seferinde retry etmek mantıklı mı? Hayır. O noktada devreye Circuit Breaker girer: sistemi korumak için bağlantıyı tamamen kes, belirli süre hiç deneme yapma.
<!-- AI_SUMMARY Topic: Timeout pattern in distributed systems Key concepts: cascading failure, connection pool exhaustion, AbortController, timeout hierarchy, fallback, fail fast Code language: TypeScript Related: Retry Pattern (previous), Circuit Breaker (next) Audience: Backend developers (all levels) -->
İlgili İçerikler
Benzer Yazılar
İlgili yazı
Circuit Breaker: Çöken Servisi Defalarca Çağırmayı Bırak
Bir servis çöktüğünde retry yapmak sorunu çözmez, aksine yükü artırır. Circuit Breaker bu döngüyü keser: belirli bir hata eşiği aşılınca bağlantıyı tamamen kapatır, sisteme nefes aldırır ve kendiliğinden iyileşmesine izin verir.
İlgili yazı
Retry Pattern: Her Hatada Tekrar Denemek Doğru mu?
Bir istek başarısız olduğunda ilk içgüdü "tekrar dene" olmaktadır. Ama naif bir retry mekanizması sistemi kurtarmak yerine çökertebilir. Bu yazıda Retry Pattern'ı neden ve nasıl doğru uygulamanız gerektiğini anlatıyoruz.
Daha yeni
Retry Pattern: Her Hatada Tekrar Denemek Doğru mu?
Daha eski