Oyun Kitabı
Production'da CQRS (Command Query Responsibility Segregation): Tutarlılık, Hatalar ve Kurtarma Stratejileri (Productionda CQRS Tutarlilik Hatalar Ve Kurtarma Stratejileri)
CQRS production ortamında nasıl güvenli çalışır? Consistency lag, duplicate event, sıra bozulması, projection recovery, retry, DLQ ve Saga stratejileri.
Bu hatalar kullanıcıya nasıl görünür?
Bir banka uygulamasında bakiye iki saniye gecikirse kullanıcı paranın kaybolduğunu düşünebilir. Buna karşılık bir beğeni sayısının iki saniye gecikmesi çoğu kullanıcı için kabul edilebilir olabilir. Aynı teknik gecikme, iki farklı ürün riskidir.
Benzer şekilde aynı OrderPlaced mesajı ikinci kez gelirse stok iki kez azalabilir. Bu yüzden production'daki kavramları yalnızca tanım olarak değil, kullanıcıya ve işe etkisi üzerinden okumak gerekir.
Production'da asıl soru
Mutlu yol basittir: command kabul edilir, event yayınlanır, projection güncellenir. Production'da ise mesaj iki kez gelir, consumer geride kalır, projection bozulur veya kullanıcı yazdığı siparişi listede göremez.
Command accepted → event published → projection delayed → user refreshes
"Siparişim kayboldu mu?"
Dağıtık sistemlerde kısmi arıza istisna değildir. Bu yazı, hatayı yok etmeye değil; hata geldiğinde veri doğruluğunu, kullanıcı güvenini ve kurtarma süresini nasıl koruyacağımıza odaklanır.
Kavramlar ilk geçtiği yerde
📦 Consistency lag
Write başarılı olduktan sonra read modelin güncellenmesine kadar geçen süre.
📦 Retry
Geçici teknik hatada aynı işi kontrollü biçimde yeniden deneme.
📦 Dead-letter queue (DLQ)
Normal akışta işlenemeyen mesajların incelenmek üzere ayrıldığı kuyruk.
📦 Checkpoint
Projection'ın güvenle işlediği son event konumu.
Eventual consistency, verinin yanlış olduğu anlamına gelmez; farklı modellerin aynı gerçeğe farklı anlarda ulaşmasıdır. Bu kabul edilebiliyorsa ürün bunu kullanıcıya doğru anlatmalı, kabul edilemiyorsa kritik sorgu için farklı bir tutarlılık stratejisi seçmelidir.
Her savunma hattının bir nedeni var
- Checkpoint tutulur; çünkü replay başladığında projection'ın güvenle nereden devam edeceğini bilmek isteriz.
- Idempotency gerekir; çünkü duplicate event'in ikinci kez etkisini uygulamak stok veya ödeme gibi gerçek iş sonuçlarını bozar.
- Retry sınırlanır; çünkü bozuk bir mesajı tekrar denemek onu düzeltmez, yalnızca yükü ve yanlış etkiyi büyütebilir.
- DLQ kullanılır; çünkü normal akışta çözülemeyen mesajın sahipliğini ve incelemesini görünür kılmak gerekir.
- Partition key aggregate ID olur; çünkü aynı aggregate'in olay sırası, global olay sırasından daha önemlidir.
- Replay yapılır; çünkü projection bozulsa bile doğrulanmış event geçmişi hâlâ doğru kaynaktır.
Tutarlılık gecikmesi teknik değil, ürün kararıdır
Bir ödeme alındıktan sonra sipariş listesinde iki saniye görünmemesi kullanıcı için veri kaybı gibi hissedilebilir. Önce görünür sözleşmeyi belirleyin: İşlemden sonra hangi ekran ne kadar sürede güncel olmalı?
POST /orders → 202 Accepted + orderId + version
GET /orders/{id}?minVersion=42
→ projection 42'ye ulaştıysa güncel görünüm
→ henüz ulaşmadıysa "işleniyor" durumu
Optimistic UI, polling veya wait-for-version kullanılabilir. Bunlar lag'i sıfırlamaz; çünkü asıl iş kullanıcıya gecikmenin anlamını dürüstçe göstermektir. Kritik kararlar için command tarafının sonucu kaynak gerçek olmaya devam eder.
Duplicate event ve sıra bozulması
At-least-once teslimat aynı mesajın tekrarını normalleştirir. Aynı sipariş event'ini ikinci kez işlemek stok sayısını iki kez azaltabilir. Consumer event kimliğini ve aggregate sürümünü kontrol etmelidir.
if event.id already processed: ignore
if event.version <= projection.version: ignore
apply event
store checkpoint and event id
Bu kontrol indexed lookup ile O(1) ortalama veya O(log n) maliyet taşır. Sıralama yalnızca partition içinde garanti ediliyorsa partition key aggregate ID olmalıdır; çünkü aynı siparişin nedensel sırası global sıradan önemlidir.
Retry, DLQ ve operatörün karar anı
Her hata tekrar denenmemelidir. Ağ zaman aşımı geçici olabilir; geçersiz event şeması kalıcı hatadır.
| Hata türü | Tepki | Neden |
|---|---|---|
| Timeout / 503 | Exponential backoff ile retry | Bağımlılık toparlanabilir |
| Rate limit | Gecikmeli retry | Yükü büyütmeden toparlanmak |
| Şema doğrulama hatası | DLQ + alarm | Retry mesajı düzeltemez |
| İş kuralı ihlali | Kaydet, incele, telafi et | Otomatik retry yanlış etkiyi büyütebilir |
DLQ çöplük değildir. Her mesajın owner'ı, inceleme süresi, replay prosedürü ve alarmı olmalıdır.
Projection bozulursa nasıl kurtarılır?
Projection cache değil, yeniden üretilebilen iş görünümüdür.
1. Consumer'ı durdur veya yeni projection sürümü oluştur
2. Son güvenli checkpoint'i doğrula
3. Event stream'i kontrollü replay et
4. Sayım ve örnek veri doğrulaması yap
5. Trafiği yeni projection'a yönlendir
6. Lag ve hata oranını izle
Replay maliyeti event sayısıyla doğrusal O(n)'dir. Snapshot veya segmentli replay bunu azaltabilir; ama snapshot kaynak gerçek değildir. Çünkü kurtarmada güvenilecek kayıt, doğrulanmış event geçmişidir.
Saga'nın başarısızlık yolu
E-ticarette ödeme başarısız olduğunda stok sonsuza kadar rezerve kalmamalıdır. Saga teknik rollback değil, iş anlamı taşıyan telafidir.
Reserve inventory → Capture payment → Create shipment
payment fails → Release inventory
Telafi de idempotent olmalıdır: ReleaseInventory iki kez çalışırsa stok iki kez artmamalıdır. Choreography küçük yerel akışlarda hafiftir; orchestration çok adımlı süreçte state machine ve tek izleme noktası sağlar.
Gözlemlenebilirlik
Consumer lag, retry sayısı, DLQ derinliği, checkpoint yaşı, duplicate oranı ve uçtan uca işlem süresini ölçün. Trace ID'yi command'den event'e, consumer'a ve kullanıcıya dönen query'ye taşıyın. Alarm teknik metrikte değil kullanıcı etkisinde anlam kazanır.
Yanlış güven duygusu oluşturan eşleştirmeler
❌ Retry = güvenilirlik
✓ Retry, yalnızca geçici hatada ve idempotent işlemde güvenlidir.
❌ DLQ = kurtarma
✓ DLQ, inceleme ve replay sürecinin başlangıcıdır.
❌ Replay = her zaman güvenli
✓ Sürümleme ve yan etkiler ayrılmadan replay etkiyi tekrar üretebilir.
❌ Eventual consistency = rastgele gecikme
✓ Gecikme ölçülmeli, ürünle kabul edilmeli ve kullanıcıya açıklanmalıdır.
Production readiness kontrolü
- Kullanıcıya görünen consistency lag için hedefiniz var mı?
- Consumer duplicate ve out-of-order event'lerde güvenli mi?
- Retry politikası geçici ve kalıcı hataları ayırıyor mu?
- DLQ mesajının owner'ı ve replay runbook'u tanımlı mı?
- Projection'ı production verisi üzerinde güvenle yeniden kurabiliyor musunuz?
- Saga telafileri idempotent mi?
Bu soruların her birine kanıtla cevap veremiyorsanız sistem ölçeklenebilir görünebilir; ancak henüz işletilebilir değildir.
Bu yazıdan aklında ne kalmalı?
- Eventual consistency, kullanıcı deneyimiyle yönetilmesi gereken bir sözleşmedir.
- Duplicate ve sıra bozulması edge case değil, dağıtık teslimatın normalidir.
- Retry, DLQ ve replay birlikte tasarlanmış bir kurtarma sistemidir.
- Projection yeniden kurulabilir değilse operational debt'e dönüşür.
- Saga rollback yapmaz; iş etkisini telafi eder.
Production mimarisi, mesajlar ilk seferde doğru geldiğinde değil; iki kez, geç veya beklenmedik sırada geldiklerinde nasıl davrandığınızda görünür.
SSS
Sık sorulan sorular
Consistency lag nedir?
Write başarılı olduktan sonra read modelin güncellenmesine kadar geçen süre.
Retry nedir?
Geçici teknik hatada aynı işi kontrollü biçimde yeniden deneme.
"Retry = güvenilirlik" doğru mu?
Retry, yalnızca geçici hatada ve idempotent işlemde güvenlidir.
Bu bölüm neyi sabitler?
Dağıtık sistemlerde kısmi arıza istisna değildir. Bu yazı, hatayı yok etmeye değil; hata geldiğinde veri doğruluğunu, kullanıcı güvenini ve kurtarma süresini nasıl koruyacağımıza odaklanır. Eventual consistency, kullanıcı deneyimiyle yönetilmesi gereken bir sözleşmedir. Mutlu yol basittir: command kabul edilir, event yayınlanır, projection güncellenir. Production'da ise mesaj iki kez gelir, consumer geride kalır, projection bozulur veya kullanıcı yazdığı siparişi listede göremez.
Ogrenilen Muhendislik Prensipleri
- Tutarlılık gecikmesi, ürün tarafından kabul edilmiş ve kullanıcıya görünür bir sözleşme olmalıdır.
- Duplicate, sıra bozulması ve replay için idempotent consumer tasarımı zorunludur.
- Kurtarma; checkpoint, runbook ve gözlemlenebilirlikle önceden tasarlanmış bir yetenektir.
Okumaya devam et
Okumaya devam et
Ilgili yazilar
Dağıtık Sistemlerde CQRS (Command Query Responsibility Segregation): Event, Broker ve Projection
CQRS dağıtık sistemlerde nasıl çalışır? Domain event, message broker, projection, Outbox, idempotency ve eventual consistency kararlarını uçtan uca…
Ilgili yazilar
CQRS (Command Query Responsibility Segregation) Pipeline Nasıl Çalışır? Command ve Query Akışının Anatomisi
CQRS request pipeline nedir? Bir HTTP isteği Controller, MediatR, pipeline behavior, handler, Outbox ve read model üzerinden nasıl ilerler?
Ilgili yazilar
Production'da DDD: Dağıtık Sistemler ve Modernizasyon Stratejileri
DDD production ortamında nasıl yaşatılır? Event Storming, Saga, Transactional Outbox, Anti-Corruption Layer ve Strangler Fig ile legacy dönüşüm rehberi.