PostgreSQL'de outbox deseni ile güvenilir API entegrasyonları
Outbox desenini öğrenin: PostgreSQL'de olayları saklayın, sonra üçüncü taraf API'lara sırayla, yeniden denemeler ve çoğaltmayı önleyerek teslim edin.

Neden entegrasyonlar uygulamanız çalışsa bile başarısız olur
Uygulamanızda "başarılı" görünen bir işlemle birlikte entegrasyonun arka planda sessizce başarısız olduğu sık görülür. Veritabanı yazmanız hızlı ve güvenilirdir. Üçüncü taraf bir API çağrısı ise değildir. Bu iki farklı dünya yaratır: sisteminiz değişikliğin olduğunu söyler, ama dış sistem bunu hiç duymamıştır.
Tipik bir örnek: bir müşteri sipariş verir, uygulamanız bunu PostgreSQL'e kaydeder ve ardından bir kargo sağlayıcısını bilgilendirmeye çalışır. Sağlayıcı 20 saniye boyunca zaman aşımına uğrarsa ve isteğiniz vazgeçerse, sipariş hâlâ gerçek olur ama gönderi hiç oluşturulmamış olur.
Kullanıcılar bunu kafa karıştırıcı, tutarsız davranış olarak yaşar. Kaybolan olaylar "hiçbir şey olmadı" gibi görünür. Çoğaltılmış (duplicate) olaylar ise "neden iki kez ücretlendirildim?" gibi sorunlara yol açar. Destek ekipleri de sorunun uygulamanız mı, ağ mı yoksa partnerde mi olduğunu söylemekte zorlanır.
Yeniden denemeler yardımcı olur ama tek başına doğruluk sağlamaz. Zaman aşımından sonra yeniden denerseniz, partnerin ilk isteği alıp almadığını bilmediğiniz için aynı olayı iki kez gönderebilirsiniz. Sıralama dışına çıkarak yeniden denerseniz, "Order shipped" olayını "Order paid" olayından önce gönderebilirsiniz.
Bu problemler genellikle normal eşzamanlılıktan kaynaklanır: birden fazla worker paralel işlem yapar, birçok sunucu aynı anda yazar ve "en iyi çaba" kuyrukları yükte zamanlamayı değiştirir. Hata modları öngörülebilirdir: API'ler kapanır veya yavaşlar, ağ istekleri düşer, işlemler kritik anda çöker ve yeniden denemeler idempotans zorlanmadıkça çoğaltmalara neden olur.
Outbox deseni bu tür normal hatalar için vardır.
Outbox deseni basitçe nedir
Outbox deseni basittir: uygulamanız önemli bir değişiklik yaptığında (örneğin bir sipariş oluşturulduğunda), aynı işlem içinde veritabanına küçük bir "gönderilecek olay" kaydı da yazar. Eğer veritabanı commit ederse, iş verisi ve olay kaydının birlikte var olduğunu bilirsiniz.
Bundan sonra ayrı bir worker outbox tablosunu okur ve bu olayları üçüncü taraf API'lara teslim eder. Eğer bir API yavaşsa, kapalıysa veya zaman aşımına uğruyorsa, ana kullanıcı isteğiniz yine de başarılı olur çünkü dış çağrıyı beklemiyor.
Bu, isteği işleyicide API çağrısı yaparken oluşan nahoş durumları önler:
- Sipariş kaydedildi ama API çağrısı başarısız oldu.
- API çağrısı başarılı oldu ama uygulamanız siparişi kaydetmeden çöktü.
- Kullanıcı tekrar denedi ve aynı şeyi iki kez gönderdiniz.
Outbox deseni çoğunlukla kaybolan olayları, kısmi hataları (veritabanı tamam, dış API tamam değil), kazara çift göndermeleri ve daha güvenli yeniden denemeleri (tahmin yapmadan tekrar deneyebilirsiniz) önlemeye yardım eder.
Her şeyi çözmez. Eğer yük yanlışsa, iş kurallarınız hatalıysa veya üçüncü taraf API veriyi reddediyorsa, yine de doğrulama, iyi hata yönetimi ve başarısız olayları inceleyip düzeltme yolu gerekir.
PostgreSQL'de bir outbox tablosu tasarlamak
İyi bir outbox tablosu kasıtlı olarak sıkıcı olmalıdır. Yazması kolay, okuması kolay ve yanlış kullanması zor olmalı.
Aşağıda uyarlayabileceğiniz pratik bir temel şema var:
create table outbox_events (
id bigserial primary key,
aggregate_id text not null,
event_type text not null,
payload jsonb not null,
status text not null default 'pending',
created_at timestamptz not null default now(),
available_at timestamptz not null default now(),
attempts int not null default 0,
locked_at timestamptz,
locked_by text,
meta jsonb not null default '{}'::jsonb
);
Bir ID seçmek
bigserial (veya bigint) kullanmak sıralamayı basit ve indeksleri hızlı tutar. UUID'ler sistemler arası benzersizlik için harikadır, ama oluşturulma sırasına göre sıralanmazlar; bu da polling'i daha tahmin edilemez yapabilir ve indeksleri ağırlaştırabilir.
Yaygın bir uzlaşma: sıralama için id'yi bigint olarak tutun ve hizmetler arasında paylaşmak için stabil bir tanımlayıcı gerekiyorsa ayrı bir event_uuid ekleyin.
Önemli indeksler
Worker'ınız aynı desenleri gün boyunca sorgulayacak. Çoğu sistem için gerekir:
- Sıradaki bekleyen olayları sıralı şekilde almak için
(status, available_at, id)gibi bir indeks. - Eski kilitleri süresinden düşürmeyi planlıyorsanız
(locked_at)üzerinde indeks. - Bazen aggregate başına teslimat yapıyorsanız
(aggregate_id, id)gibi bir indeks.
Payload'ları stabil tutun
Payload'ları küçük ve öngörülebilir tutun. Alıcının gerçekten ihtiyaç duyduğu veriyi saklayın, tüm satırınızı değil. Güvenli şekilde alanları geliştirebilmek için açık bir versiyon (örneğin meta içinde) ekleyin.
metayı yönlendirme ve hata ayıklama bağlamı için kullanın: tenant ID, correlation ID, trace ID ve dedup anahtarı gibi. Bu ek bağlam, destek gerektiğinde "bu siparişle ne oldu?" sorusunu cevaplamayı kolaylaştırır.
İş yazımı ile olayları güvenli şekilde kaydetmek
En önemli kural basittir: iş verisini ve outbox olayını aynı veritabanı işleminde yazın. İşlem commit ederse, her ikisi de var olur. Rollback olursa, hiçbiri olmaz.
Örnek: bir müşteri sipariş veriyor. Tek bir işlem içinde order satırını, order item'larını ve order.created gibi bir outbox satırını insert edin. Herhangi bir adım başarısız olursa, "created" olayı dünyanın dışına kaçmamalıdır.
Bir olay mı yoksa birçok mı?
Mümkünse iş eylemi başına bir olayla başlayın. Bu düşünmesi daha kolay ve işlem maliyeti daha düşüktür. Farklı tüketiciler gerçekten farklı zamanlama veya payload istiyorsa (örneğin fulfillment için order.created ve faturalama için payment.requested) o zaman ayırın. Bir tıklama için çok sayıda olay üretmek yeniden denemeleri, sıralama sorunlarını ve tekrar göndermeyi artırır.
Hangi payload'ı saklamalısınız?
Genelde seçim şunlar arasındadır:
- Snapshot: eylem anındaki ana alanları saklamak (sipariş toplamı, para birimi, müşteri ID). Bu ileride ekstra okuma yapmayı engeller ve mesajı stabil tutar.
- Referans ID: sadece order ID saklayıp worker'ın detayları sonra yüklemesine izin vermek. Bu outbox'ı küçük tutar ama ek okuma gerektirir ve sipariş düzenlenirse değişebilir.
Pratik orta yol: kimlikler artı kritik bazı alanların küçük bir snapshot'ı. Bu, alıcıların hızlı hareket etmesine yardımcı olur ve hata ayıklamayı kolaylaştırır.
İşlem sınırını dar tutun. Üçüncü taraf API çağrılarını aynı işlem içinde yapmayın.
Olayları üçüncü taraf API'lara teslim etme: worker döngüsü
Olaylar outbox'a girince, bunları okuyup üçüncü taraf API'lara çağıracak bir worker gerekir. Bu kısım deseni güvenilir bir entegrasyona dönüştürür.
Polling genellikle en basit seçenektir. LISTEN/NOTIFY gecikmeyi azaltabilir, ama ekstra hareketli parçalar ekler ve bildirimler kaçırıldığında veya worker yeniden başladığında yedek ihtiyacı olur. Çoğu ekip için küçük partilerle sabit polling çalıştırmak daha kolay izlenir ve hata ayıklaması daha basittir.
Satırları güvenli şekilde sahiplenme
Worker, iki worker'ın aynı olayı aynı anda işlememesi için satırları sahiplenmelidir. PostgreSQL'de yaygın yaklaşım SKIP LOCKED ile satır kilitleri kullanarak bir batch seçmek ve sonra bunları işlemde işaretlemektir.
Pratik bir durum akışı:
pending: gönderilmeye hazırprocessing: bir worker tarafından kilitlendi (kullanlocked_byvelocked_at)sent: başarıyla teslim edildifailed: maksimum deneme sonrası durduruldu (ya da manuel inceleme için ayrıldı)
Veritabanına nazik davranmak için partileri küçük tutun. 10-100 satır arası bir batch ve her 1-5 saniyede bir çalıştırmak yaygın bir başlangıçtır.
Bir çağrı başarılı olduğunda satırı sent olarak işaretleyin. Başarısız olduğunda attempts'ı artırın, available_at'ı gelecekte bir zamana ayarlayın (backoff), kilidi temizleyin ve tekrar pending haline getirin.
Sızıntı yapmayan loglama
İyi loglar hataları eyleme dönüştürür. Outbox id, event tipi, hedef adı, deneme sayısı, zamanlama ve HTTP durum veya hata sınıfını loglayın. İstek gövdesini, auth header'larını veya tam yanıtları loglamaktan kaçının. Eğer korelasyon gerekiyorsa, ham payload yerine güvenli bir istek ID'si veya hash saklayın.
Gerçek sistemlerde işe yarayan sıralama kuralları
Birçok ekip "olayları oluşturma sırasıyla gönder" ile başlar. Ancak "aynı sıra" nadiren küreseldir. Bir global kuyruk zorlarsanız, tek bir yavaş müşteri veya hatalı API herkesin önünü tıkayabilir.
Pratik kural: küresel sıralama yerine grup başına sıralamayı koruyun. Dış dünyanın verilerinize nasıl baktığına uyan bir gruplayıcı anahtar seçin: customer_id, account_id veya aggregate_id (ör. order_id). Sonra her grup içinde sıralamayı garanti edin ve farklı grupları paralel gönderin.
Sıralamayı bozmadan paralel worker'lar
Birden fazla worker çalıştırın, ama aynı grup için iki worker'ın aynı anda işlem yapmadığından emin olun. Yaygın yaklaşım her zaman bir aggregate_id için en erken gönderilmemiş olayı teslim etmektir ve farklı aggregate'ler arasında paralellik izin vermektir.
Sahiplenme kurallarını basit tutun:
- Her grup için sadece en erken pending olayı teslim edin.
- Gruplar arasında paralelliğe izin verin; aynı grup içinde paralelliğe izin vermeyin.
- Bir olayı sahiplenin, gönderin, durumu güncelleyin, sonra devam edin.
Bir olay geri kalanları engellediğinde
Er ya da geç, bir "poison" olay saatlerce başarısız olabilir (kötü payload, iptal edilmiş token, sağlayıcı kesintisi). Eğer grup içi sıralamayı sıkı uyguluyorsanız, o grubun sonraki olayları beklemelidir, ama diğer gruplar devam etmelidir.
Çalışabilir bir uzlaşma: olay başına deneme sayısını sınırlayın. Ondan sonra failed olarak işaretleyin ve sadece o grup için duraklatın; böylece bozuk bir müşteri herkesi yavaşlatmaz.
Durumu daha da kötüleştirmeden yeniden denemeler
Yeniden denemeler, iyi bir outbox kurulumunu güvenilir veya gürültülü yapan yerdir. Amaç basittir: muhtemelen işe yarayacaksa tekrar deneyin, işe yaramayacaksa çabuk durun.
Üstel backoff ve sert bir üst sınır kullanın. Örnek: 1 dakika, 2 dakika, 4 dakika, 8 dakika, sonra dur (veya maksimum gecikme örn. 15 dakika ile devam). Bir kötü olayın sistemi sonsuza kadar tıkamaması için her zaman maksimum deneme sayısı koyun.
Her hata yeniden denenmemelidir. Kuralları net tutun:
- Yeniden dene: ağ zaman aşımı, bağlantı sıfırlamaları, DNS sorunları ve HTTP 429 veya 5xx cevapları.
- Yeniden deneme yapma: HTTP 400 (geçersiz istek), 401/403 (yetkilendirme sorunları), 404 (yanlış uç nokta) veya göndermeden önce tespit edebileceğiniz doğrulama hataları.
Yeniden deneme durumunu outbox satırında saklayın. attempts'ı artırın, bir sonraki deneme için available_at ayarlayın ve kısa, güvenli bir hata özeti kaydedin (durum kodu, hata sınıfı, kırpılmış mesaj). Hata alanlarında tam payload veya hassas veri saklamayın.
Rate limit'ler özel muamele gerektirir. HTTP 429 alırsanız, Retry-After varsa ona uyun. Yoksa, retry fırtınasını önlemek için daha agresif geri çekilin.
Çoğaltmayı önleme ve idempotans temelleri
Güvenilir API entegrasyonları kurarken aynı olayın iki kez gönderilebileceğini varsayın. Worker, HTTP çağrısından sonra çökebilir ama başarı kaydını tutamayabilir. Zaman aşımı bir başarıyı gizleyebilir. Yeniden deneme yavaş ilk denemeyle çakışabilir. Outbox deseni kaçan olayları azaltır ama tek başına çoğaltmayı engellemez.
En güvenli yaklaşım idempotanstır: tekrar edilen teslimatlar tek bir teslimatın yarattığı sonucu üretir. Üçüncü taraf API'ya çağrı yaparken hedefe ve olaya göre sabit kalan bir idempotency anahtarı ekleyin. Birçok API bunu bir header'ta destekler; desteklemiyorsa anahtarı istek gövdesine koyun.
Basit bir anahtar hedef + event ID olabilir. Örneğin evt_123 ID'li bir olay için her zaman destA:evt_123 gibi bir anahtar kullanın.
Kendi tarafınızda çoğaltmayı önlemek için outbound teslimat günlüğü tutun ve (destination, event_id) gibi benzersiz bir kural uygulayın. İki worker yarışsa bile sadece biri "bunu gönderiyoruz" kaydını oluşturabilir.
Webhook'lar da çoğaltabilir
Webhook callback'leri (ör. "teslimat onaylandı" veya "durum güncellendi") alıyorsanız, bunları da aynı şekilde ele alın. Sağlayıcılar yeniden dener ve aynı payload'u tekrar görebilirsiniz. İşlenmiş webhook ID'lerini saklayın veya sağlayıcının mesaj ID'sinden stabil bir hash hesaplayıp tekrarları reddedin.
Veriyi ne kadar saklamalısınız
Outbox satırlarını başarı (veya kabul ettiğiniz nihai bir hata) kaydedilene kadar tutun. Teslimat günlüklerini daha uzun tutun; birisi "Gönderdik mi?" diye sorduğunda bunlar denetim izi sağlar.
Yaygın yaklaşım:
- Outbox satırları: başarıdan sonra kısa bir güvenlik penceresiyle silin veya arşivle (günler).
- Teslimat günlükleri: uyumluluk ve destek ihtiyaçlarına göre haftalar veya aylar boyunca saklayın.
- Idempotency anahtarları: en az yeniden denemelerin olabileceği süre kadar saklayın (webhook tekrarları için daha uzun).
Adım adım: outbox desenini uygulamak
Ne yayınlayacağınıza karar verin. Olayları küçük, odaklı ve daha sonra tekrar oynatılabilir tutun. İyi bir kural: alıcıya eylem yapması için yeterli veri ile iş olgusunu bir olay olarak yayınlayın.
Temeli kurun
Açık event isimleri seçin (ör. order.created, order.paid) ve payload şemanızı versiyonlayın (ör. v1, v2). Versiyonlama yeni alanlar eklerken eski tüketicileri kırmaz.
PostgreSQL outbox tablonuzu oluşturun ve worker'ın en çok çalışacağı sorgular için indeksler ekleyin; özellikle (status, available_at, id) indeksine dikkat edin.
Yazma akışınızı güncelleyin ki iş değişikliği ve outbox insert'i aynı veritabanı işleminde olsun. Bu ana garanti budur.
Teslimat ve kontrol ekleyin
Basit bir uygulama planı:
- Uzun vadede destekleyebileceğiniz event tiplerini ve payload versiyonlarını tanımlayın.
- Outbox tablosunu ve indeksleri oluşturun.
- Ana veri değişikliğinin yanında bir outbox satırı insert edin.
- Satırları sahiplenen, üçüncü taraf API'ya gönderen ve sonra durumu güncelleyen bir worker oluşturun.
- Backoff ile yeniden deneme zamanlaması ve denemeler tükenince
faileddurumu ekleyin.
Sorunları erken fark etmeniz için temel metrikler ekleyin: gecikme (en eski gönderilmemiş olayın yaşı), gönderim hızı ve hata oranı.
Basit bir örnek: sipariş olaylarını harici servislere gönderme
Bir müşteri uygulamanızda sipariş verir. Sistemin dışına yapılması gereken iki şey var: ödeme sağlayıcısı kartı tahsil etmeli ve kargo sağlayıcısı bir gönderi oluşturmalı.
Outbox ile checkout isteği içinde bu API'ları çağırmazsınız. Bunun yerine siparişi ve outbox olayını aynı PostgreSQL işlemi içinde kaydedersiniz, böylece "sipariş kaydedildi ama bildirim gönderilmedi" (veya tersi) gibi durumlar olmaz.
Tipik bir outbox satırı aggregate_id (sipariş ID), event_type order.created ve toplamlar, item'lar ve teslimat detaylarını içeren bir JSONB payload içerir.
Bir worker pending satırları alır ve dış servisleri çağırır (ya tanımlı bir sırayla ya da payment.requested ve shipment.requested gibi ayrı olaylar yayınlayarak). Bir sağlayıcı kapalıysa worker denemeyi kaydeder, available_at'ı ileriye iter ve diğerlerini bloklamadan devam eder. Sipariş yine vardır ve olay daha sonra yeniden denenecektir.
Sıralama genellikle "her sipariş için" veya "her müşteri için" olur. Aynı aggregate_id'ye ait olayların sırayla işlenmesini sağlayın ki order.paid asla order.created'dan önce gelmesin.
Çoğaltmayı önlemek iki kez ücretlendirmeyi veya iki gönderi oluşturmayı engeller. Üçüncü taraf destekliyorsa idempotency anahtarı gönderin ve bir hedef teslimat kaydı tutun ki zaman aşımından sonra yeniden denemek ikinci bir eylem tetiklemesin.
Göndermeden önceki hızlı kontroller
Bir entegrasyonu para taşıması, müşterileri bilgilendirmesi veya veriyi senkronize etmesi için güvenmeye başlamadan önce uçları test edin: çökmeler, yeniden denemeler, tekrarlar ve birden çok worker.
Yaygın hataları yakalayan kontroller:
- Outbox satırının iş değişikliği ile aynı işlemde oluşturulduğunu doğrulayın.
- Göndericinin birden fazla örnekte güvenle çalışabileceğini doğrulayın. İki worker aynı olayı aynı anda göndermemelidir.
- Sıralama önemliyse, kuralı tek cümlede tanımlayın ve stabil bir anahtar ile uygulayın.
- Her hedef için tekrarları nasıl önlediğinizi ve "gönderdiğimizi" nasıl kanıtladığınızı kararlaştırın.
- N N denemeden sonra olayın
failedolacağını, son hata özetini saklayacağınızı ve basit bir yeniden işleme eylemi sağlayacağınızı tanımlayın.
Gerçekçi bir kontrol: Stripe isteği kabul edebilir ama worker başarıyı kaydetmeden çöker. Idempotency yoksa yeniden deneme çift eyleme neden olabilir. Idempotency ve kaydedilmiş bir teslimat kaydı ile yeniden deneme güvenli olur.
Sonraki adımlar: uygulamayı uygulamadan bozmadan dağıtmak
Dağıtım (rollout) outbox projelerinin genelde başarılı veya tıkanık olduğu yerdir. İlk başta küçük tutun ki gerçek davranışı görün ve tüm entegrasyon katmanınızı riske atmayın.
Bir entegrasyon ve bir event tipi ile başlayın. Örneğin sadece order.created olayını tek bir tedarikçi API'ına gönderin. Bu size throughput, gecikme ve hata oranı için temiz bir temel sağlar.
Problemleri erken görünür kılın. Outbox gecikmesi (bekleyen olay sayısı ve en eskisinin yaşı) ve hata oranı için dashboard'lar ve alarmlar ekleyin; "şu an geride miyiz?" sorusuna 10 saniye içinde cevap verebiliyorsanız sorunları kullanıcılar görmeden yakalarsınız.
İlk olaydan önce güvenli bir yeniden işleme planınız olsun. "Yeniden işleme"nin ne anlama geldiğine karar verin: aynı payload'u yeniden gönderme, güncel veriden payload'u yeniden oluşturma veya manuel incelemeye gönderme. Hangi durumların yeniden gönderilmesinin güvenli olduğunu ve hangilerinin insan müdahalesi gerektirdiğini dokümante edin.
AppMaster (appmaster.io) gibi no-code bir platformla inşa ediyorsanız, aynı yapı geçerlidir: iş verinizi ve outbox satırınızı PostgreSQL'e birlikte yazın, sonra ayrı bir backend işlemi çalıştırarak teslim edin, yeniden deneyin ve olayları sent veya failed olarak işaretleyin.
SSS
Outbox deseni, bir kullanıcı işlemi veritabanınızı güncelliyorsa ve başka bir sistemde işlem tetiklemesi gerekiyorsa kullanın. Zaman aşımı, dalgalı ağ bağlantısı veya üçüncü taraf kesintileri "bizde kaydedildi, onların tarafında eksik" durumlarını önler.
İş verisini ve outbox satırını aynı veritabanı işleminde yazmak size tek bir net garanti verir: ya ikisi birden vardır ya da hiçbiri. Bu, "API çağrısı başarılı oldu ama sipariş kaydedilmedi" veya "sipariş kaydedildi ama API çağrısı gerçekleşmedi" gibi kısmi hataları önler.
Pratik bir varsayılan: id, aggregate_id, event_type, payload, status, created_at, available_at, attempts ve locked_at, locked_by gibi kilit alanlar. Bu, gönderme, yeniden deneme zamanlaması ve güvenli eşzamanlılık için yeterlidir.
Çoğu sistem için temel bir indeks: (status, available_at, id) — böylece worker'lar sıradaki gönderilebilir olayları hızlıca alır. Gerçekten sorguladığınız alanlar için ek indeks ekleyin; gereksiz indeksler insert'leri yavaşlatır.
Çoğu ekip için polling (döngüyle sorgulama) en basit ve öngörülebilir yaklaşımdır. Küçük partiler ve kısa aralıklarla başlayın, sonra yük ve gecikmeye göre ayarlayın; basit bir döngü sorun çıktığında debug etmeyi kolaylaştırır.
Satırları satır düzeyinde kilitleyerek iki worker'ın aynı olayı aynı anda işlememesi sağlanır; yaygın yöntem SKIP LOCKED ile seçim yapmaktır. Sonra satırı processing olarak işaretleyin, gönderin ve başarıyla gönderildiyse sent olarak güncelleyin veya başarısızsa kilidi temizleyip available_at ayarlayın.
Üstel geri çekilme (exponential backoff) ve maksimum deneme sayısı kullanın; yalnızca muhtemelen geçici hataları yeniden deneyin. Zaman aşımı, ağ hataları ve HTTP 429/5xx yeniden denenecekler; doğrulama hataları ve çoğu 4xx genelde yeniden denememelidir.
Outbox deseni tek-başına tam-tekil (exactly-once) teslimatı garanti etmez; kısmi başarılar veya worker çökmesi tekrarlar yaratabilir. Her hedef için event başına sabit bir idempotency anahtarı kullanın ve (destination, event_id) gibi benzersiz bir kısıtla outbound teslimat kaydı tutun.
Sıralamayı küresel olarak zorlamayın; bunun yerine bir grup içinde sıralamayı koruyun. aggregate_id veya customer_id gibi bir gruplayıcı anahtar seçin, aynı grup içindeki olayları tek tek işleyin ve farklı grupları paralel olarak işleyin.
Uzun süre başarısız olan bir olayı ("poison" event) maksimum deneme sayısından sonra failed olarak işaretleyin, kısa ve güvenli bir hata özeti tutun ve aynı grup içindeki sonraki olayları yalnızca ilgili sorun çözülene kadar durdurun. Bu, tek bir müşterinin herkesi yavaşlatmasını engeller.


