Outbox Pattern: Kurtarıcı mı, Yoksa Baş Belası mı?
Dağıtık sistemlerde veri tutarlılığını sağlamanın getirdiği zorluklar ve Outbox Pattern’in bu denkleme getirdiği tartışmalı “çözüm” üzerine…
Outbox Pattern: Kurtarıcı mı, Yoksa Baş Belası mı?
Dağıtık sistemlerde veri tutarlılığını sağlamanın getirdiği zorluklar ve Outbox Pattern’in bu denkleme getirdiği tartışmalı “çözüm” üzerine bir inceleme.

Uzun yıllardır yazılım dünyasının içinde, özellikle dağıtık sistemlerin karmaşık ve bir o kadar da büyüleyici problemleriyle boğuşan biri olarak, bazı tasarım desenlerinin nasıl bir anda popülerleşip sonra da ateşli tartışmaların merkezine oturduğuna sıkça şahit oldum. İşte Outbox Pattern de tam olarak böyle bir desen. Bir yanda veri tutarlılığı için neredeyse sihirli bir değnek gibi sunulurken, diğer yanda getirdiği yan etkilerle “yağmurdan kaçarken doluya tutulmak” deyimini akla getiriyor.
Peki, nedir bu Outbox Pattern? Gerçekten dağıtık sistemlerin aziz bir kurtarıcısı mı, yoksa dikkatli kullanılmadığında sistemlerimize daha büyük dertler açan bir baş belası mı? Gelin, bu deseni bir cerrah titizliğiyle masaya yatıralım, artılarını ve özellikle de eksilerini tüm çıplaklığıyla inceleyelim.
Sorun Neydi ki?
Her şey o meşhur senaryoyla başlar: Bir servisin aynı anda hem veritabanına bir kayıt atması hem de bu olayla ilgili dış dünyaya bir mesaj göndermesi gerekir. En klasik örnek, bir e-ticaret sitesinde siparişin veritabanına kaydedilmesi ve aynı anda siparişin oluşturulduğuna dair bir olayın (event) bir mesajlaşma kuyruğuna (message broker) gönderilmesidir.
// Outbox Pattern OLMADAN bir örnek
public async Task CreateOrder(Order order)
{
// 1. Adım: Siparişi veritabanına kaydet
await _dbContext.Orders.AddAsync(order);
await _dbContext.SaveChangesAsync();
// Peki ya uygulama tam burada çökerse?
// Veya mesaj kuyruğu ayakta değilse?
// 2. Adım: Olayı mesaj kuyruğuna gönder
var orderCreatedEvent = new OrderCreatedEvent(order.Id);
await _messageBroker.PublishAsync(orderCreatedEvent);
}
Yukarıdaki kodda masum görünen iki adımlı bir süreç var. Ama bu süreç, tutarsızlıklar için adeta bir mayın tarlasıdır.
- Veritabanı işlemi başarılı olur ama mesaj gönderilemezse: Sipariş sistemde var olur ama kimsenin haberi olmaz. Stok yönetimi, faturalandırma gibi diğer servisler bu siparişten bihaber yaşar gider.
- Mesaj gönderilir ama veritabanı işlemi başarısız olursa (veya geri alınırsa): Ortada olmayan bir siparişin haberi tüm sisteme yayılır. Hayali bir sipariş için stok düşülür, fatura kesilmeye çalışılır.
İşte bu, iki farklı sisteme (veritabanı ve mesaj kuyruğu) aynı anda yazmaya çalışmanın getirdiği “Dual Write” problemidir. Geleneksel dağıtık işlemler (2PC — Two-Phase Commit) ise genellikle modern mimarilerde pratik veya istenen bir çözüm değildir.
Sahneye Çözüm Olarak Çıkan Kahraman: Outbox Pattern
Outbox Pattern bu soruna zarif bir çözüm sunar gibi görünür. Fikir oldukça basittir: “Madem iki farklı sisteme aynı anda güvenli bir şekilde yazamıyorum, o zaman tek bir sisteme, yani kendi veritabanıma iki şey yazayım!”
- Servis, yapması gereken asıl veritabanı işlemini (örneğin siparişi kaydetmek) ve göndermesi gereken mesajı, aynı veritabanı işlemi (transaction) içinde iki ayrı tabloya yazar. Biri
Orderstablosu, diğeri iseOutboxMessagestablosu. - Bu iki yazma işlemi tek bir atomik transaction içinde olduğu için ya ikisi de başarılı olur ya da ikisi de geri alınır. Böylece veri tutarsızlığı riski ortadan kalkar. Sipariş varsa, onun gönderilecek mesajı da “giden kutusunda” bekliyordur.
- Ayrı bir “mesaj rölesi” (message relay) süreci (genellikle bir arka plan servisi), periyodik olarak bu
OutboxMessagestablosunu kontrol eder. Gönderilmemiş mesajları bulur, onları asıl mesajlaşma kuyruğuna gönderir ve son olarak tablodaki ilgili mesajı "gönderildi" olarak işaretler.
Bu sayede, veritabanı işlemi başarılı olduğu sürece mesajın en az bir kez (at-least-once) gönderileceği garanti altına alınmış olur. Kulağa harika geliyor, değil mi? Teoride evet. Ama pratikte işin rengi biraz değişiyor.
Madalyonun Diğer Yüzü: Outbox Neden Tehlikeli Olabilir?
Bu deseni öven birçok kaynak bulabilirsiniz, ancak uzun yıllara dayanan tecrübem, bir çözümün potansiyel maliyetlerini ve yan etkilerini anlamadan onu benimsemenin felaketle sonuçlanabileceğini öğretti. Gelin detaylara beraber bakalım.
1. Veritabanı: En Zayıf Halkanın Üzerine Daha Fazla Yük Bindirmek
Çoğu sistemde veritabanı zaten en kritik, en hassas ve ölçeklenmesi en zor bileşendir. Kendisi başlı başına bir “Single Point of Failure” (Tekil Hata Noktası) adayıdır. Outbox Pattern, bu en zayıf halkayı alıp üzerine daha da fazla sorumluluk ve yük bindirir.
- Artan Yazma Yükü: Her bir iş işlemi artık veritabanına en az bir ek yazma işlemi (outbox tablosuna INSERT) daha yapmak zorunda. Yüksek işlem hacmine sahip sistemlerde bu, veritabanı üzerinde ciddi bir baskı oluşturur.
- Sürekli Sorgulama (Polling): “Mesaj rölesi” sürecinin en yaygın uygulama şekli, outbox tablosunu sürekli sorgulayan bir “polling publisher”dır. Bu, veritabanına sürekli “Gönderilecek yeni mesaj var mı?” diye bağıran bir komşuya benzer. Bu durum, veritabanı kaynaklarını tüketir ve özellikle bulut ortamında sorgu başına maliyetlendirme yapılan servislerde faturayı kabartır.
2. Transaction Cehennemi ve Kilitlenme (Contention) Riski
İşin en tehlikeli kısımlarından biri de burası. Outbox tablosu, sistemdeki tüm farklı iş süreçlerinin ortak yazma noktası haline gelir.
- Bir kullanıcıyı oluşturan süreç de, bir ürünü güncelleyen süreç de, bir siparişi onaylayan süreç de gelip aynı
OutboxMessagestablosuna yazmaya çalışır. - Yüksek eşzamanlılık altında bu durum, veritabanı seviyesinde ciddi kilitlenmelere (locking) ve beklemelere (contention) yol açar. Birbiriyle hiç alakası olmayan iki iş süreci, sırf outbox tablosuna yazma sırası bekledikleri için birbirini yavaşlatabilir, hatta tüm sistemi durma noktasına getirebilir.
- Bu sorunu aşmak için
SELECT ... FOR UPDATE SKIP LOCKEDgibi gelişmiş veritabanı özelliklerini kullanmak, sistemi daha da karmaşıklaştırır ve her veritabanı sisteminde aynı şekilde desteklenmeyebilir.
Bu noktada akla hemen şu itiraz gelebilir: “Neden tek bir outbox tablosu kullanalım ki? Her iş süreci veya her ana tablo için (Orders_Outbox, Customers_Outbox gibi) ayrı bir outbox tablosu oluştursak bu kilitlenme sorununu çözmez miyiz?"
Teoride bu yaklaşım, farklı süreçlerin tek bir tablo üzerinde kilitlenme yarışına girmesini engelleyerek sorunu hafifletir gibi görünür. Ancak bu “çözüm” de kendi baş belalarını beraberinde getirir ve çoğu zaman problemi daha karmaşık bir hale sokar:
- Veritabanında Tablo Karmaşası: Olay yayınlaması gereken her bir iş süreci veya varlık için yeni bir outbox tablosu oluşturmak, veritabanı şemasını kısa sürede onlarca, hatta yüzlerce tabloyla doldurur. Bu durum, veritabanının yönetimini ve anlaşılırlığını ciddi şekilde zorlaştırır.
- Mesaj Rölesinin Kabusu: En büyük darbeyi mesaj rölesi süreci alır. Artık tek bir tabloyu dinlemek yerine, potansiyel olarak onlarca farklı tabloyu dinlemesi gerekir. Bu nasıl yapılacak? Her tabloyu sırayla mı sorgulayacak? Bu, verimsizliğe ve gecikmelere yol açar. Tüm tabloları
UNIONile birleştirip tek bir sorgu atmaya çalışmak ise performans açısından bir kabustur. Daha da kötüsü, süreçler arası olay sıralamasını (global ordering) garanti etmek neredeyse imkansız hale gelir. - Artan Bakım Maliyeti: Her yeni olay yayınlayan süreç için sadece kodda değil, veritabanında yeni bir tablo oluşturmak ve mesaj rölesi servisini bu yeni tabloyu tanıyacak şekilde güncellemek gerekir. Bu, sistemi kırılgan hale getirir ve geliştirme süreçlerini yavaşlatır.
Kısacası, kilitlenme sorunundan kaçmak için birden fazla outbox tablosu kullanmak, sizi veritabanı ve uygulama karmaşasıyla dolu başka bir fırtınanın ortasına atar.
3. Asenkron İletişim Yanılsaması ve Yeni Hata Noktaları
Outbox Pattern, asenkron iletişimin doğasına bir katman daha ekleyerek onu daha da “asenkron” ve dolayısıyla daha gecikmeli hale getirir.
- Artan Gecikme (Latency): Normal bir pub/sub modelinde mesajınız (sistem sağlığı yerindeyse) neredeyse anında kuyruğa düşer. Outbox ile mesajınız önce veritabanına yazılır, sonra arka plan servisinin uyanmasını, mesajı okumasını ve göndermesini bekler. Bu gecikme saniyeler, hatta dakikalar mertebesinde olabilir. Müşteriler (ve bazen yöneticiler) için “butona bastım, neden hala e-posta gelmedi?” sorusunu daha sık duymanıza neden olabilir.
- Yeni Bir Hata Noktası: Artık mimarinizde yeni ve kritik bir bileşeniniz var: Mesaj Rölesi (Message Relay). Bu servis çökerse ne olur? Mesajlar gönderilmez. Outbox tablosu şişmeye başlar, bu da veritabanı performansını olumsuz etkiler ve bir süre sonra tüm sistemin yavaşlamasına neden olur. Yağmurdan kaçarken doluya yakalanmak tam olarak budur.
Sonuç: Kullanmalı mıyız, Kaçmalı mıyız?
Outbox Pattern, “Dual Write” gibi çok gerçek ve kritik bir sorunu çözer. Bu hakkını teslim etmek gerekir. Ancak bu çözümü, bedelsiz bir sihir gibi görmek büyük bir hatadır. Getirdiği çözümün yanında, özellikle yüksek hacimli ve düşük gecikme süresi gerektiren kurumsal uygulamalarda başa çıkması çok daha zor olabilecek yeni sorunlar yaratma potansiyeli taşır: veritabanı darboğazı, kilitlenme sorunları ve artan operasyonel karmaşıklık.
Kesinlikle kullanılamaz bir desen midir? Hayır. Düşük işlem hacimli veya gecikmenin tolere edilebildiği bazı senaryolarda hayat kurtarıcı olabilir. Ancak bir deseni sırf popüler diye veya bir sorunu “garantili” çözdüğünü iddia ettiği için kullanmak yerine, getireceği maliyetleri ve sisteminizin karakteristiğini iyi analiz etmek gerekir.
Çoğu zaman, bu deseni kullanmak yerine, idempotent (tekrarlanan işlemlere karşı güvenli) consumer’lar tasarlamak ve hataları yönetmek için daha basit mekanizmalar kurmak daha sürdürülebilir bir yol olabilir. Veya Debezium gibi araçlarla “Transaction Log Tailing” gibi daha gelişmiş ama daha az endirek yöntemleri araştırmak gerekebilir. Benim Doğuş Teknolojideki projelerimizde kullandığımız çözümümü ise başka bir makalede anlatacağım. Bu da başka bir makalenin konusu olsun :D
Unutmayın, yazılım mimarisinde kesin çözüm yoktur. Her çözüm, kendi takaslarını (trade-offs) beraberinde getirir. Önemli olan, hangi takasları yapmaya razı olduğunuzu bilmektir. Outbox Pattern, sizi bir sorundan kurtarırken, başınızı daha büyük belalara sokma potansiyeli yüksek olan, dikkatle yaklaşılması gereken güçlü bir araçtır.
Kaynaklar:
- Pattern: Transactional outbox — https://microservices.io/patterns/data/transactional-outbox.html
- Outbox Design Pattern Nedir? Nasıl Uygulanır? (Inbox Pattern Eşliğinde) — https://www.gencayyildiz.com/blog/outbox-design-pattern-nedir-nasil-uygulanir-inbox-pattern-esliginde/
- Implementing the Outbox Pattern — https://www.milanjovanovic.tech/blog/implementing-the-outbox-pattern
메타데이터
- post_id
- 09ee2a04bd85
- slug
- outbox-pattern-kurtarıcı-mı-yoksa-baş-belası-mı-09ee2a04bd85
- url
- https://medium.com/dogus-teknoloji/outbox-pattern-kurtar%C4%B1c%C4%B1-m%C4%B1-yoksa-ba%C5%9F-belas%C4%B1-m%C4%B1-09ee2a04bd85
- canonical_url
- https://medium.com/dogus-teknoloji/outbox-pattern-kurtar%C4%B1c%C4%B1-m%C4%B1-yoksa-ba%C5%9F-belas%C4%B1-m%C4%B1-09ee2a04bd85
- author_url
- https://medium.com/@alperkonuralp
- status
- ok
- fetched_at
- 2026-07-09 13:13:48