Telafi İşlemleri (Compensating Transactions) Tasarımı
Mikroservis mimarisinde her servis kendi veritabanına sahip olduğu için veri bütünlüğü sorunları beklenen hale gelir. Çok servisli işlem yönetimi bu noktada temel bir gereksinim olarak öne çıkar. Saga deseni deseni, uzun soluklu işlemleri bağımsız transaction'lara bölerek zincirleme biçimde yürütür. Her adım sorunsuz olursa süreç tamamlanır. Bir adım başarısız olursa tamamlanan işlemler telafi işlemleriyle geri alınır. Koreografi tabanlı saga'larda servisler birbirini olaylar üzerinden tetikler. Merkezi kontrol tabanlı yaklaşımda ise bir koordinatör adımların sırasını belirler. Her iki yöntemin de kendine özgü avantajları ve zorlukları vardır. Telafi işlemlerinin idempotent tasarlanması vazgeçilmezdir. Ağ gecikmeleri nedeniyle olaylar birden fazla kez işlenebilir. Bu nedenle her adım için benzersiz bir kimlik tutulmalı. Saga'nın takibi de ayrı bir özen gerektirir. Dağıtık izleme araçları bu akışı görünür kılar.
Olay tabanlı veri senkronizasyonu, servisler arasında zayıf bağlılığı koruyarak veri akışını mümkün kılar. Bir servis durumunu değiştirdiğinde bir olay yayınlar. Diğer servisler bu olayı dinleyerek kendi yerel kopyalarını yeniler. Mesaj aracıları bu iletimi güvenilir şekilde sağlar. Kafka gibi altyapılar olayları kalıcı biçimde saklayarak tekrar oynatmaya izin verir. Bu özellik, yeni bir tüketici eklendiğinde geçmiş verinin aktarılmasını basitleştirir. Event sıralaması ise özel bir mesele olarak dikkate alınmalıdır. Aynı varlığa ait olaylar yanlış düzende işlenirse tutarsız durumlar oluşur. Bu sebeple bölüm anahtarları doğru seçilmelidir. Dead letter kuyrukları işlenemeyen mesajları ayırarak sistemin çalışmasını korur. Yeniden deneme politikaları kademeli geri çekilme ile uygulanmalıdır. Operasyonel olarak olay gecikmelerinin izlenmesi önemlidir.
Eventual consistency kavramı, değişikliklerin bütün kopyalara sınırlı bir süre içinde yayılacağını öngörür. Anlık tutarlılığın feda edildiği bu yaklaşım yüksek kullanılabilirlik sağlar. Son kullanıcılar bazen eski veri görebilir. Bu durum birçok senaryo için tolere edilebilir. Bankacılık gibi alanlarda ise daha sıkı garantiler şart olabilir. Okuma sırasında farklılığı azaltmak için okuma onarımı yöntemleri kullanılabilir. Arka planda çalışan süreçler de sapmaları zamanla giderir. Sürüm vektörleri çakışmaları tespit için tercih edilir. Son yazan kazanır gibi basit çözüm yaklaşımları her durumda uygun olmayabilir. Domain kuralına dayalı birleştirme çok doğru çözümler verir. Geliştirici ekipleri bu yaklaşımı ürün deneyimiyle birlikte tasarlamalıdır.
İki aşamalı commit protokolü, dağıtık işlemlerde atomikliği sağlamak için klasik bir yöntemdir. Koordinatör önce tüm katılımcılara hazır olup olmadıklarını sorar. Dahil olan servisler hazırsa kaynakları tutarak onay verir. Merkezi bileşen hepsinden olumlu yanıt alırsa kayıt komutunu gönderir. Ancak koordinatör çöktüğünde bağlı servisler uzun süre kilitli kalabilir. Bu durum sistemde önemli bir darboğaz yaratır. Bağlantı kopmaları da yöntemin güvenilirliğini azaltır. Büyüme kapasitesi açısından bu yaklaşım kısıtlı kalır. Yüksek gecikmeli ortamlarda verim ciddi biçimde düşer. Bu nedenle modern mikroservis sistemlerinde genellikle daha esnek desenler seçilir edilir. Yine de bazı sınırlı durumlarda yöntem hâlâ faydalıdır.
Servisler arası veri paylaşımı, mikroservis sistemlerin en çok tartışılan konularından biridir. Senkron API çağrıları kolay olsa da zincirleme bağımlılıklar yaratabilir. Bir servis yavaşladığında tüm zincir etkilenir. Asenkron mesajlaşma bu tehlikeyi azaltır. Veri çoğaltma ile modüller ihtiyaç duydukları verileri yerel olarak tutar. Fakat bu senaryoda tutarlılık yükü artar. Ortak veritabanı deseni hızlı çözüm sağlar. Lakin servisler arasında güçlü bağ yaratır. Bu da bağımsız dağıtımı zorlaştırır. Backend for frontend gibi yapılar farklı istemcilerin ihtiyaçlarını karşılar. Kontrat odaklı geliştirme ile API değişiklikleri planlı şekilde yönetilir. Uygun strateji, ürün gereksinimlerine ve organizasyon yapısına bağlı seçilmelidir.
Idempotent mesaj işleme teknikleri, mikroservislerde aynı olayın birden fazla kez teslim edilmesi durumunda sistem tutarlılığını koruyabilmek için vazgeçilmez bir yaklaşımdır. Ağ kesintileri, yeniden deneme mekanizmaları ve broker kaynaklı tekrar teslimler sıkça görülen olaylardır. Bu nedenle her mesajın benzersiz bir kimlik ile işaretlenmesi ve işlenmiş mesajların bir tabloda kaydedilmesi önerilir. Tüketici servis, yeni bir mesaj geldiğinde önce bu kayda bakarak daha önce işlenip işlenmediğini kontrol eder. Böylece aynı iş mantığı tekrar tetiklenmez. Doğal kontrol ilkesi olan koşullu güncellemeler de bu ihtiyaca güçlü biçimde destek olur. Örneğin bir sipariş durumunu güncellerken mevcut sürüm numarasının eşleşmesi şartı aranabilir. Mesaj işleme ile kayıt güncellemesinin aynı yerel işlem içinde tamamlanması ise atomikliği güvence altına alır. Idempotency anahtarları belirli bir zaman aralığı sonra temizlenebilir, ancak bu süre yeniden teslim olasılığına göre belirlenmelidir. Test aşamasında aynı mesajın birden fazla kez gönderildiği senaryolar mutlaka denenmeli edilmelidir. Bu sayede beklenmeyen yan etkilerin üretim ortamına sızması engellenir. Sonuç olarak idempotent tüketiciler, dağıtık sistemlerde sağlamlığı artıran temel bir yapı taşıdır.
Outbox deseni ile güvenilir olay yayını, bir servisin veritabanı değişikliği ile olay yayınlama işlemini birlikte gerçekleştirmesini sağlayan yaygın bir yöntemdir. Klasik yaklaşımda veritabanına yazma ve mesaj broker'ına gönderme ayrı aşamalar olduğundan, biri başarısız olduğunda sistem tutarsız konuma düşebilir. Outbox tablosu bu sorunu, olayın aynı transaction içinde iş verisiyle birlikte kaydedilmesi ile çözer. Ayrı bir yayıncı süreç ya da CDC tabanlı bileşen bu tablodaki kayıtları okuyup broker'a gönderir. Yayın başarıyla gerçekleştiğinde ilgili kayıt işaretlenir ya da silinir. Bu yaklaşım at-least-once teslim güvencesi sunduğundan tüketici tarafında idempotency zorunlu hale gelir. Olay sıralamasının korunması için aynı varlığa ait olayların aynı partisyon anahtarıyla yayınlanması önemlidir. Outbox tablosunun büyümesi performans sorunlarına yol açabileceğinden düzenli temizlik politikası tanımlanmalıdır. Zaman damgası, olay türü ve yük alanlarının sabit bir şemayla tutulması bakım açısından faydalıdır. Ayrıca yayınlayıcının çökmesi durumunda kayıtların kaybolmadan işlenmeye devam etmesi tasarlanmalıdır. Bu desen, özellikle sipariş gibi kritik alanlarda güvenilir entegrasyonun omurgasını oluşturur.
Dağıtık kilitleme ve eşzamanlılık kontrolü, birden fazla servis örneğinin aynı kaynağa aynı anda erişmesinden doğabilecek tutarsızlıkları önlemek için zorunludur. Redis tabanlı kilitler, ZooKeeper veya etcd gibi koordinasyon servisleri bu amaçla sıkça tercih edilir araçlardır. Kilit ömrü tanımlanırken işlemin en uzun makul süresi göz önünde bulundurulmalıdır. Aksi halde kilit sahibi çökerse kaynak gereksiz yere kilitli kalabilir. Fencing token kullanımı, süresi dolmuş bir kilit sahibinin bayat istek göndermesini engellemek için önemli bir yöntemdir. İyimser eşzamanlılık kontrolü ise sürüm numarası ya da zaman damgası kıyaslayarak çakışmaları yalnızca güncelleme anında belirler. Çekişmenin az olduğu senaryolarda bu yaklaşım kilitlemeye göre daha hızlı çalışır. Yüksek çekişmeli kaynaklarda ise pesimistik kilitleme daha kararlı sonuçlar üretebilir. Kilit kullanımı sistemin genel yanıt süresini artırabileceğinden her kaynak için dikkatli tasarım yapılmalıdır. Hata durumlarında kilitlerin güvenli biçimde serbest bırakılması ve izlenmesi operasyonel dayanıklılık için şarttır.
Veri çoğaltma ve tutarlılık modelleri, dağıtık veritabanlarında verinin farklı düğümler arasında hangi biçimde senkronize edileceğini belirleyen temel kavramlardır. Senkron çoğaltmada yazma işlemi tüm replikalar onaylanana kadar bekler, bu da tutarlılığı güçlendirirken gecikmeyi artırır. Asenkron çoğaltmada ise birincil düğüm yazmayı hemen tamamlar, replikalar daha sonra yetişir. Bu model daha yüksek performans sunsa da replika gecikmesi nedeniyle eski veri okunabilir. Güçlü tutarlılık, her okumanın en son yazılan değeri döndürmesini garanti eder. Nihai tutarlılık ise sistemin belirli bir süre sonunda tüm düğümlerin aynı duruma ulaşacağını vaat eder. CAP teoremi, ağ bölünmesi anında tutarlılık ile erişilebilirlik arasında bir denge yapılması gerektiğini ortaya koyar. Servis sınırlarını aşan verilerde genellikle nihai tutarlılık kabul edilebilir bulunur. Okuma-yazma kuorumları ve vektör saatleri gibi mekanizmalar çakışan güncellemelerin ele alınmasında yardımcı olur. Hangi modelin benimseneceği iş gereksinimlerine ve kabul edilebilir gecikme toleranslarına göre belirlenmelidir.
Hata telafisi ve kompanzasyon işlemleri, dağıtık işlemlerde bir adım başarısız olduğunda önceki adımların etkisini geri çevirmek için tasarlanmış mekanizmalardır. Klasik iki aşamalı commit protokolleri mikroservislerde esneklik açısından sınırlayıcı olabildiğinden Saga deseni öne çıkan bir alternatiftir. Saga, her yerel işlemi bir kompanzasyon işlemiyle eşleştirerek ilerler. Koreografi tabanlı sagalarda servisler birbirlerinin olaylarını dinleyerek sürecin sonraki adımına ilerler. Orkestrasyon tabanlı sagalarda ise merkezi bir koordinatör adımların sırasını yönetir. Kompanzasyon işlemleri birebir anlamda geri alma değildir; örneğin gönderilmiş bir e-posta geri çekilemez, yalnızca düzeltici bir bildirim iletilebilir. Her kompanzasyonun da idempotent tasarlanması gerekir, çünkü kendisi de yeniden tetiklenebilir. Zaman aşımı, yeniden deneme kuralları ve ölü harf kuyrukları başarısız adımların yönetilmesi için gerekli bileşenlerdir. İşlem durumunun kalıcı olarak tutulması sistemin çökme sonrasında kaldığı yerden ilerlemesini sağlar. Sonuçta iyi tasarlanmış kompanzasyon mekanizmaları, dağıtık sistemlerde iş tutarlılığını korumanın en güvenilir yollarından biridir.
Mikroservislerde Veri Tutarlılığının Temelleri
Mikroservis yapılarında servisler genelde bağımsız veri depolarıyla çalışır, bu yaklaşım ölçeklenebilirlik ve bağımsız dağıtım avantajı sağlar. Ancak dağıtık veri ortamında bir işlemin birden fazla servise yayılması, tutarlılığın korunmasını zorlaştırır. Geleneksel işlem garantileri yerine çoğunlukla nihai tutarlılık modeli alır. Bu anlayış, kısa süreli senkronizasyon gecikmelerini doğal görür. Mimari kararlar verilirken bu dengenin iyi kurulması gerekir. Ekipler, hangi verinin anlık, hangi verinin gecikmeli tutarlı olması gerektiğini net biçimde belirlemelidir. Teknik ve iş gereksinimlerinin örtüşmesi, sistemin sürdürülebilirliğini artırır. Bu başlıkta sorunun kaynağını ve temel terimleri ele alacağız.
Mikroservislerde Yaygın Tutarlılık Hataları
Birçok proje, servisler arası doğrudan ve eş zamanlı çağrılara dayanarak kurulur, bu tercih, bir noktadaki gecikmenin kaskad hatalara dönüşmesine sebep olabilir. Bir başka tipik sorun, iki aşamalı commit'in her durumda en iyi çözüm sanılmasıdır, oysa bu protokol performansı düşürür ve kilitlenme riskini artırır. Mesajların tekrar işlenmesi ve idempotent olmayan operasyonlar da sık karşılaşılan sorunlardır. Olay yayınlama ile veritabanı güncellemesinin ayrı transaction'larda yapılması, veri kaybına veya uyumsuzluğa neden olabilir. Bu tür aksaklıkların kökeni çoğunlukla kısa vadeli ve basit görünen kararlara dayanır.
Deneyimli Mühendislerden Pratik Öneriler
Profesyonel ekipler genellikle Saga yaklaşımını öncelikli tercih eder, zira her servis kendi işlemini yürütür ve hata durumunda telafi eylemi tanımlanır. Uzmanlar, olayları bir outbox tablosunda saklayıp ayrıca yayınlamanın güvenilir sonuç verdiğini belirtir. Idempotency anahtarları kullanılarak çift işlemenin önlenmesi, deneyimli ekiplerin ortak tavsiyesidir. İzleme altyapısının güçlü kurulması, olası sapmaların geç kalmadan görülmesine yardımcı olur. Gereksinimlerin baştan doğru tanımlanması, uzun vadede önemli maliyet tasarrufu sağlar.
Tutarlılığın Finansal Yönleri
Çözüm bütçesi, kullanılan mimari kalıba ve ekip deneyimine bağlı olarak ciddi değişkenlik taşır, ara katman bileşenleri hem kurulum hem de operasyon maliyetini yükseltir. Ancak bu yatırımın getirisi, veri hatalarından kaynaklanan müşteri kaybı ve düzeltme maliyetleriyle kıyaslandığında genellikle çok daha yüksektir. Maliyet kontrolü açısından, tüm sistemi aynı sıkılıkta korumak yerine kritik akışlara öncelik verilmesi tavsiye edilir. Bulut sağlayıcılarının sunduğu hazır servisler, kurulum süresini ve harcamayı kısaltabilir. Bununla birlikte, uzun dönemli operasyon ve insan kaynağı maliyetlerinin hesaba katılması şarttır.
Geçiş Döneminde Zamanlama Stratejileri
Tutarlılık mekanizmalarının devreye alınması, trafiğin düşük olduğu dönemlere planlandığında risk önemli ölçüde azalır, çünkü veri senkronizasyonu sırasında geçici tutarsızlıklar yaşanabilir. Canlıya alınmadan önce, gerçek veriye yakın koşullarda kapsamlı doğrulama yürütülmelidir. Aşamalı dağıtım yöntemleri, yeni yapının yalnızca bir kısmına önce uygulanmasına imkân tanır. Ekiplerin nöbet ve destek planlarını, kritik zaman dilimlerini kapsayacak biçimde hazırlaması da önemlidir. Bakım pencereleri de iyi duyurulmalı ve paydaşlarla önceden koordine edilmelidir.
Tutarlılık Hakkında Sıkça Sorulanlar
Tüm sistemlerde gevşek tutarlılık yaklaşımı mı benimsenmelidir, hayır, ödeme ve stok gibi kritik akışlarda güçlü tutarlılık çoğu zaman zorunludur. Okuyucular çoğunlukla iki aşamalı commit ile Saga deseni arasındaki farkı merak eder, Saga, uzun süren işlemleri yerel adımlara bölerek telafi mekanizması ile yönetirken, klasik yöntem kilitlere dayanır. Ekipler ayrıca olayların sırasının nasıl korunacağını da sıkça sorar, partition key kullanımı ve sıralı tüketim mekanizmaları bu sorunu büyük ölçüde çözer. Bir sapma yakalandığında nasıl müdahale edileceği, sıkça karşılaşılan bir merak konusudur, sorunun temel nedeni incelenmeli, akabinde telafi eylemleri ve mutabakat mekanizmaları işletilmelidir.