Aynı isteği iki kez göndermek. Sistemin cevabı ne?

This post is in Turkish.

Escrow yazısında şu cümleyi yazmıştım: bir ödeme akışının olgunluğunu tek soruyla ölçebilirsin — aynı isteği iki kez gönderdiğimde ne oluyor?

O yazıda tek satırda geçmişti. Ama asıl iş o satırın içinde, ve idempotency'yi yanlış uygulamak, hiç uygulamamaktan daha tehlikeli. Çünkü ikinci durumda dikkatli oluyorsun; ilkinde güvende olduğunu sanıyorsun.

Anahtarı kim üretir

İlk ve en sık yapılan hata: idempotency anahtarını sunucuda üretmek.

Mantıklı geliyor — her isteğe bir kimlik veriyorsun, tekrarını yakalıyorsun. Ama sunucu anahtarı üretiyorsa, aynı isteğin ikinci gelişi de yeni bir anahtar alıyor. Yani tam olarak yakalamak istediğin şeyi kaçırıyorsun.

Anahtar, niyetin sahibi tarafından üretilmek zorunda. Kullanıcı "bu ödemeyi yap" dediği anda istemci bir anahtar üretiyor ve o niyet boyunca aynı anahtarı kullanıyor. Ağ koptu, tekrar denedi, kullanıcı butona bastı — hepsi aynı anahtar. Anahtar isteğin kimliği değil, niyetin kimliği.

Cevabı da saklamak zorundasın

İkinci hata: anahtarı görüp isteği yok saymak.

Naif uygulama şöyle: anahtar daha önce görülmüşse hiçbir şey yapma, tamam dön. Ama istemci ilk cevabı hiç alamamış olabilir — zaman aşımına düşen istek belki de başarıyla işlenmişti. İstemcinin ihtiyacı olan şey "tamam" değil, ilk çalıştırmanın sonucu. İşlem kimliği, tutar, durum.

Yani idempotency bir yinelenme kalkanı değil, bir sonuç önbelleği. Anahtarla birlikte ilk yanıtın gövdesini de saklıyorsun ve ikinci istekte onu aynen döndürüyorsun. İstemci, isteğin ilk mi ikinci mi olduğunu bilmek zorunda kalmıyor.

İdempotent bir uç nokta, "iki kez çalışmayan" uç nokta değil. Kaç kez sorarsan aynı cevabı veren uç nokta.

Yarış hâlâ duruyor

Üçüncüsü ve en sinsisi: iki eşzamanlı istek.

Anahtarı kontrol ediyorsun, yok. Diğer istek de aynı anda kontrol ediyor, o da yok. İkisi de işlemi çalıştırıyor. Kontrol var, koruma yok.

Buradaki çözüm kontrolü ve kaydı ayrı adımlar olmaktan çıkarmak. Anahtar üzerinde benzersizlik kısıtı olan bir tablo, ve işlemin kendisi ile anahtarın yazılması aynı transaction içinde. İkinci istek kısıta çarpıp kaybediyor — kaybettiği için de birincinin sonucunu okuyup döndürüyor.

Yani veritabanına yaptırabileceğin bir işi uygulama katmanında yapmaya çalışmak, burada doğrudan para kaybı.

Anahtarın kapsamı

Dördüncüsü: anahtar ne kadar yaşayacak ve neyi kapsayacak?

Sonsuza kadar saklamak makul değil. Ama çok kısa tutarsan, gecikmiş bir yeniden denemeyi yeni bir işlem sanıyorsun. Bir başka tuzak: aynı anahtarın farklı bir gövdeyle gelmesi. Kullanıcı tutarı değiştirip aynı anahtarla tekrar gönderirse, doğru davranış eski sonucu döndürmek değil — çelişkiyi hata olarak bildirmek. Sessizce ilkini uygulamak, kullanıcının istemediği tutarı onaylamak demek.

Ödemeyle bitmiyor

Bunu ödeme için öğrendim ama para hareketine özel değil. E-posta göndermek, bildirim atmak, stok düşmek, ambar hareketi yazmak — hepsi aynı sınıfta. Teck'te bir barkod okutulduğunda stok düşüyor; aynı okutma ağ hıçkırığı yüzünden iki kez giderse, stok iki kez düşmemesi gerekiyor.

Ortak nokta şu: dış dünyada bir etki yaratan her işlem, yeniden denenebilir olmak zorunda. Ve yeniden denenebilir olmak, idempotent olmak demek.

Dağıtık bir sistemde yeniden denemenin güvenli olmadığı her yer, er ya da geç bir olay kaydına dönüşüyor.