Dağıtık Transaction — 2PC Neden Nadiren, Saga Neden Sık
Önce şunu oku: Servis Sınırları — Bounded Context ve Database-per-Service , Saga Pattern
30 saniyede özet
Bir @Transactional yalnızca kendi veritabanını geri alır. Two-phase commit iki veritabanını tek karara bağlar, ama koordinatör düşerse herkes kilitli bekler. Mikroservisler bu yüzden çoğu zaman saga ve outbox'ı seçer.
İki arkadaş birlikte bir eve taşınmaya karar veriyor: ikisi de “tamam” demeden kimse eski evinin anahtarını bırakmamalı. Peki biri cevap vermeden ortadan kaybolursa, öteki ne kadar bekler?
-
Bayt: Saga çok adım istiyor. İki veritabanını tek bir transaction'a bağlasak olmaz mı?
-
Sen: Öyle bir şey gerçekten var mı?
-
Bayt: Var! Veritabanlarının eski bir numarası: önce herkese sor, sonra karar ver.
-
Bayt: Ama bir bedeli var. Bugün o bedeli birlikte ölçeceğiz.
Satır satır: @Transactional nereye kadar uzanır
Sipariş servisi siparişi kendi veritabanına yazıyor, parayı ise Payment servisine HTTP ile çektiriyor. Metodun üstünde @Transactional var; güvende miyiz?
Rollback nereye kadar ulaşır?
@Transactionalpublic void placeOrder(Order order) { orders.save(order); paymentClient.charge(order.id(), order.total()); inventoryClient.reserve(order.id(), order.sku());}Debug
Order Sipariş yazıldı ama commit edilmedi. Transaction yalnızca Order veritabanının bağlantısına bağlı.
- Order DB
- = sipariş (commit yok)
Sol/sağ ok tuşlarıyla da gezebilirsin.
@Transactional tek bir veritabanı bağlantısını yönetir. Başka bir servisin commit ettiği iş bir ağ çağrısının ardındadır ve rollback ona ulaşmaz.
Database-per-service kuralı yüzünden bu istisna değil, kuraldır.
Kafam karıştı, daha basit anlat
Rollback yalnızca kendi defterindeki satırları siler. Başka bir servisin kendi defterine çoktan yazdığı satıra dokunamaz.
Sipariş servisindeki `@Transactional` bir metot önce siparişi kendi veritabanına kaydediyor, sonra Payment servisini HTTP ile çağırıp parayı çektiriyor, en sonda bir exception fırlatıyor. Ne olur?
Two-phase commit: önce söz, sonra karar
two-phase commitBirden çok veritabanını tek transaction gibi bitiren protokol: koordinatör önce herkese 'hazır mısın?' diye sorar, hepsi evet derse COMMIT, biri hayır derse ABORT gönderir.Sözlükte gör → (2PC) işi bir koordinatöre verir. Birinci aşamada herkese PREPARE gönderir: işini yap, satırlarını kilitle, diske yaz ve “commit edebilirim” diye söz ver.
İkinci aşamada kararı bildirir. Herkes evet dediyse COMMIT, tek bir hayır varsa ABORT gider; katılımcılar uygular ve kilitlerini bırakır.
İki katılımcı da evet dedi, sonra koordinatör kararı bildiremeden çöktü. Payment beklemekten sıkılıp kendi başına rollback yapsa ne olabilir? Cevabı göster
Veri tutarsızlaşabilir. Koordinatör çökmeden önce Inventory’ye COMMIT göndermiş olabilir ve Payment bunu bilemez. Söz vermiş bir katılımcı bu yüzden tek başına karar veremez: bekler ve kilidini tutar.
Adım adım oku
- Koordinatör iki katılımcıya da hazır olup olmadıklarını soruyor.
- İkisi de evet diyor. Artık söz verdiler ve satırlarını kilitli tutuyorlar.
- Koordinatör kararı ilan edemeden ortadan kayboluyor.
- İki katılımcı da bekliyor. Kendi başlarına karar veremezler ve aynı satırları değiştirmek isteyen herkes de bekliyor.
Bu bekleyen katılımcıya in-doubt transactionPrepare'e 'evet' deyip koordinatörün kararını henüz almamış katılımcı. Tek başına commit de rollback de yapamaz; karar gelene kadar kilitlerini tutar.Sözlükte gör → denir. Aynı satırı değiştirmek isteyen her işlem de bekler: koordinatör ne kadar yoksa o satırlar o kadar donuk kalır.
Java'da 2PC: XA ve JTA· istersen atla
Katılımcıların bu protokolü konuşması için XAVeritabanı ve mesaj kuyruğu gibi kaynakların two-phase commit'e katılması için standart arayüz. Java'da JTA ile kullanılır; kaynağın XA desteklemesi gerekir.Sözlükte gör → arayüzü gerekir. Spring’de Narayana ya da Atomikos gibi bir JTA transaction manager, iki XA DataSource’u tek bir @Transactional altında toplayabilir. Kafka ise XA’ya katılmaz; HTTP arkasındaki bir servis de hiçbir zaman XA kaynağı değildir.
Kafam karıştı, daha basit anlat
2PC bir nikâh gibi: önce iki taraftan da “evet” alınır, sonra ilan edilir. Evet diyen taraf, ilan gelmeden kalkıp gidemez.
Two-phase commit'in iki aşaması sırasıyla nedir?
Prepare'e 'evet' demiş bir katılımcı koordinatörden bir türlü karar alamıyor. Doğru davranış hangisi?
Kendin gör
Aynı sipariş, iki protokol, bir arıza. Kartlarda her veritabanının durumu ve kilit süresi görünüyor.
2PC mi saga mı — hata anında kim, ne kadar bekliyor?
Tohum 3- Payment DBboşta
hesaptan 100 TL düş
kilit yok0 tick
- Inventory DBboşta
1 adet stok ayır
kilit yok0 tick
Oynat ya da adımla.
Şu an ne oldu?
Bir sipariş, iki veritabanı, bir koordinatör
Önce herkese "commit edebilir misin?" diye sorulacak, sonra karar herkese bildirilecek.
Görevler0/3
Katılımcıları belirsizlikte bırakaçık
İpucu
2PC'de iki taraf EVET dedikten sonra koordinatör kaybolursa ne olur?
Commit edilmiş bir adımı telafiyle geri çeviraçık
İpucu
Sagada ilk adım commit ettikten sonra ikinci adım "hayır" derse?
Aynı çöküşü kimseyi kilitte bekletmeden atlataçık
İpucu
Koordinatör çökmesini sagada dene ve kilit sürelerini 2PC ile karşılaştır.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Mutlu yolu 2PC ile oynat. Kilitler karar gelene kadar tutuluyor.
- Arızayı “Koordinatör yolun ortasında çöküyor” yap. İki taraf belirsiz ve kilitli bekliyor.
- Protokolü sagaya çevir, aynı arızayı dene. Kilit yok, ama bir süre yarım durum görünüyor.
- “Inventory hayır diyor” ile karşılaştır. 2PC iz bırakmıyor, saga bir iade satırı yazıyor.
- Üç görevi de tamamla.
Her cümle hangi yaklaşımı anlatıyor?
Mikroservisler neden çoğunlukla sagayı seçer
2PC tutarlılığı beklemekle öder. Her katılımcı, en yavaş katılımcı ve koordinatör kadar kilit tutar; biri düşerse ötekiler de durur.
Mikroservislerde bu bedel büyür. Servisler HTTP ya da Kafka ile konuşur ve bunlar XA’ya katılamaz; her servisi ortak bir koordinatöre bağlamak da onları birlikte kırılgan yapar.
Saga başka bir takas yapar: her adım hemen commit eder, hata bir telafi adımıyla düzeltilir. Sistem eventual consistencySistemin bir süre tutarsız kalıp sonunda tutarlı hâle gelmesi. Saga'nın verdiği garanti budur; arada geçilen ara durumu tasarımın kabul etmesi gerekir.Sözlükte gör → ile, yani sonunda tutarlı olur; adımlar arasındaki mesajları outbox kaybolmaktan korur.
| 2PC | Saga | |
|---|---|---|
| Arada yarım durum | Kilitlerin arkasında saklı | Dışarıdan görünür |
| Koordinatör düşerse | Katılımcılar kilitli bekler | Kilit yok, saga kaldığı yerden sürer |
| Bir adım hayır derse | ABORT, iz kalmaz | Telafi, iz kalır (iade satırı) |
| Gereken | XA destekleyen kaynaklar | Her adıma telafi, idempotent adımlar |
2PC kötü değil, yeri dar. Aynı uygulamada XA destekleyen bir veritabanı ve bir JMS kuyruğu kısa bir işlemde birlikte değişmeliyse, 2PC basit ve doğru bir seçimdir.
Kafam karıştı, daha basit anlat
2PC herkesi bekletir ve hepsi birlikte bitirir. Saga kimseyi bekletmez; arada yarım bir durum görünür ve hata yeni bir adımla düzeltilir.
Hangi durumda two-phase commit hâlâ makul bir seçimdir?
Tuzaklar
“JTA’yı açarız, olur” demek. Zincirde XA’ya katılmayan tek bir halka varsa garanti o halkada biter. Kafka’ya gönderilen mesaj ya da bir REST çağrısı bu halkalardandır.
Belirsiz transaction’ı elle çözmek. Uzun süre bekleyen bir katılımcıyı yönetici elle commit ya da rollback edebilir. Koordinatörün kararıyla çelişirse tutarsızlığı artık insan yaratmıştır.
JTA transaction manager kurulu, iki DataSource da XA. Yine de nadiren sipariş yokken para çekiliyor. Hangi satır garantinin dışında kalıyor?
Derinleş · Hesaptan kart borcuna ödeme: iki servis, bir saga 4 dosya · ~93 satır · ilk okumada atlayabilirsin
Kendini sına
Bir @Transactional metot, içinden çağrılan başka bir servisin commit ettiği işi de geri alır.
Koordinatör COMMIT kararını kendi günlüğüne yazdı, Payment'a gönderdi ve Inventory'ye gönderemeden çöktü. Yeniden başladığında ne yapmalı?
Aklında kalacak üç şey
- 1 Bir @Transactional yalnızca kendi veritabanı bağlantısını geri alır. Başka bir servisin commit ettiği iş bir ağ çağrısının ardındadır ve o rollback'e dahil olmaz.
- 2 Two-phase commit önce herkesten söz alır, sonra kararı bildirir. Söz vermiş bir katılımcı kararı alamazsa kilitlerini tutarak bekler; koordinatör ne kadar yoksa bekleme o kadar uzar.
- 3 Mikroservisler çoğunlukla saga ve outbox'ı seçer: kimse beklemez, bedeli arada görünen yarım durum ve telafi adımlarıdır. Aynı uygulamada, XA destekleyen iki kaynak arasında kısa bir işlem için 2PC hâlâ makul bir seçimdir.
5 kart sonraki derste seni bekliyor