E-posta göndermek, PDF üretmek, dosya taramak, arama indeksini güncellemek veya bir dış sağlayıcıya bildirim iletmek gibi işler çoğu zaman HTTP isteğinin içinde yapılmaz. Kullanıcı isteği hızlıca tamamlanır; iş bir kuyruğa bırakılır ve worker daha sonra işler. Bu yapı arayüzü hızlandırır ve yoğunluğu dengeler, fakat beraberinde önemli bir gerçeği getirir: mesajlaşma altyapısı çoğu zaman “iş tam olarak bir kez çalışır” garantisi vermez.
Bir worker işlemi tamamladıktan hemen sonra çökerse onay mesajını gönderemeyebilir. Kuyruk aynı mesajı tekrar teslim eder. Ağ zaman aşımı, işlemin hiç gerçekleşmediğini değil, sonucunun bilinmediğini gösterebilir. Güvenilir tasarımın başlangıç noktası şudur: Bir iş tekrar çalışabilir, sırası değişebilir ve gecikebilir. Uygulama bu koşullarda doğru sonuç üretmelidir.
“En az bir kez” teslimi kabul edin
Birçok kuyruk sistemi en az bir kez teslim davranışına yakındır. Mesaj kaybolmasın diye, alıcıdan açık onay bekler. Onay ulaşmazsa aynı mesaj yeniden gönderilebilir. Bu davranış hata değil, dayanıklılık özelliğidir; fakat işleyicinin iki kez çalışmayı tolere etmesi gerekir.
Örneğin “ödeme alındı” mesajı iki defa işlenirse müşteriye iki e-posta gitmesi can sıkıcı olabilir. Aynı mesajın iki defa bakiye artırması ise çok daha ciddi bir sonuç doğurur. Bu iki örnekte de sorun kuyruğun mesajı iki kez teslim etmesi değil, tüketicinin tekrar işleme karşı güvenli olmamasıdır.
İş tanımını en baştan açık yapmak yararlıdır: Bu işlem tekrar çalışırsa hangi sonuç sabit kalmalı, hangi yan etki yalnızca bir kez oluşmalı ve hangi kayıt işlemin tamamlandığını kanıtlamalı? Bu sorular cevaplanmadan ack çağrısının yeri hakkında sağlıklı karar verilemez.
İdempotency'yi veri katmanında kurun
İdempotent bir işlem aynı girdiyle birden çok kez çağrılsa da hedefte aynı sonucu bırakır. Bunu yalnızca bellekte tutulan bir “işlendi” listesiyle sağlamak güvenli değildir; worker yeniden başlatıldığında liste kaybolur. Kalıcı veri katmanı, kararın kaynağı olmalıdır.
Her mesajın veya iş isteğinin benzersiz bir kimliği bulunmalıdır. Tüketici, bu kimliği işlenmiş işlemler tablosunda veya hedef kaydın üzerinde saklayabilir. Aynı kimlik tekrar geldiğinde sistem yan etkiyi yeniden üretmeden başarılı sonucu döndürür. Veritabanı benzersiz indeksi, iki worker'ın aynı mesajı aynı anda ele alması durumunda önemli bir savunmadır.
İdempotency anahtarının kapsamı da önemlidir. Bir kullanıcının “raporu yeniden üret” düğmesine iki kez basması gerçekten tek iş mi, yoksa iki ayrı rapor talebi mi? Anahtarı iş anlamına göre tasarlamak gerekir. Rastgele bir istek kimliğini her çağrıda değiştirmek, tekrar denemeyi ayırt etmez; aynı anahtarı farklı işlemlerde kullanmak ise meşru işlemleri bastırabilir.
Tekrar denemeyi hata türüne göre yapın
Her hata tekrar denemeye uygun değildir. Geçici ağ kopması, aşırı yükleme, zaman aşımı veya kısa süreli bir dış servis hatası yeniden denenebilir. Geçersiz veri, yetki reddi, bozuk dosya veya iş kuralı ihlali ise tekrar denendikçe düzelmez. Bu ayrımı yapmayan sistemler hem kuyruğu şişirir hem de gerçek hataları gizler.
Geçici hatalarda üstel geri çekilme kullanmak yaygındır: ilk deneme kısa süre sonra, sonraki denemeler artan aralıklarla yapılır. Aralığa rastgelelik eklemek, çok sayıda işin aynı anda tekrar deneyip bağımlı servisi yeniden zorlamasını önler. Maksimum deneme sayısı ve toplam süre için de sınır gerekir; sonsuz tekrar deneme, kalıcı hatayı sessizce tüketen bir döngüdür.
Worker logunda veya iş kaydında deneme sayısı, son hata türü ve bir sonraki deneme zamanı görünmelidir. Böylece destek ekibi “iş neden hâlâ bekliyor?” sorusuna yalnızca genel bir hata metniyle değil, somut durumla yanıt verebilir.
Veritabanı değişikliği ile mesaj yayınını ayırmayın
Sık rastlanan hata, önce veritabanına kayıt yazıp sonra ayrı bir adımda mesaj yayınlamaktır. Uygulama bu iki adım arasında çökerse kayıt oluşur ama olay hiç yayınlanmaz. Tersi durumda mesaj yayımlanır, fakat kayıt geri alınır; tüketiciler hiç var olmamış bir nesne için işlem yapar.
Outbox deseni bu boşluğu kapatır. İş kuralındaki değişiklik ve yayımlanacak olay, aynı veritabanı işlemi içinde kaydedilir. Ayrı bir yayınlayıcı outbox kayıtlarını okur, kuyruğa iletir ve başarıyla iletilen kaydı işaretler. Yayınlayıcı da tekrar çalışabileceği için mesajın benzersiz kimliği korunur; tüketiciler yine idempotent tasarlanır.
Bu yaklaşım “tam olarak bir kez” sihrini yaratmaz, fakat kayıp olay ile çift teslim sorunlarını açık ve yönetilebilir hâle getirir.
Sıralama varsayımını daraltın
Birçok iş akışında global mesaj sırası yoktur. Aynı kaynağa ait iki olay farklı partition'a düşebilir, farklı worker'lar tarafından paralel işlenebilir veya tekrar deneme yüzünden eski olay daha geç gelebilir. “Önce OrderCreated, sonra OrderCancelled gelir” varsayımı açıkça korunmuyorsa güvenli değildir.
Gerekli olduğunda sıralamayı tüm sistemde değil, iş anahtarı düzeyinde sağlayın. Aynı sipariş veya belge için olayları aynı partition'a yönlendirmek bir seçenek olabilir. Diğer durumlarda tüketici, olayın sürüm numarasını kontrol ederek eski güncellemeyi görmezden gelebilir. Bu kararlar iş alanının anlamına bağlıdır; gereksiz sıralama kısıtı kapasiteyi düşürür.
Hata kuyruğunu çöplük değil, çalışma yüzeyi yapın
Maksimum deneme sayısını aşan veya kalıcı hata alan mesajlar bir dead-letter queue'ya alınabilir. Bu kuyruk, “sorun yok” işareti değildir; insan veya otomasyon tarafından incelenmesi gereken hata çalışma yüzeyidir.
Her hata mesajı için özgün kimlik, hata sınıfı, ilk oluşma zamanı, deneme sayısı ve iş bağlamı görünmelidir. Hassas içerik veya erişim anahtarı taşımadan, sorunu yeniden üretmeye yetecek kadar bilgi saklanmalıdır. Operasyon ekibi mesajı güvenle yeniden oynatabiliyor mu, yoksa önce veriyi düzeltmesi mi gerekiyor? Bu akış önceden tanımlanmalıdır.
Tüm DLQ mesajlarını tek tuşla geri göndermek risklidir. Kalıcı bir şema hatası veya yetki sorunu binlerce mesajı yeniden başarısızlığa sürükleyebilir. Filtrelenmiş, kontrollü ve gözlemlenen yeniden oynatma daha güvenlidir.
Başarıyı kullanıcı etkisiyle ölçün
Kuyruk boş diye iş akışı sağlıklı sayılmaz. İletilmesi gereken bir e-posta hiç gönderilmemiş, arama indeksi güncel olmamış veya dosya taraması yarım kalmış olabilir. Bu yüzden iş tamamlama süresi, başarısızlık oranı, en eski bekleyen işin yaşı, tekrar deneme oranı ve DLQ derinliği gibi metrikler izlenmelidir.
Kritik bir iş için kullanıcıya görünen durum da tasarlanmalıdır: “işleniyor”, “tamamlandı”, “yeniden denenecek” veya “desteğe iletildi” gibi durumlar belirsizliği azaltır. Ancak işin gerçekten tamamlanmadığı yerde kullanıcıya kesin başarı göstermekten kaçınmak gerekir.
Güvenilir arka plan işlerinde amaç başarısızlığı ortadan kaldırmak değildir. Amaç; tekrar teslim, kısmi kesinti ve dış bağımlılık hataları yaşandığında, sistemin yanlış veya geri alınamaz yan etkiler üretmeden toparlanmasını sağlamaktır. İdempotency, seçici tekrar deneme, outbox ve işletilebilir hata kuyruğu bu dayanıklılığın temel parçalarıdır.