← Back to list

Dönüşüm Günlüğü: Parantez — Cevap Gelmedi, Peki İşlem Oldu mu?

Geçen yazıda Saga Pattern’i konuştuk. Ve bir cümleyle kapattık: compensating action’lar — yani klasik rollback gibi “hiç olmamış gibi geri…

oğuz musapaşaoğlu · 2026-06-15 00:52 · 0 claps · 4.6 min read
#idempotency #distributed-systems #microservices #outbox-pattern #resilience
Open on Medium ↗
Wiki topics: 🚀 · Self Improvement

Dönüşüm Günlüğü: Parantez — Cevap Gelmedi, Peki İşlem Oldu mu?

Geçen yazıda Saga Pattern’i konuştuk. Ve bir cümleyle kapattık: compensating action’lar — yani klasik rollback gibi “hiç olmamış gibi geri dön” değil, “oldu, ama şimdi telafi edelim” mantığıyla çalışan geri alma adımları — idempotent olmalı, yani aynı işlemi bir kez de on kez de çalıştırsan sonuç değişmemeli. Çünkü aynı mesaj iki kez gelebilir.

Bu cümle küçük bir not gibi duruyor. Ama altında çok daha büyük bir problem var — ve bu problem Saga’ya, hatta mikroservise bile ihtiyaç duymuyor. En sıradan iki servis arasındaki tek bir çağrıda bile karşına çıkabilir.

Senaryo

Sipariş Servisi, Ödeme Servisi’ne “bu siparişin ödemesini al” isteğini gönderdi.

Ödeme Servisi isteği aldı. İşledi. Ödemeyi çekti. Cevabı dönmeye hazırlanıyor.

Ama tam o sırada Sipariş Servisi düştü.

Neden düştü, onu boş ver — deploy oldu, pod yeniden başlatıldı, memory limiti aşıldı, scheduler instance’ı taşıdı, ne olduysa oldu. Sipariş Servisi bir süre sonra tekrar ayağa kalktı.

Şimdi Sipariş Servisi’nin bildiği ne?

Hiçbir şey.

Ödeme Servisi’ne gönderdiği o isteğin cevabı ne oldu? Ödeme alındı mı? Hata mı döndü? Cevap geldi ama o sırada kimse dinlemediği için sessizce kayboldu mu?

Sipariş Servisi bunların hiçbirini bilmiyor. Restart oldu, hafızası sıfırlandı. Elinde sadece şu var: “Ben bir zamanlar bir ödeme isteği göndermiştim. Sonucunu bilmiyorum.”

Ambiguous Outcome

Buna Ambiguous Outcome — “belirsiz sonuç” — ya da Unknown Outcome — “bilinmeyen sonuç” diyoruz. İkisi de aynı şeyi anlatıyor: isteği gönderen taraf, işlemin başarılı mı başarısız mı olduğunu bilemiyor. Cevap kaybolmuş olabilir, ya da cevabı alacak taraf artık o cevabı dinleyecek durumda değil.

Circuit Breaker’la karşılaştırınca fark netleşiyor:

Durum                  Problem
─────────────────────────────────────────────────
Circuit Breaker        İstek karşıya ulaşamıyor
Ambiguous Outcome       İstek ulaştı, işlem yapıldı,
                        ama cevabı kimse alamadı

Circuit Breaker’da işin rahatlığı şu: “konuşamadık, bekleyelim.” Ambiguous Outcome’da öyle bir lüks yok — konuşuldu, iş yapıldı, ama sonucu kim bilecek?

Somut Bir Örnek — OCPP Üzerinden

EV şarj dünyasında OCPP (Open Charge Point Protocol) standardı var — şarj istasyonlarını yöneten arka sistemler ile fiziksel cihazlar arasındaki iletişimi tanımlıyor.

Bir backend, bir şarj cihazına RemoteStartTransaction komutu gönderir — "şarjı başlat" der. Cihaz komutu alır, şarjı başlatır, transaction oluşur.

Peki ya bu komutu gönderen servis, cevap (RemoteStartTransaction.conf) dönmeden önce restart olursa?

Cihaz şarjı başlatmış olabilir. Ama backend bunu bilmiyor — çünkü cevabı alacak süreç artık orada değil.

Backend tekrar “şarjı başlat” derse, cihaz zaten şarj yapan bir araca ikinci kez başlatma komutu alabilir. Ya da backend hiç komut göndermediğini düşünüp kullanıcıya “şarj başlatılamadı” diyebilir — halbuki şarj gerçekten başlamıştır.

Ağ ve sistemler yalan söyleyebilir. “Cevap gelmedi” demek “işlem olmadı” anlamına gelmez.

Peki Ne Yapacağız?

Beş araç var. Her biri aynı soruya farklı bir açıdan cevap veriyor: “Restart sonrası ne yapıyordum, ve şimdi güvenle ne yapabilirim?”

1. Idempotency Key

Sipariş Servisi, Ödeme Servisi’ne istek gönderirken bir benzersiz key üretir — bir GUID, mesela. İsteği bu key ile gönderir.

Ödeme Servisi şu kuralı uygular: “Bu key’i daha önce işledim mi? İşlediysem eski sonucu döndür, işlemedim mi şimdi işle.”

İstek: POST /payment  { idempotencyKey: "abc-123", ... }
       → Ödeme Servisi işler, sonucu key="abc-123" ile saklar
(Sipariş Servisi restart oldu, cevabı kaçırdı)
Retry: POST /payment  { idempotencyKey: "abc-123", ... }
       → Ödeme Servisi: "Bu key'i zaten işledim"
       → Aynı sonucu döner, ödemeyi İKİNCİ KEZ ÇEKMEZ

Artık retry güvenli. Ama bir sorun var: Sipariş Servisi restart olduktan sonra bu key’i hatırlıyor mu? Hatırlamıyorsa, key’in kendisi hiçbir işe yaramaz.

2. Outbox Pattern

İşte tam burada Outbox devreye giriyor — ve bu senaryoda asıl can simidi bu.

Sipariş Servisi, Ödeme Servisi’ni çağırmadan önce şunu kendi DB’sine yazar: “Sipariş #456 için idempotencyKey=abc-123 ile bir ödeme isteği gönderiyorum, durum: beklemede.” Bu yazma işlemi, siparişin kendi durum güncellemesiyle aynı transaction içinde olur.

Ancak bundan sonra Ödeme Servisi’ne istek gönderilir.

Restart olduğunda Sipariş Servisi’nin hafızası DB’den geri gelir. “Ben sipariş #456 için abc-123 key’iyle bir ödeme isteği göndermiştim, ve bu hâlâ ‘beklemede’ duruyor” der.

Artık ne yapacağını biliyor — az sonra göreceğimiz Status Check’i kullanabilir.

1. DB transaction:
   - Sipariş durumu: "ödeme bekleniyor"
   - Outbox kaydı: idempotencyKey=abc-123, hedef=Ödeme Servisi
2. Transaction commit
3. Ödeme Servisi'ne istek gönder (idempotencyKey=abc-123)
   ── restart ──
4. Outbox'tan oku: "abc-123 için bekleyen bir isteğim var"
5. Ödeme Servisi'ne sor: "abc-123'ün durumu ne?"

3. Status Check (Durum Sorgulama)

Outbox sana “ne yapıyordum”u hatırlattı. Status Check ise “ne oldu”yu öğretir.

GET /payment/status/abc-123
→ { status: "completed", paymentId: "pay-789" }

Sipariş Servisi artık biliyor: ödeme tamamlanmış. Tekrar istek göndermesine gerek yok — sadece kendi sipariş durumunu güncelleyip devam edebilir.

Eğer cevap { status: "not_found" } dönerse, bu da bir bilgi: Ödeme Servisi bu key'i hiç görmemiş, yani istek gerçekten gitmemiş — artık güvenle (idempotency key ile) tekrar gönderebilir.

4. CorrelationId

Idempotency Key ile karıştırılır ama amacı farklı. Idempotency Key “bu işlemi tekrar çalıştırma” derken, CorrelationId “bu cevap hangi isteğe ait, onu bul” der.

Özellikle asenkron mesajlaşmada kritik. Bir mesaj gönderiyorsun, cevap başka bir kanaldan, başka bir zamanda geliyor.

Sipariş Servisi → [CorrelationId: xyz-789] → Queue → Ödeme Servisi
                                                          │
Sipariş Servisi ← [CorrelationId: xyz-789] ← Queue ← ────┘

Restart sonrası Sipariş Servisi kendi reply queue’sunu dinlediğinde, bekleyen cevabı CorrelationId üzerinden eski isteğiyle eşleştirebilir.

5. Request-Reply Pattern

CorrelationId’nin üzerine kurulan yapı. Senkron HTTP’de “istek-cevap” otomatik gelir. Ama asenkron mesajlaşmada bunu sen kurman gerekir.

Sipariş Servisi bir “reply queue” belirtir, isteği CorrelationId ile gönderir. Ödeme Servisi işi yapar, cevabı o reply queue’ya aynı CorrelationId ile bırakır. Sipariş Servisi — restart olmuş olsa da — bu queue’yu dinlemeye başladığında bekleyen cevabı bulur.

Ne Zaman Hangisi?

İhtiyaç Araç Retry güvenli olsun, tekrar çalıştırma Idempotency Key Restart sonrası “ne yapıyordum” hatırla Outbox Restart sonrası “ne oldu” öğren Status Check Asenkron cevabı isteğiyle eşleştir CorrelationId Asenkron ama cevap bekleyen akış Request-Reply

Tek başına kullanılmazlar. Outbox + Idempotency Key + Status Check üçlüsü, “restart oldum, ne yapıyordum, ne oldu, şimdi güvenle ne yapabilirim” sorularının hepsine birlikte cevap verir.

Sonuç

Circuit Breaker bize “karşıya ulaşamıyorsam ne yapmalıyım” sorusunun cevabını verdi. Bu yazı tam tersini sordu: “karşıya ulaştım, iş yapıldı, ama ben bunu bilmiyorum — restart oldum, hafızam yok — şimdi ne yapmalıyım?”

İkisi birlikte, dağıtık sistemlerde “her şey ters gidebilir” varsayımının iki yüzü. Biri bağlantı kuramama, diğeri bağlantıyı kurup da sonucu — ya da kendi hafızanı — kaybetme.

Ve unutma: sistemler yalan söyleyebilir. “Cevap gelmedi” demek “işlem olmadı” anlamına gelmez.

Serinin diğer makalelerine profilimden ulaşabilirsiniz.

Idempotency

Distributed Systems

Microservices

Outbox Pattern

Resilience


메타데이터
post_id
21b3da3ed770
slug
dönüşüm-günlüğü-parantez-cevap-gelmedi-peki-i̇şlem-oldu-mu-21b3da3ed770
url
https://medium.com/@oguzmusa/d%C3%B6n%C3%BC%C5%9F%C3%BCm-g%C3%BCnl%C3%BC%C4%9F%C3%BC-parantez-cevap-gelmedi-peki-i%CC%87%C5%9Flem-oldu-mu-21b3da3ed770
canonical_url
https://medium.com/@oguzmusa/d%C3%B6n%C3%BC%C5%9F%C3%BCm-g%C3%BCnl%C3%BC%C4%9F%C3%BC-parantez-cevap-gelmedi-peki-i%CC%87%C5%9Flem-oldu-mu-21b3da3ed770
author_url
https://medium.com/@oguzmusa
status
ok
fetched_at
2026-06-24 11:06:28