İçeriğe atla

2025-09-04

Dead Letter Queue Stratejileri: Dayanıklı Olay-Güdümlü Sistemler için Production-Ready Kalıplar

DLQ stratejileri, monitoring ve recovery kalıpları için kapsamlı rehber. Circuit breaker, exponential backoff, ML-tabanlı recovery ve kaçınılması gereken anti-pattern'lar hakkında gerçek production deneyimleri.

Dead Letter Queue, bir tüketicinin retry bütçesini tükettikten sonra işleyemediği mesajları tutan kuyruktur. DLQ olmadan bir poison pill ya birincil kuyruğu baştan (head-of-line) tıkar ya da başarısız handler ile birlikte sessizce kaybolur; her iki sonuçta da hem event hem de bir şeylerin yanlış gittiğine dair operasyonel sinyal kaybedilir. DLQ, “işlenecek mesajlar” ile “insan ya da tool müdahalesi gerektiren mesajlar” arasındaki görev ayrımıdır ve yalnızca etrafındaki retry politikası, uyarı ve replay araçları birlikte tasarlandığında işe yarar.

Bu yazı, SQS, SNS ve EventBridge üzerinde production event-driven sistemleri için DLQ stratejilerini ele alır. Retry politikası sözleşmesini, DLQ uyarı ve replay mekanizmalarını, poison-pill desenlerini ve başarısız mesajlara erişim sağlamanın maliyet/görünürlük dengelerini kapsar.

DLQ Nedir ve Neden İhtiyacınız Var

DLQ, başarıyla işlenemeyen mesajlar için güvenlik ağınızdır. Doğru DLQ handling olmadan, başarısız mesajlar:

  1. Sonsuza kadar kaybolur (sessiz hatalar)
  2. Tüm kuyruğu bloke eder (poison pill problemi)
  3. Sonsuz retry döngüleri oluşturur (cascade hatalar)

DLQ’yu sisteminizin “acil servisi” olarak düşünün - hasta mesajların teşhis ve tedavi için gittiği yer.

DLQ Implementation Pattern’ları

Pattern 1: Jitter ile Exponential Backoff

En yaygın pattern, ama çoğu implementasyon yanlış yapıyor:

class DayanıklıMesajİşlemcisi {
  async backoffIleİşle(mesaj: Message, maxRetry = 5) {
    let retryCount = 0;
    let sonHata;

    while (retryCount < maxRetry) {
      try {
        return await this.işle(mesaj);
      } catch (hata) {
        sonHata = hata;
        retryCount++;

        // Thundering herd'i önlemek için jitter ekle
        const temelGecikme = Math.pow(2, retryCount - 1) * 1000;
        const jitter = Math.random() * 1000;
        const gecikme = temelGecikme + jitter;

        await this.bekle(gecikme);

        // Retry bağlamıyla mesajı zenginleştir
        mesaj.metadata = {
          ...mesaj.metadata,
          retryCount,
          sonHata: hata.message,
          retryZamani: new Date().toISOString(),
          backoffGecikme: gecikme
        };
      }
    }

    // Max retry aşıldı - tam bağlamla DLQ'ya gönder
    await this.dlqyaGonder(mesaj, sonHata, retryCount);
  }
}

Pattern 2: Circuit Breaker DLQ

Downstream servis hataları için:

class CircuitBreakerDLQ {
  private hatalar = new Map<string, { sayı: number, sonHata: Date }>();
  private devreState: 'KAPALI' | 'AÇIK' | 'YARIM_AÇIK' = 'KAPALI';

  async mesajIşle(mesaj: Message) {
    const servisKey = this.servisKeyiÇıkar(mesaj);

    if (this.devreAçıkMı(servisKey)) {
      // Deneme bile yapma - direkt DLQ'ya circuit breaker sebebiyle
      return this.dlqyaGonder(mesaj, new Error('Circuit breaker açık'), {
        devreState: this.devreState,
        hataCount: this.hatalar.get(servisKey)?.sayı || 0
      });
    }

    try {
      const sonuç = await this.timeoutIleİşle(mesaj, 30000);
      this.başarıKaydet(servisKey);
      return sonuç;
    } catch (hata) {
      this.hataKaydet(servisKey);

      if (this.devreAçılmalıMı(servisKey)) {
        this.devreAç(servisKey);
      }

      throw hata; // Normal retry mantığının halletmesine bırak
    }
  }
}

Pattern 3: Content-Based DLQ Routing

Farklı mesaj tipleri farklı DLQ stratejileri gerektirir. Payment, notification, analytics için farklı maxRetries ve alertLevel. Mesaj tipi ve hata türüne göre payment-dlq-critical, retry-dlq veya investigation-dlq’ya yönlendir.

DLQ Monitoring: Temel Metriklerden Öte

Çoğu team sadece DLQ derinliğini monitor eder. İzlemeniz gerekenler:

class DLQMonitoring {
  private metrikler = {
    // Temel metrikler
    dlqDerinlik: new Gauge('dlq_depth'),
    dlqOran: new Counter('dlq_messages_total'),

    // Gelişmiş metrikler
    dlqMesajYaş: new Histogram('dlq_message_age_seconds'),
    hataKalıpları: new Counter('dlq_error_patterns', ['error_type', 'message_type']),
    retryBaşarıOranı: new Gauge('dlq_retry_success_rate'),

    // Business metrikler
    gelirEtkisi: new Gauge('dlq_revenue_impact_dollars'),
    müşteriEtkisi: new Counter('dlq_customer_impact', ['severity'])
  };
}

DLQ Recovery Stratejileri

Strateji 1: ML ile Otomatik Recovery

class MLDLQRecovery {
  async analizeEtveKurtar() {
    const dlqMesajları = await this.dlqMesajlarınıGetir();

    // Hata kalıplarına göre grupla
    const hataGrupları = this.hataKalıplarınaGöreGrupla(dlqMesajları);

    for (const [kalıp, mesajlar] of hataGrupları.entries()) {
      // Bilinen bir düzeltmemiz var mı kontrol et
      const düzeltme = await this.mlModel.düzeltmeTahminEt(kalıp);

      if (düzeltme.confidence > 0.8) {
        await this.otomatikDüzeltmeUygula(mesajlar, düzeltme);
      } else {
        await this.jiraTicketOluştur(kalıp, mesajlar, düzeltme);
      }
    }
  }
}

Strateji 2: Progressive Recovery

Batch halinde recovery. Exponential delay between batches. Failure rate > 0.5 ise alert ve 1 dakika bekle. Thundering herd önle.

Cloud Provider DLQ Özellikleri

AWS SQS DLQ

RedrivePolicy ile deadLetterTargetArn, maxReceiveCount. MessageRetentionPeriod 14 gün. CloudWatch alarm ApproximateNumberOfMessagesVisible.

Azure Service Bus DLQ

MaxDeliveryCount sonrası otomatik DLQ. SubQueue.DeadLetter ile recovery.

GCP Pub/Sub DLQ

dead_letter_policy, max_delivery_attempts, retry_policy minimum/maximum_backoff.

DLQ Kaçınılacak Anti-Pattern’lar

Infinite retry, DLQ’yu unutmak, tek DLQ tüm mesaj tipleri için, monitoring eksikliği, manuel recovery olmadan.

Production DLQ Checklist

  • Uygun retention periyodları yapılandır (minimum 14 gün)
  • DLQ derinlik alarmları kur (> 10 mesaj)
  • DLQ yaş metriklerini monitor et (1 saatten eski mesajlar)
  • Bilinen hata kalıpları için otomatik recovery uygula
  • Manuel araştırma için runbook’lar oluştur
  • DLQ mesajlarından business impact metriklerini takip et
  • Team standuplarında düzenli DLQ review’ları
  • Yüksek hata oranları sırasında DLQ davranışını load test et

Gerçek Dünya DLQ Savaş Hikayeleri

Kritik Payment DLQ Olayı

DLQ’muz monitor edilmediği için payment’lar sessizce başarısız oluyordu. Mesajlar DLQ’ya gidiyordu ama hiç alarm kurulmamıştı. 50.000lık payment’ların DLQ’da sıkıştığını fark etmemiz 3 gün aldı.

Öğrenilen ders: Sadece ana kuyruk metriklerini değil, her zaman DLQ derinlik ve yaşını monitor et.

Thundering Herd DLQ Sorunu

Downstream servis kesintisi sırasında, jitter olmadığı için tüm retry girişimlerimiz eş zamanlı gerçekleşti. Bu durum, toparlanmaya çalışan servisi aşırı yükleyen bir thundering herd yarattı.

Öğrenilen ders: Retry girişimlerini yaymak için exponential backoff’a her zaman jitter ekle.

Black Friday’i Bozan Poison Pill

Hatalı biçimlendirilmiş bir mesaj sürekli yeniden işlenip order servisimizi çökertiyordu. Doğru DLQ handling olmadan, en yüksek trafiğimizin olduğu gün sonraki tüm siparişleri bloke etti.

Öğrenilen ders: Circuit breaker’lar ve farklı hata tipleri için ayrı DLQ’lar uygula.

Sonuç

İyi tasarlanmış bir DLQ stratejisi çoğu zaman küçük bir olay ile büyük bir kesinti arasındaki fark olur. Odaklan:

  1. Temel derinlik metriklerinin ötesinde kapsamlı monitoring
  2. Mesaj tipi ve hata kalıplarına dayalı akıllı routing
  3. Bilinen sorunlar için otomatik recovery
  4. Manuel müdahale için net runbook’lar
  5. Kalıpları geliştirmek için düzenli review’lar

Unutma: DLQ’n production güvenlik ağın. Ana işleme mantığına verdiğin özenin aynısını ona da ver.


İlgili Okuma: Olay-güdümlü sistem araçları ve kalıplarının daha geniş bir genel bakışı için olay-güdümlü mimari araçları kapsamlı rehberini görün.

İlgili yazılar

Olay Güdümlü Mimari Araçları: Kafka, SQS, EventBridge ve Cloud Alternatifleri Üzerine Kapsamlı Rehber

Olay güdümlü sistem araçları, mesaj teslimat kalıpları, DLQ stratejileri ve cloud provider eşlenikleri üzerine derinlemesine inceleme. AWS, Azure, GCP ve edge deployment'lar hakkında production deneyimleri.

architectureazuredlq+7
Transactional Outbox Pattern: Dağıtık Sistemlerde Güvenilir Event Publishing

Transactional Outbox Pattern'in dağıtık sistemlerdeki dual-write problemini nasıl çözdüğünü, PostgreSQL, DynamoDB ve CDC araçlarıyla pratik implementasyonlarını öğren.

distributed-systemsmicroservicesevent-driven+7
Kafka mı, Event Bus mı? SNS/SQS/EventBridge'i Aşmanız Gerektiğini Söyleyen Sinyaller

Yönetilen bir event bus'tan Kafka'ya geçişi hak eden sinyaller ve rip-and-replace yapmadan taşımak için outbox tabanlı dört aşamalı geçiş planı.

kafkaevent-drivenaws+4
İzole Consumer Hesaplarına Event Fan-Out: Sıfır Dokunuşlu Producer, Domain Başına Sahiplik

Çok takımlı AWS organizasyonları için platform mühendisliği varsayılanı: tek event, birçok consumer, her biri kendi hesabında kendi SQS ve DLQ'suyla; fan-out event bus katmanında yaşar.

awseventbridgeevent-driven+5
wasmCloud + NATS: Kilitlenme Aslında Event Bus Topolojisinde Yaşar

Bir keşif tezi: event-driven sistemlerde vendor lock-in runtime katmanında değil, bus topolojisinde yaşar; wasmCloud ve NATS ise bus'ı taşınabilir bir primitif haline getiriyor.

wasmcloudnatsevent-driven+4