Mikroservislerde Transaction Yönetimi: Neden Kolay Değil ve Nasıl Başarılır?
Monolitikten mikroservislere geçerken transaction’ların nasıl karmaşıklaştığını, dağıtık sistemlerde tutarlılık problemlerini ve event-based yaklaşımlar ile saga desenini keşfedelim!
Monolitik dünyada transaction’lar oldukça basitti. Tek veritabanı, ACID garantileri ve hazır framework desteği sayesinde state tutarlılığını sağlamak kolaydı. Ama mikroservisler sahneye çıktığında, bu basitlik yerini karmaşık, dağınık ve zor yönetilen süreçlere bıraktı.
Neden? Çünkü mikroservislerde her servis kendi veritabanının sahibi oluyor. İşlemler parçalara bölünüyor ve servisler bağımsız çalışıyor. Bu kulağa özgürlük gibi geliyor ama transaction yönetimini tam bir kabusa çeviriyor.
Monolitik sistemlerde veri tek bir merkezdeydi; transaction’lar veritabanı seviyesinde garantilenirdi. Ama mikroservis mimarisinde, her servis kendi veritabanının sahibi oluyor. Bu, işlem tutarlılığını sağlama şeklimizi tamamen değiştiriyor ve transaction yönetimi artık uygulama seviyesinde çözülmesi gereken bir mesele haline geliyor.
Bu makalede, mikroservislerde transaction’ların neden kolay olmadığını, dağıtık sistemlerde karşılaştığımız tutarlılık problemlerini ve bunları aşmak için kullanılan temel yaklaşımları derinlemesine inceleyeceğiz.
1. Dağıtık Sistemlerde İşlem Tutarlılığı: Neden Bu Kadar Zor?
SimpleBank adında bir bankanın müşterisi olduğunu düşün, elindeki hisseyi satmak istiyorsun. İşlem aslında birkaç adımdan oluşuyor:
Sipariş oluşturuyorsun.
Uygulama stok pozisyonunu kontrol edip rezerve ediyor.
Ücret tahsil ediliyor.
Son olarak, sipariş borsaya gönderiliyor.
Müşteri olarak bu işlemin atomik olduğunu düşünüyorsun; yani tüm adımlar ya birlikte başarılı olur ya da hiç olmaz. Sahip olmadığın hisseyi satamazsın, aynı hisseyi birden fazla kere satamazsın.
Monolitik uygulamalarda bu aslında çok kolay. Tek bir veritabanı ve ACID garantileriyle, bu adımları tek bir transaction olarak sarmalayabilirsin. Hata olursa, tüm işlem geri alınır, sistem tutarlı kalır.
Burada kısaca ACID nedir, bilmeyenlere bir örnekle açıklamak isterim. Bilenlere de hatırlatma olur :)
Mesela bir banka uygulamasında para transferi yapıyorsun. Para transferi iki adımdan oluşur: Bir hesaptan para çekmek ve diğer hesaba yatırmak.
Atomiklik (Atomicity): Bu iki adım ya birlikte başarılı olur ya da hiç olmaz. Eğer para çekme gerçekleşip yatırma olmazsa, çekilen para geri iade edilir.
Tutarlılık (Consistency): İşlem sonunda tüm banka kayıtları kurallara uygun ve tutarlı kalır.
İzolasyon (Isolation): Aynı anda birçok işlem yapılırken, işlemler birbirini etkilemez.
Dayanıklılık (Durability): İşlem tamamlandıktan sonra, veriler kalıcıdır ve sistem çökse bile kaybolmaz.
Bu özellikler ACID’in temel taşlarıdır ve klasik veritabanlarında transaction yönetimini güvenilir kılar.
Ama mikroservislerde durum farklıdır. Her adım farklı servislerde gerçekleşiyor ve bu servisler kendi verilerini yönetiyor. Stok rezerve eden servis ayrı, ücret tahsil eden ayrı. Eğer bir adım başarısız olursa, örneğin ücret tahsilatı, elimizde yarım kalmış bir işlem olur. Sistem tutarsız hale gelir.
İşte bu noktada “consistent transaction” kavramı mikroservislerde çok daha karmaşık bir hal alıyor.
2. Dağıtık Transaction’lar Mikroservislerde Neden Pratik Değil?
Mikroservislerin en büyük gücü, bağımsız ve otonom servisler olmalarıdır. Her biri kendi veritabanını yönetir ve istediği teknolojiyi kullanabilir. Ancak bu da işlemleri klasik veritabanı transaction’ları gibi tek bir noktadan yönetmeyi imkansız kılar.
Birden fazla servisin katıldığı bir işlemde, tüm parçaların aynı anda başarılı olması gerekir. Ama servisler farklı sunucularda, farklı zamanlarda çalışır. Bir servis başarısız olduğunda, diğer servislerin yaptığı işlemleri geri almak (rollback) kolay değildir.
Pratikte şöyle düşünebilirsin: Bir e-ticaret sisteminde sipariş veriyorsun. Stok servisi ürünleri rezerve ediyor, ödeme servisi ücreti tahsil ediyor. Ödeme başarısız olursa, stok servisi ürünü serbest bırakmalı. Ama bu iletişim gecikirse ya da başarısız olursa, sistemde yarım kalmış, tutarsız durumlar oluşur.
Klasik transaction’ların tek veritabanı için tasarlandığını düşünürsek, mikroservislerde bunun aynısını yapmak mümkün değil. Bu yüzden transaction yönetimi uygulama seviyesine, servisler arası koordinasyona taşınır.
3. Event-Based Communication ve Eventual Consistency
Mikroservislerde transaction işi bu kadar zor olunca, bazıları olaya başka yerden bakmaya başladı. “Senkron zincirler yerine, neden event’ler üzerinden asenkron çalışmayalım?” diye sordular. Ve gerçekten, event-based communication bu mimaride devrim gibi oldu.
Servisler birbirleriyle doğrudan konuşmak yerine, event’leri yayınlar ve ilgilenenler o event’leri dinler. Böylece birbirlerine bağımlılıkları azalıyor, sistem daha esnek oluyor. Temiz!
SimpleBank hisse satışı örneği üzerinden konuşalım.
Her servis, ilgili event’leri dinleyerek ne zaman iş yapması gerektiğini bilir. Mesela;
Kullanıcı UI üzerinden bir satış talebi gönderdiğinde, uygulama “OrderRequested” event’ini yayınlar.
Sipariş servisi OrderRequested event’ini yakalar, işler ve ardından “OrderCreated” event’ini event kuyruğuna geri gönderir.
Hesap hareketleri ve ücret servisleri, bu event’i alır, işlemlerini yapar ve tamamlandığını bildiren event’ler yayınlar.
Market servisi ise, hem ücretin tahsil edildiğini hem de stokun rezerve edildiğini gösteren event’leri bekler. İki event de geldikten sonra siparişi borsaya iletir ve işlem tamamlandığında “OrderPlaced” event’ini yayınlar.
Bu yapı, erişilebilirliği önceliklendirir. Örneğin, ücret servisi geçici olarak kapalıysa sipariş servisi yine sipariş almaya devam edebilir. Ücret servisi tekrar aktif olduğunda, bekleyen event’leri işleyerek kaldığı yerden devam eder.
Ücret tahsilatı başarısız olursa, ücret servisi “ChargeFailed” event’i yayınlar.
Diğer servisler bu event’i alır ve siparişin iptali için gerekli işlemleri başlatır.
Ama tabii burası biraz “eventual consistency” dediğimiz karmaşık bir konseptin kapısı. Burada sistem “anlık değil ama zaman içinde tutarlı olacak” diyor. Başta kulağa garip geliyor, ama mikroservis dünyasında bu, işlem tutarlılığını sağlamak için oldukça pratik bir yaklaşım.
3.1 Eventual Consistency: Sadece Backend Değil, Tüm Sistem İçin Tasarım Meselesi
Eventual consistency, mikroservis dünyasının temel taşlarından biri. Ama çoğu zaman sadece backend geliştiricilerin kafasında kalıyor. Oysa bu, tüm sistemin ve ekibin anlayıp üzerine düşünmesi gereken bir konsept.
Neden mi? Çünkü sistemde bir şey değiştiğinde, bu değişikliğin her yerde “anında” görünmesi beklenmez. Belki birkaç saniye, belki daha uzun sürede tüm parçalar tutarlı hale gelir. Bu durum, frontend’den backend’e, kullanıcı deneyiminden operasyonlara kadar her katmanı etkiler.
Mesela bir kullanıcı sipariş verdi, ama stok servisi henüz güncellenmedi. Eğer UI hemen “Stokta yok” diyorsa, kullanıcı kafası karışabilir. Ya da tam tersi, sipariş verildi ama listede görünmüyorsa kullanıcı kendini güvensiz hisseder.
Bu yüzden event-based sistemlerde, tasarımcılar, geliştiriciler, QA ve operasyon ekipleri birlikte hareket etmeli. Herkes şunu bilmeli: “Veri belki hemen tutarlı değil, ama sonunda olacak.” UI bunu kullanıcıya nasıl net anlatacak? Hangi durumlarda bekletme mesajları gösterilecek? Hangi aksiyonlar kullanıcıyı şaşırtmaz?
Eventual consistency, sadece teknik bir zorluk değil, aynı zamanda takım koordinasyonu, iletişim ve kullanıcı deneyimi tasarımı meselesidir. Bu nedenle ekip içi eğitimler, doğru dokümantasyon ve açık iletişim çok önemlidir.
Unutmamak gerekir ki; mikroservis mimarisi sadece koddan ibaret değil. Bu, kültürden ve anlayıştan da geçer. Eventual consistency’yi benimsemek, tüm takımın bu belirsizliği ve zaman gecikmelerini kabullenip çözümler üretmesi demektir.
4. Saga Tasarım Deseni: Mikroservislerde Transaction Yönetimine Yeni Bakış
Mikroservislerde klasik transaction yöntemleri havada kalıyor. Çünkü artık işlemler tek bir veritabanı değil, birbirinden bağımsız birçok servis ve veri deposu arasında dağıtılmış durumda. Bu da tutarlılık ve hata yönetimini eskisi gibi kolay yapmıyor.
Saga tasarım deseni, bu karmaşayı yönetmenin pratik yolu. Temelde, birbirini tetikleyen küçük işlemler zincirinden oluşuyor. Her bir işlem kendi içinde atomik; ama bütün olarak baktığında, zincirin tamamı değil. Bir adım başarısız olursa, sistem bu durumu telafi etmek için önceki adımlara geri dönebiliyor.
Bu sayede, mikroservislerin bağımsızlığı ve erişilebilirliği korunurken, karmaşık ve uzun süreli işlemler de güvenilir şekilde yönetilebiliyor.
Saga Türlerine Kısa Bir Giriş
Saga deseni, mikroservislerde işlemleri koordine etmek için iki temel yaklaşımla uygulanabilir: choreography ve orchestration.
Choreography: Servisler, event’ler aracılığıyla birbirleriyle asenkron bir şekilde iletişim kurar. Her servis, hangi event’lere tepki vereceğini bilir ve kendi işlemlerini bağımsızca yürütür. Bu, esnek ve dağıtık bir yapı sunar, ancak koordinasyon eksikliği karmaşık senaryolarda zorluk yaratabilir.
Orchestration: Bir merkezi koordinatör (orchestrator), işlemin akışını yönetir ve hangi servisin ne zaman ne yapacağını belirler. Bu, daha kontrollü bir yaklaşım sağlar, ancak merkezi bir bileşen bağımlılığı getirir.
Her iki yaklaşımın da avantajları ve dezavantajları var. Hangi yöntemin seçileceği, sistemin ihtiyaçlarına ve ekibin tercihlerine bağlı. Bir sonraki makalede, bu saga türlerini derinlemesine inceleyerek örneklerle nasıl kullanıldıklarını keşfedeceğiz.
Saga’nın Temel İşleyişi: Küçük İşlemler, Büyük Koordinasyon
Saga, birden fazla mikroservis üzerinde gerçekleşen uzun soluklu işlemleri küçük, yönetilebilir parçalara bölerek kontrol etme yöntemidir. Her adım kendi başına atomik ve bağımsızdır; bir hata durumunda ise önceden tanımlanmış telafi (compensating) işlemleri devreye girer.
Bu yapıda, tüm işlemler tek bir dev işlem gibi değil, birbirine bağlı ama bağımsız parçalardan oluşur. Böylece, hata durumlarında sistem tamamen çökmez, sadece sorunlu adımlar düzeltilir ve sistem tutarlılığı korunur.
Gerçek hayat örneği ile anlamaya çalışalım. Bir fincan kahve satın almak. Bu işlem genellikle dört adımdan oluşur: sipariş verme, ödeme yapma, kahvenin hazırlanması ve teslimatı. Normal şartlarda müşteri, kahvesi için ödeme yapar ve sipariş ettiği kahveyi alır.
Ama her zaman işler planlandığı gibi gitmez! Kahve makinesi bozulabilir; barista cappuccino yapar ama sen flat white istemişsindir; ya da kahveni yanlış kişiye verebilirler. Böyle bir durumda, baristanın yapacağı şey doğal olarak durumu telafi etmektir: ya kahveni tekrar yapar ya da ödemen geri iade edilir. Çoğu zaman sonunda kahveni alırsın.
Özetle …
Mikroservis mimarisi, yazılım dünyasında esneklik ve ölçeklenebilirlik vaat ederken, transaction yönetimini adeta bir bulmacaya dönüştürüyor. Monolitik sistemlerin basit, ACID garantili dünyasından, dağıtık sistemlerin karmaşık ve tutarlılık gerektiren evrenine geçiş, geliştiricileri yeni yaklaşımlar aramaya itiyor.
Event-based communication ve eventual consistency, bu kaotik ortamda tutarlılığı sağlarken sistemin bağımsızlığını koruyor; saga tasarım deseni ise karmaşık işlemleri yönetilebilir adımlara bölerek güvenilirliği artırıyor. Ancak bu sadece teknik bir mesele değil; başarılı bir mikroservis yolculuğu, ekiplerin kültürel uyumunu ve kullanıcı odaklı düşünmeyi gerektiriyor.
Daha derinlemesine çözümler için, saga türlerini ve pratik uygulama stratejilerini ele alacağım bir sonraki makaleyi kaçırmayın, dağıtık sistemlerin karmaşasını fethetmek için bir adım daha atın!