Neden Transactional Outbox?
Bir iş işlemi veritabanına başarıyla yazıldıktan sonra kuyruğa mesaj göndermek ayrı bir sistem çağrısıdır. Uygulama commit sonrasında çökerse iş kaydı kalıcı olur ancak event yayınlanmayabilir. Tersi durumda mesaj önce gönderilip veritabanı işlemi geri alınırsa tüketici gerçekte gerçekleşmemiş bir işlemi işleyebilir.
Transactional Outbox yaklaşımında iş verisi ile yayınlanacak event aynı veritabanı transaction'ı içinde yazılır. Ayrı bir worker, outbox kayıtlarını okuyup kuyruğa iletir. Buradaki hedef mesajı tam olarak bir kez göndermek değil; iş verisiyle event kaydını birlikte kalıcılaştırmak ve tüketicinin tekrar teslimatları güvenli biçimde işleyebilmesini sağlamaktır.
Outbox Veri Modeli ve Durum Geçişleri
Outbox kaydında en az şu bilgiler bulunmalıdır: benzersiz event kimliği, event türü, aggregate veya kaynak kimliği, payload, oluşturulma zamanı, deneme sayısı, son deneme zamanı, durum, lease sahibi ve lease bitiş zamanı. Event kimliği küresel olarak benzersiz olmalı ve tüketici tarafında idempotency anahtarı olarak kullanılabilmelidir.
Durum alanını belirsiz biçimde kullanmak yerine açık geçişler tanımlayın:
Teslimat Semantiği ve İdempotent Tüketici
Worker'ın broker'a gönderim yaptıktan sonra veritabanındaki durumu güncellemesi iki ayrı adımdır. Gönderim başarılı olduğu halde worker kapanırsa aynı event yeniden yayınlanabilir. Bu nedenle pratik tasarım çoğunlukla en az bir kez teslimat semantiğine dayanır.
Tüketici tarafında şu kontroller bulunmalıdır:
Ölçümleme, Kapasite ve Alarm Tasarımı
Yalnızca worker CPU kullanımını izlemek yeterli değildir. Aşağıdaki metrikleri üretim ve tüketim hızlarıyla birlikte değerlendirin:
Temizlik, Arşivleme ve Operasyon Kontrolü
Outbox kayıtlarını sınırsız biçimde aynı tabloda tutmak indeksleri büyütür ve worker sorgularını zamanla yavaşlatır. Published kayıtlar için saklama süresi belirlerken hata analizi, denetim gereksinimi ve yeniden oynatma ihtiyacını hesaba katın. Büyük hacimlerde tarih bazlı bölümleme, arşiv tablosu veya ayrı bir saklama katmanı değerlendirilebilir. Temizleme işlemini küçük partiler halinde çalıştırın; tek seferde büyük silmeler transaction sürelerini, kilit beklemelerini ve transaction log büyümesini artırabilir.
Canlıya almadan önce şu senaryoları test edin:
Bir iş işlemi veritabanına başarıyla yazıldıktan sonra kuyruğa mesaj göndermek ayrı bir sistem çağrısıdır. Uygulama commit sonrasında çökerse iş kaydı kalıcı olur ancak event yayınlanmayabilir. Tersi durumda mesaj önce gönderilip veritabanı işlemi geri alınırsa tüketici gerçekte gerçekleşmemiş bir işlemi işleyebilir.
Transactional Outbox yaklaşımında iş verisi ile yayınlanacak event aynı veritabanı transaction'ı içinde yazılır. Ayrı bir worker, outbox kayıtlarını okuyup kuyruğa iletir. Buradaki hedef mesajı tam olarak bir kez göndermek değil; iş verisiyle event kaydını birlikte kalıcılaştırmak ve tüketicinin tekrar teslimatları güvenli biçimde işleyebilmesini sağlamaktır.
Outbox Veri Modeli ve Durum Geçişleri
Outbox kaydında en az şu bilgiler bulunmalıdır: benzersiz event kimliği, event türü, aggregate veya kaynak kimliği, payload, oluşturulma zamanı, deneme sayısı, son deneme zamanı, durum, lease sahibi ve lease bitiş zamanı. Event kimliği küresel olarak benzersiz olmalı ve tüketici tarafında idempotency anahtarı olarak kullanılabilmelidir.
Durum alanını belirsiz biçimde kullanmak yerine açık geçişler tanımlayın:
- pending: Yayınlanmayı bekleyen kayıt.
- processing: Bir worker tarafından alınmış ve geçici sahiplik süresi devam eden kayıt.
- published: Broker'a başarıyla iletildiği düşünülen kayıt.
- retryable: Geçici hata nedeniyle yeniden denenmesi gereken kayıt.
- dead-letter: Deneme veya zaman sınırını aşmış, operatör incelemesi gereken kayıt.
Teslimat Semantiği ve İdempotent Tüketici
Worker'ın broker'a gönderim yaptıktan sonra veritabanındaki durumu güncellemesi iki ayrı adımdır. Gönderim başarılı olduğu halde worker kapanırsa aynı event yeniden yayınlanabilir. Bu nedenle pratik tasarım çoğunlukla en az bir kez teslimat semantiğine dayanır.
Tüketici tarafında şu kontroller bulunmalıdır:
- Event kimliğini işlenmiş event kayıtlarında benzersiz tutmak.
- Aynı event tekrar geldiğinde yan etki üretmeden mevcut sonucu döndürmek.
- İşlem ile idempotency kaydını mümkünse aynı transaction içinde yazmak.
- Sıra önemliyse kaynak kimliği bazında sıralama veya sürüm kontrolü uygulamak.
- Eski sürümlü bir event'in daha yeni veriyi ezmesini engellemek.
- Kalıcı hataları ayrı bir inceleme ve yeniden oynatma akışına almak.
Ölçümleme, Kapasite ve Alarm Tasarımı
Yalnızca worker CPU kullanımını izlemek yeterli değildir. Aşağıdaki metrikleri üretim ve tüketim hızlarıyla birlikte değerlendirin:
- Outbox backlog: Bekleyen kayıt sayısı ve büyüme eğilimi.
- Publish latency: Event'in oluşturulmasından broker'a iletilmesine kadar geçen süre.
- Retry rate: İlk denemede başarısız olan kayıtların oranı.
- Age of oldest message: En eski bekleyen kaydın yaşı.
- Duplicate delivery rate: Tekrar teslim edilen event oranı.
- Dead-letter count: Kalıcı olarak başarısız kayıtların sayısı.
- Database impact: Outbox yazma, claim, güncelleme ve temizleme sorgularının gecikmeye etkisi.
Temizlik, Arşivleme ve Operasyon Kontrolü
Outbox kayıtlarını sınırsız biçimde aynı tabloda tutmak indeksleri büyütür ve worker sorgularını zamanla yavaşlatır. Published kayıtlar için saklama süresi belirlerken hata analizi, denetim gereksinimi ve yeniden oynatma ihtiyacını hesaba katın. Büyük hacimlerde tarih bazlı bölümleme, arşiv tablosu veya ayrı bir saklama katmanı değerlendirilebilir. Temizleme işlemini küçük partiler halinde çalıştırın; tek seferde büyük silmeler transaction sürelerini, kilit beklemelerini ve transaction log büyümesini artırabilir.
Canlıya almadan önce şu senaryoları test edin:
- Veritabanı commit sonrasında uygulamanın çökmesi.
- Broker'a gönderim sırasında ağ bağlantısının kopması.
- Gönderim başarılı olduktan sonra worker'ın durum güncellemeden kapanması.
- Aynı kaydı iki worker'ın almaya çalışması ve lease süresinin dolması.
- Tüketicinin aynı event'i birden fazla kez alması.
- Uzun süre başarısız kalan mesajın dead-letter akışına taşınması.
- Broker veya veritabanı gecikmesinin backlog ve publish latency üzerindeki etkisi.