Distributed Transaction: Choreography ve Orchestration ile Saga Tasarımları

Distributed Transaction: Saga Designs with Choreography and Orchestration

Cihat Solak
Intertech
9 min readMar 28, 2024

--

Transaction Nedir?

Bir veya birden fazla işlemle gerçekleşen bir işlem birimidir. Örneğin bankacılık uygulamasında para yatırma veya çekme, veri tabanında ise CREATE/UPDATE/DELETE işlemleri transaction örnekleridir.

Cihat’ın vadesiz hesabından 1000 TL çekip, Mesut’un hesabına 1000 TL yatırma işlemleri iki ayrı transaction’ı temsil eder.

Distributed Transaction Nedir?

Birden fazla veri tabanının birbiriyle entegre bir şekilde çalıştığı bir senaryoyu ifade eder. Özellikle mikroservis gibi yaklaşımlarda, her servisin kendi veri tabanını yönetmesi durumu distributed transaction olarak adlandırılır.

Birden fazla mikroservis paylaşılan (shared) bir veri tabanına bağlanabilir ancak her bir mikro servisin kendisine ait veri tabanının olması ideal olan durumdur.

Mikroservis mimarisinde (E-Ticaret), sipariş oluşturma, stok düşme ve ödeme alma gibi işlemler farklı mikroservislerde gerçekleştirileceğinden, bu işlemleri koordine etmek ve tutarlılığı sağlamak için distributed transaction yönetimi gereklidir. Başarısız bir transaction durumunda, veri tutarsızlıkları meydana gelebilir; örneğin, sipariş oluşturulabilir, ödeme alınabilir, ancak ilgili ürün stoktan düşmez.

Monolith Uygulamalarda Transaction

Monolith projeler, tek bir solution ve veri tabanı üzerinde yönetilen uygulamalardır. Bu nedenle, transaction yönetimi daha basittir. Veri tabanına bağlanmak için kullanılan framework/library, bu işlemi yönetmek için API adresleri sağlar.

ACID (Atomicity, Consistency, Isolation, Durability) Nedir?

Veritabanlarında yapılan değişiklikleri yöneten prensiplere ACID denir. ACID prensipleri, verinin güncel ve tutarlı kalmasını sağlar. Tek bir veritabanında ACID uygulamak, diğer yöntemlere göre daha kolaydır.

Atomocity: Ya hep ya da hiç!

Tüm transaction’ların başarılı olması gereklidir; bir hata durumunda ise hiçbiri uygulanmaz. Bu, veri tutarlılığını sağlar.

Consistency: Verilerin tutarlı olması. Veri tabanını sürekli valid (geçerli) tutar.

Servislerdeki veri tabanları bütünsel açıdan anlamlı ve tutarlı olmalıdır.

Isolation: Transactionların birbirinden bağımsız olmasını ifade eder.

Transactionlar birbirinden tamamen soyutlanmışdır. Bir transaction işini bitirmeden bir başka transaction işleme giremez. Transactionlar birbirinden soyutlanmış durumda olduğundan dolayı veri tutarlılığı sağlanmaktadır.

Durability: Verilerin güvenli bir ortamda saklanmasını ifade eder.

Transaction işlemi bittiğinde, veri tabanında yeni bir satır oluşur. Oluşan yeni satır fiziksel diske kaydedildiği için veri tutarlılığı sağlanmış olur.

Mikroservisler Arasındaki İletişim Türleri

Senkron ve asenkron olmak üzere iki farklı iletişim bulunmaktadır. Senkron iletişim mikroservislerin birbirleri arasında point-to-point yani endpointler üzerinden haberleşmesidir.

Örneğin A servisi, üstüne düşen görevi gerçekleştirdikten sonra B servisinin endpoint’ine istek yaparak haber vermektedir. Bu senkron iletişimdir ve mümkün olduğunca tercih edilmemelidir. Çünkü A servisinden B servisine istek yapıldığı esnada B servisi ayakta değilse yapılan istek boşa gitmiş/kaybolmuş olacaktır. Buradaki riski azaltmak adına retry mekanizmaları kurgulanabilir fakat yine de tehlike arz edecektir.

Saga Design Pattern

Saga, bağımsız servislerdeki birden fazla transaction’ı sistemli bir şekilde işleyerek veri tutarlılığını sağlayan bir design pattern’dir. İlk transaction dış bir etkiyle (örneğin kullanıcı tarafından butona tıklama) tetiklendikten sonra, bir önceki servisteki başarılı bir transaction’a bağlı olarak diğer servislerdeki işlemleri tetikler.

Events/Choreography-based Saga (Koreografi Tabanlı) ve Command/Orchestration-based Saga (Orkestrasyon Tabanlı) olmak üzere 2 farklı uygulama yöntemi bulunmaktadır.

1- Choreography-based Saga

Her servis, hem dinleyici (consumer) hem de gönderici (publisher) rolündedir. Örneğin, 4 mikroservis içeren bir mimaride bir request, ilgili servislerin tamamından geçer. Birden fazla servis işe dahil olduğunda distributed transaction kavramı devreye girer.

1️⃣ 2 ile 4 servis arasında bir distributed transaction yönetimi için uygun bir yöntemdir.

Distributed transaction yönetimine dahil olan mikroservis sayısı 4'ten fazlaysa, orchestration based saga uygulaması önerilir.

2️⃣ Transaction yönetiminin merkezi olmadığı için performans darboğazı (bottleneck) azalır.

Message broker kullanarak iletişim, performansa katkı sağlar. Örneğin, Service 3'ü dinleyen uygulamadan ihtiyaca göre 5 veya 10 instance olabilir.

3️⃣ Uçtan uça (point-to-point) iletişim olmadığından servisler arasında bağımlılık (coupling) azalır.

Her mikroservis, diğerleriyle doğrudan iletişim kurmaz. İletişim, message broker aracılığıyla sağlanır. Dolayısıyla point-to-point iletişim olmaması anlamına gelir. Bu sayede mikroservisler birbirinden habersizdir; bir servis kuyruğa event/message gönderir, ancak işleme kimin ya da kimlerin alacağını bilmez.

4️⃣ Her servis kuyruğu dinler. Gelen event/message ile ilgili işlemi yapar ve sonucu message broker’a bildirir.

Servisler, kendi kuyruklarını dinlemeli ve işlemlerinin sonuçlarını event/message ile bildirmelidir. Hem publisher (gönderici) hem de consumer (tüketici) olarak rol almalıdır.

5️⃣ Choreography yöntemini uygulamanın bir yolu asynchronous messaging patterns kullanmaktadır.

Senkron iletişim veri tutarlığı konusunda risk içermektedir.

6️⃣ Mimari içerisindeki servisler başarılı/başarısız karar vericidir.

Her bir mikro servis başarılı ya da başarısız durumu message broker’a bildirmelidir. Örneğin müşterinin hesabından transfer amacıyla para çekildiği fakat transfer edilecek müşteriye iletilemediyse işlemin başarısız olduğuna dair event/message gönderilmelidir.

7️⃣ Local transaction sırasını kullanarak transaction yönetimi sağlar.

Tüm servisler izole veri tabanıyla ilgili transaction yönetimini sağlamaktadır.

Choreography tasarımında merkezi bir yönetim yoktur; servisler asenkron iletişimle message broker üzerinden haberleşir. Örneğin, Service 1'in görevi tamamlandığında, Service 2'nin endpoint’ine istek yapmak yerine, message broker’a event göndererek iletişim kurar. Tüm iletişim, message broker üzerinden gerçekleşir. Lineer olmayan durumlarda, Service 2 görevini tamamlayıp message broker’a bildirdiğinde, Service 3 ve Service 4 aynı anda paralel olarak başlayabilir. Bu sayede birden fazla servis paralel işlem yapabilir.

Saga tasarım uygulamasında zorunlu olmasa bile mümkün olduğu sürece message broker aracılığıyla asenkron iletişim tercih edilmelidir.

Choreography, mikroservis sayısı az olduğunda basit ve gereksiz karmaşıklık olmadan kullanılabilir. Ancak mikroservis sayısı 4'ten fazlaysa, çok sayıda kuyruğu yönetmek zor olabilir. Bu durumda, orkestrasyon tabanlı bir yaklaşım daha uygun olabilir.

1.1 — Compensable Transaction

Bir transaction’ın yapmış olduğu işlemi tersini alan transactionlara denir.

Örneğin, Order Service sipariş oluşturduktan sonra Stock Service stok düşer. Ancak Payment Service’te bakiye yetersiz olduğu için ödeme alınamaz, bu durumda bir PaymentFailedEvent gönderilir. Stock Service, bu event’i dinleyerek stokta meydana gelen değişikliği geri alır. Bu işleme compensable transaction veya telafi işlemi denir. Bu durumda Payment Service’e ait compensable transaction, müşterinin hesabından çekilen paranın herhangi bir hata meydana geldiği için hesaba geri iade edilmesidir. Zıt bir transaction işlemidir.

Compensable transactionlar yönetilemediği senaryolarda veri tutarsızlığı (data inconsistency) meydana gelecektir.

2- Orchestration-based Saga

1️⃣ Mikroservisler arasındaki tüm transactionlar Saga State Machine dediğimiz merkezi konumdan yönetilmektedir.

Saga state machine, mikroservislerden ayrı tutulmalıdır. Örneğin, worker service kullanarak saga state machine oluşturulabilir ve tüm transactionlar bu şekilde yönetilebilir.

2️⃣ Transaction, merkezi konumdan yönetildiği için performans darboğazı (bottleneck) fazladır.

Compensable transaction yönetimi dahil olmak üzere bir sonraki adıma geçmek için fırlatılacak event/message yönetiminden saga state machine sorumludur.

3️⃣ Asenkron mesajlaşma deseni (asynchronous messaging patterns) kullanmak uygundur.

Asenkron iletişim tercih edileceği için mimaride message broker bulunmalıdır.

4️⃣ 4'den fazla mikroservice arasında distributed transaction yönetimi için uygun yöntemdir.

4'ten fazla mikroservis varsa Orchestration, 4'ten az ise duruma göre Choreography kullanılmalıdır.

State Nedir? Automatonymous Nedir?

State pattern, sistemin anlık durumunu ifade eder ve state problemleri için özel patternlar içerir. Bu sayede uygulama içinde state’leri yönetmek ve kolayca geçiş yapmak mümkündür.

Örneğin, bir otomatının sakız satma işlemlerini başarıyla yöneten state makinesinden bahsedelim.

Başlangıç Durumu (Ready): Kullanıcı, sakız almak için hazır durumda bekliyor. Sistem madeni para almayı bekliyor.

Alma Durumu (Accepting): Kullanıcı 1 TL atarsa, sistem sakız verir ve işlem tamamlanır.

Geçersiz Durumu (Invalid): Kullanıcı 50 kuruş atarsa, sistem geçersiz duruma geçer, 50 kuruşu geri verir ve kullanıcıya “En az 1 TL atmalısınız” uyarısı yapar.

Tamamlandı Durumu (Completed): Kullanıcı sakız aldıktan sonra sistem tekrar başlangıç durumuna geçer ve yeni bozuk para almayı bekler.

MassTransit kolayca Saga Orchestraction implementasyonunu gerçekleştirmemize imkan verir. Ayrıca state işlemleri için MassTransit içerisinde varsayılan (default) olarak gelen Automatonymous adında açık kaynak kütüphane kullanır.

Automatonymous kütüphanesi içerisinde Initial ve Final olmak üzere 2 adet varsayılan state barındırır. Çünkü state’inde mutlaka başlangıç ve bitişi olmak zorundadır.

Automatonymous kütüphanesi kullanımı zorunlu değil, State pattern kullanarak çözüm sağlanabilir. Ancak Automatonymous kütüphanesi, hız ve fayda sağlamak adına tercih edilebilir.

Genellikle web uygulamalarında “state” kavramına sık rastlanmaz. Bu terim genellikle otomasyon ve mobil uygulamalarda önem kazanır.

Örneğin, bir bankamatik sırasında beklediğinizi düşünün. Önceki kullanıcı işlemini tamamladıktan sonra siz sıraya geçersiniz ve banka işlemlerinizi gerçekleştirmek için beklersiniz. Bu süre zarfında ekranın belirli bir duruma gelmesini beklersiniz. İşte o anda, sistemdeki durum (state) sıfırlanır ve kredi kartınızı kullanmaya hazır hale gelir. Ancak, bu duruma ulaşmak biraz zaman alabilir çünkü bir dizi işlem tamamlanmalıdır. Her kullanıcının işlemi tamamlandığında, sistem başlangıç durumuna sıfırlanır.

Ancak, orkestrasyon tabanlı bir sistemde durumları takip etmek daha karmaşıktır. Örneğin, saniyede onlarca istek alabilirsiniz. Her istek farklı bir kullanıcıya ait olabilir ve her bir kullanıcının işlemi farklı bir aşamada olabilir. Örneğin, Ahmet’in gönderdiği siparişin stok rezerve edilme aşamasında olabilirken, Hasan’ın sipariş oluşturma isteği ödeme aşamasında olabilir.

Bu durumda, her kullanıcının durumunu ayrı ayrı takip etmek gereklidir. Ayrıca, her kullanıcının kredi kartı bilgileri farklıdır ve her bir işlem başarısızlık durumunda farklı şekilde ele alınmalıdır. Bu, monolitik bir sistemden farklı olarak daha fazla koordinasyon ve yönetim gerektirir, özellikle de yüksek talep ve hızlı işlem gerektiren bir ortamda.

Orchestration tasarımında her servisin, diğer servislerle ilgili bilgiye ihtiyaç duymadan çalışabilmesi, bu nedenle Separation of Concerns ilkesinin geçerli olduğu anlamına gelir.

BONUS ZAMANI

Message Broker Avantajı

Sektörde ActiveMQ, Kafka, RabbitMQ, Microsoft Azure Service Bus, ZeroMQ vb. gibi oldukça fazla çeşit message broker kullanılmaktadır.

Müşteri sipariş oluşturduğunda, stock service stoktan düşer ve payment service ödemeyi alır. Bu sırada email service kısa süreli bir kesinti yaşasa da, diğer servisler işlemlerini tamamlayarak bilgiyi message broker aracılığıyla iletilir. Email service tekrar aktif olduğunda, message broker’dan aldığı bilgilerle işlemlerine kaldığı yerden devam eder.

Diğer bir faydası.. stock servisinde yoğunluk varsa, stock service instance’larını arttırmak gerekir. Birden fazla instance, kuyruktaki mesajları dinleyerek event/message gönderir. Sonuç olarak performansa katkı sağlanır.

Message broker kullanılan mimari de consumer/publisher sayılarınızı dengelemenize bağlı olarak darboğaz yaşama ihtimali bulunmamaktadır.

MassTransit

Event ve Message Arasındaki Fark Nedir?

Event, geçmiş olayları, geçmiş zamanı ifade etmektedir. Örneğin siparişin veri tabanında oluştuğu konusunda bilgi için OrderCreatedEvent isimlendirmesi kullanılır.

Message ise gelecek olayları ifade etmektedir. Örneğin müşteri kayıt olduğunda hoş geldin mesajı göndermek için SendWelcomeEmailMessage isimlendirmesi kullanılabilir.

IPublishEndpoint ile ISendEndpoint Arasındaki Fark Nedir?

Temel fark, mesajların nasıl iletilip işlendiği ve alıcılara nasıl dağıtıldığıdır.

IPublishEndpoint, bir olayı (event) bir exchange yaymak için kullanılır. Örneğin, RabbitMQ’ya bir olay gönderdiğinizde, bu olay doğrudan takıma gönderilir. Ancak, bu yayınlanan olaya kimlerin abone olduğunu önceden bilemezsiniz. Yani, bu olayı kimlerin dinleyeceğini bilmeden yayınlarsınız. Eğer olayı dinleyen bir servis veya uygulama yoksa, gönderdiğiniz olay boşa gider.

OrderCreatedEvent orderCreatedEvent = new()
{
OrderId = 1
};

await _publishEndpoint.Publish(orderCreatedEvent);

ISendEndpoint, bir mesajı doğrudan belirli bir kuyruğa göndermek için kullanılır. Bu, mesajın bir hedefe, yani belirli bir kuyruğa doğrudan iletilmesi anlamına gelir. Yani, mesajınız doğrudan belirli bir hedefe gitmektedir, o hedefe abone olan bir servis veya uygulama olmalıdır. Başka bir deyişle, doğrudan kuyruğa gönderilen bir mesajı almak istiyorsanız, o kuyruğa abone olmanız gerekir. IPublishEndpoint’te olduğu gibi olayın dinleyicisi olup olmadığını kontrol etme ihtiyacı yoktur, çünkü mesaj doğrudan belirli bir kuyruğa gitmektedir.

var sendEndpoint = await _sendEndpointProvider.GetSendEndpoint(new Uri($"queue:customer-email"));

SendWelcomeEmailMessage sendWelcomeEmailMessage = new()
{
CustomerId = 1
};

await sendEndpoint.Send(sendWelcomeEmailMessage);

Sonuç olarak, IPublishEndpoint olayları geniş bir kitleye duyurmak için kullanılırken, ISendEndpoint doğrudan belirli bir hedefe mesaj göndermek için kullanılır ve bu nedenle, bu hedefe abone olan bir alıcı olmalıdır. Bu iki yöntem, mesaj iletimi ve dağıtımı için farklı senaryolara hizmet eder.

--

--