Modern yazılım mimarisinde, monolitik uygulamalardan mikro servislerine geçiş, veri tutarlılığı açısından önemli karmaşıklıklar getirmiştir. Tek bir işlem birden fazla servisi kapsadığında ve her birinin kendi veritabanı olduğunda, ya tüm adımların başarılı olması ya da tüm adımların başarısız olması sağlama altına almak monumental bir zorluk haline gelir. Dağıtık işlem olarak bilinen bu kavram, karmaşık sistemlerde veri bütünlüğünü korumak için kritik öneme sahiptir. Uygun stratejiler uygulanmazsa, veri bozulması, kayıp siparişler veya işlemleri ciddi şekilde etkileyebilecek finansal tutarsızlıklarla karşılaşma riski taşırsınız.
Temel Zorlukları Anlamak
Çözümlere geçmeden önce, dağıtık işlemlerin neden zor olduğunu anlamak esastır. Tek bir veritabanında, ACID özellikleri (Atomiklik, Tutarlılık, İzolasyon, Dayanıklılık) veritabanı motoru tarafından sorunsuz bir şekilde yönetilir. Ancak dağıtık bir ortamda bu özellikler ücretsiz gelmez. Dağıtık bir sistemin yalnızca üç özelliğinden ikisini garanti edebileceğini belirten CAP teoremi tarafından dayatılan ödünleşimlere sıkça maruz kalırsınız: Tutarlılık, Erişilebilirlik ve Bölünme Toleransı.
Ayrıca, ağ gecikmesi ve olası düğüm arızaları nedeniyle basit bir `COMMIT` komutu artık yeterli değildir. Kısmi arızaları zarif bir şekilde yöneten protokoller uygulamanız gerekir. Eğer A Servisi başarılı olur ancak B Servisi başarısız olursa, A Servisi'nin değişikliklerini geri almak için bir mekanizmaya ihtiyacınız vardır; bu sürece telafi edici işlemler denir. Bu zorlukları görmezden gelmek, verinin sistem sınırları boyunca tutarsız hale geldiği "dağıtık anti-desenlere" yol açar.
Saga Desenini Uygulama
Dağıtık işlemleri yönetmenin en sağlam yaklaşımlarından biri Saga desenidir. Bir Saga, büyük bir işlemi, her biri tek bir servis içindeki veritabanını güncelleyen yerel işlemler dizisine böler. Bir adım başarısız olursa, Saga önceki adımlar tarafından yapılan değişiklikleri geri almak için bir dizi telafi edici işlemi yürütür. Bu yaklaşım, yüksek erişilebilirliğe sahip mikro servisler için genellikle daha pratik olan güçlü tutarlılık yerine nihai tutarlılığı tercih eder.
Saga deseni, hem Senkronizasyon tabanlı (servislerin olay yayınladığı ve bunlara tepki verdiği) hem de Orkestrasyon tabanlı (merkezi bir koordinator akışı yönlendirdiği) yaklaşımlar kullanılarak uygulanabilir. Orkestrasyon yaklaşımı genellikle hata ayıklama ve bakım açısından daha kolaydır. Aşağıda, bir e-ticaret sipariş süreci için basit bir Orkestrasyon tabanlı Saga'yı gösteren Python örneği yer almaktadır:
class OrderSaga:
def __init__(self, order_service, payment_service, inventory_service):
self.order_service = order_service
self.payment_service = payment_service
self.inventory_service = inventory_service
def execute(self, order_id, user_id, items):
try:
# Adım 1: Sipariş Oluştur
order = self.order_service.create(order_id, user_id)
# Adım 2: Ödemeyi İşle
payment_status = self.payment_service.charge(order.amount, user_id)
# Adım 3: Envanteri Ayır
self.inventory_service.reserve(items)
# Tüm adımlar başarılı olursa, sipariş tamamlanır
return order
except Exception as e:
# Telafi edici işlemleri yürüt
self._compensate(order_id, items)
raise e
def _compensate(self, order_id, items):
# Adımları ters sırada geri al
self.inventory_service.release(items)
self.payment_service.refund(order_id)
self.order_service.cancel(order_id)
İki Aşamalı Onay: Geleneksel Yaklaşım
Saga deseni mikro servislerde popüler olsa da, güçlü tutarlılığın müzakere edilemez olduğu senaryolar vardır. İki Aşamalı Onay (2PC) protokolü, birden fazla düğümde atomikliği sağlayan klasik bir dağıtık konsensüs algoritmasıdır. İlk aşamada (Hazırlık), koordinator tüm katılımcılara taahhüt etmeye hazır olup olmadıklarını sorar. İkinci aşamada (Onay), tüm katılımcılar "evet" oyu verirse, koordinator bir onay mesajı gönderir; aksi takdirde bir iptal mesajı gönderir.
2PC güçlü tutarlılığı garanti etse de önemli dezavantajlara sahiptir. Bloke edicidir, yani işlem sırasında koordinator başarısız olursa, katılımcılar belirsiz bir durumda kalabilir. Ayrıca, birden fazla ağ dönüşü yüksek gecikmeye yol açar ve bu da sistem performansını etkileyebilir. Sonuç olarak, 2PC yalnızca finansal defter sistemleri veya diğer kritik veri depoları için kesinlikle gerekli olduğunda modern bulut-native uygulamalarda nadiren kullanılır.
// İki Aşamalı Onay için Sahte Kod
function twoPhaseCommit(coordinator, participants):
// 1. Aşama: Hazırlık
for participant in participants:
result = participant.prepare()
if result != READY:
coordinator.send(ABORT)
return
// 2. Aşama: Onay
coordinator.send(COMMIT)
for participant in participants:
participant.commit()
Uygulama İçin En İyi Uygulamalar
Sisteminizi tasarlarken, dağıtık işlemlerden yalnızca kesinlikle gerekli olduğunda kaçının. Bunun yerine, servislerinizi gevşek bağlı olacak şekilde tasarlayın ve nihai tutarlılığa güvenin. Durum değişikliklerini asenkron olarak yaymak için olay tabanlı mimariler kullanın. Tekrar deneme senaryolarını güvenli bir şekilde yönetebilmek için telafi edici işlemlerinizin idempotent (yinelemeye dayanıklı) olduğundan emin olun. Son olarak, otomatik tekrar denemeler başarısız olursa manuel uzlaştırma işleri çalıştırabilmeniz için tutarsızlıkları erken tespit etmek üzere sağlam izleme ve uyarı sistemleri uygulayın. 2PC gibi güçlü tutarlılık modelleri ile Saga gibi esnek modeller arasında dikkatlice seçim yaparak, etkili şekilde ölçeklenebilen dayanıklı sistemler inşa edebilirsiniz.