Oyun Kitabı

Webhook Altında Optimistic Concurrency (Webhook Altinda Optimistic Concurrency)

Webhook ile senkron yanıt aynı ödemeye aynı anda dokunduğunda version token ve lease nasıl yarışı çözer? Bayat okumanın terminal ödemede client secret…

Dağıtık Ödeme Motoru (Distributed Payment Engine)

Bolum 17 / 22

Capture ile complete arasındaki boşluğu kapatan dağıtık ödeme mimarisi serisi.

Distributed payment engine architecture diagram

Önceki bölümde eventual consistency'nin neden kaçınılmaz olduğunu gördük: PSP transaction protokolünüze katılamaz, saga ve mutabakat gerçek cevaptır. Bu bölüm, o tutarsızlık penceresinin en yoğun anına odaklanıyor — webhook ile senkron yanıtın aynı ödeme kaydına eşzamanlı dokunduğu an.

Checkout orchestrator bir charge isteği gönderir; provider gateway PSP'den yanıt alır. Aynı anda — bazen milisaniyeler önce, bazen sonra — aynı ödeme için bir webhook gelir. İki yol da doğru bilgi taşıyabilir; ikisi de aynı anda yazmaya kalkarsa sonuç ya kayıp güncelleme ya da daha kötüsü, terminal bir ödemede bayat okumaya dayalı yanlış bir client yanıtıdır.

Senkron yanıt ──► Payment #42 (version=3) ──► Captured
Webhook       ──► Payment #42 (version=3) ──► Captured (tekrar?)
                      │
                      ▼
              Version token + lease
              → tek kazanan yazar

Optimistic concurrency burada bir performans optimizasyonu değil; terminal statüde yanlış veri döndürmemenin mekanizmasıdır.

Kavramlar ilk geçtiği yerde

📦 Version Token (Optimistic Lock)
Her güncellemede artan sayaç; yazma yalnızca beklenen sürüm eşleşirse başarılı olur.

📦 Lease
Bir worker'ın belirli bir ödeme kaydını işleme hakkını süre sınırlı olarak alması.

📦 Stale Read
İşlem sırasında okunan ama yazma anında artık geçerli olmayan eski sürüm.

📦 Terminal Payment
Captured, Failed veya Refunded gibi geri dönüşü olmayan nihai statü.

Lease, bir worker'ın 'bu kaydı ben işliyorum' demesidir; version token ise 'ben hâlâ bu sürümü görüyorum' demesidir. İkisi birlikte, webhook ile senkron yolun birbirinin üzerine yazmasını engeller.

İki yol, tek kayıt: yarış nerede başlar

Bir redirect tabanlı ödemede senkron yol genellikle Pending döner; gerçek sonuç webhook ile gelir. Kart ödemesinde ise her iki yol da Captured veya Failed taşıyabilir — ve ikisi de neredeyse aynı anda ulaşabilir. Checkout orchestrator her iki yoldan gelen bilgiyi aynı payment satırına yazmaya çalışır.

Klasik hata: her iki handler da kaydı okur, statüyü günceller, kaydeder. Son yazan kazanır; aradaki güncelleme sessizce kaybolur. Daha tehlikeli senaryo: orchestrator terminal statüye geçmeden önce eski bir sürümü okur ve müşteriye hâlá geçerli görünen bir client secret veya redirect URL döner — ödeme aslında çoktan tamamlanmış veya başarısız olmuştur.

T=0  Orchestrator: charge gönder
T=1  Webhook gelir → Captured yaz (version 2→3)
T=2  Senkron yanıt gelir → Pending okudu (version 1)
     → client'a redirectUrl döner (bayat!)
T=3  Müşteri redirect'e gider → ödeme zaten Captured

Version token: yazma ancak sürüm eşleşirse

Her payment kaydında monoton artan bir version alanı tutulur. Güncelleme şu koşulla yapılır: UPDATE ... WHERE id = ? AND version = ?. Eşleşme yoksa güncelleme sıfır satır etkiler — bu, başka bir yolun araya girdiğinin sinyalidir.

Webhook handler
  READ payment (version=2, status=Processing)
  → status=Captured, version=3
  UPDATE WHERE version=2 ✓ (1 row)

Senkron handler (bayat okuma)
  READ payment (version=2, status=Processing)  ← webhook henüz commit olmadı
  → status=Captured, version=3
  UPDATE WHERE version=2 ✗ (0 rows — webhook zaten yazdı)
  → yeniden oku, terminal statüyü gör, client secret döndürme

Version token tek başına yeterli değildir; stale read'i tespit ettikten sonra ne yapılacağını da tanımlamak gerekir: yeniden oku, terminal statüyü kontrol et, client'a yalnızca güncel durumu döndür.

Lease: webhook işleme hakkını süre sınırlı almak

Webhook handler'ı kayda dokunmadan önce kısa süreli bir lease alır: 'Payment #42'yi 30 saniye boyunca ben işliyorum.' Lease süresi boyunca başka bir worker aynı kaydı webhook veya recovery akışında işleyemez.

Webhook gelir
  → lease al (paymentId, ttl=30s)
  → lease alınamazsa → defer / retry
  → lease alındı → version token ile güncelle
  → lease bırak

Lease, aynı webhook'un iki worker tarafından eşzamanlı işlenmesini engeller. Version token ise farklı yolların (senkron vs webhook) çakışmasını çözer. İkisi farklı sorunlara cevap verir; birlikte kullanılmaları gerekir.

Terminal ödemede client secret sızıntısı

En ciddi stale-read senaryosu terminal statüde client secret veya redirect URL dönmektir. Ödeme Captured olduktan sonra müşteriye hâlâ Pending + redirectUrl dönmek, gereksiz bir ikinci charge denemesine veya kafa karışıklığına yol açar.

Kural basittir: terminal statüye geçmiş bir ödemede client secret, redirect URL veya yeniden deneme token'ı asla döndürülmez. Handler, version conflict aldığında veya stale read şüphesi varsa kaydı yeniden okur; terminal statü görürse yalnızca nihai sonucu döner.

Durum Client'a dönen
Processing, redirect gerekli redirectUrl (geçerli)
Captured (terminal) Başarı sonucu, secret yok
Failed (terminal) Hata sonucu, secret yok
Version conflict → yeniden oku → Captured Başarı sonucu, secret yok

Sık karıştırılan ayrımlar

❌ Pessimistic lock her zaman daha güvenlidir
✓ Ödeme akışında kısa süreli lease + version token, throughput'u korurken yarışı çözer

❌ Version conflict = hata, exception fırlat
✓ Version conflict = başka yol kazandı; yeniden oku ve güncel duruma uy

❌ Lease ve version token aynı şeyi yapar
✓ Lease aynı kaydın eşzamanlı işlenmesini engeller; version token eşzamanlı yazmayı

Lease vs version token

Kriter Lease Version Token
Engellediği Aynı kaydın paralel işlenmesi Kayıp güncelleme (lost update)
Süresi TTL ile sınırlı Kalıcı, her yazmada artar
Conflict davranışı Bekle / defer Yeniden oku / retry

Optimistic concurrency kontrol listesi

  1. Payment güncellemeleri WHERE version = ? koşuluyla mı yapılıyor?
  2. Version conflict alındığında handler yeniden okuyup terminal statüyü kontrol ediyor mu?
  3. Terminal statüde client secret veya redirect URL dönülmesi kod seviyesinde engellenmiş mi?
  4. Webhook handler'ı işleme başlamadan lease alıyor mu?
  5. Lease süresi, webhook işleme süresinin P99'undan uzun mu?
  6. Senkron yanıt handler'ı ile webhook handler'ı aynı finalize mantığını mı paylaşıyor?

Bu yazıdan aklında ne kalmalı

  1. Webhook ile senkron yol aynı kayda eşzamanlı dokunur; optimistic concurrency bu yarışın standart cevabıdır.
  2. Version token kayıp güncellemeyi engeller; lease aynı kaydın paralel işlenmesini engeller.
  3. Version conflict bir hata değil, yeniden okuma sinyalidir.
  4. Terminal ödemede bayat okumadan client secret dönmek, sessiz bir güvenlik ve UX hatasıdır.

Yarışı çözmek için pessimistic lock'a ihtiyacınız yok; ihtiyacınız olan, bayat okumanın client'a ulaşmasını engelleyen disiplinli bir version token ve lease kombinasyonudur.

Bir sonraki bölümde bu yarışları ve finalize adımlarını görebilmek için observability'ye geçiyoruz: payment id ile korelasyon, adım adım event log ve deferred finalize metrikleri.

SSS

Sık sorulan sorular

Version Token (Optimistic Lock) nedir?

Her güncellemede artan sayaç; yazma yalnızca beklenen sürüm eşleşirse başarılı olur.

Lease nedir?

Bir worker'ın belirli bir ödeme kaydını işleme hakkını süre sınırlı olarak alması.

"Pessimistic lock her zaman daha güvenlidir" doğru mu?

Ödeme akışında kısa süreli lease + version token, throughput'u korurken yarışı çözer

Bu bölüm neyi sabitler?

Optimistic concurrency burada bir performans optimizasyonu değil; terminal statüde yanlış veri döndürmemenin mekanizmasıdır. Webhook ile senkron yol aynı kayda eşzamanlı dokunur; optimistic concurrency bu yarışın standart cevabıdır. Önceki bölümde eventual consistency'nin neden kaçınılmaz olduğunu gördük: PSP transaction protokolünüze katılamaz, saga ve mutabakat gerçek cevaptır. Bu bölüm, o tutarsızlık penceresinin en yoğun anına odaklanıyor — webhook ile senkron yanıtın aynı ödeme kaydına eşzamanlı dokunduğu an.

Ogrenilen Muhendislik Prensipleri

  • Version token kayıp güncellemeyi engeller; conflict yeniden okuma sinyalidir.
  • Lease ile version token farklı yarışları çözer; ikisi birlikte kullanılmalıdır.
  • Terminal ödemede bayat okumadan client secret dönülmemelidir.

Okumaya devam et

Okumaya devam et

Seride sonraki yazi

Seride sonraki yazi

Ayni seriden

Paylaş