Architecture

İki kez gelen webhook için tasarlayın

Bir ödeme olayının ikinci kopyası yeni iş değildir. Benzersiz bir anahtar, bir giden kutu ve geçmişi düzenlemeyen bir iptal.

Design for the webhook that arrives twice

Tehlikeli istek ilki değildir. Gönderen ilk yanıtı duymadığı ya da duyup yine de yeniden denediği için aynı isteğin yeniden gelmesidir. Ödeme sağlayıcıları bunu yapar. Kuyruklar bunu yapar. Bir kişi düğmeye iki kez basınca tarayıcı bunu yapar. Sistem ikinci kopyayı yeni iş sayarsa iki kez tahsil edersiniz, iki kez gönderirsiniz ya da bir bahis için iki defter satırı yazarsınız.

Kopyanın geleceğini varsayın

İşleyiciyi, ikinci teslimin hiçbir şey yapmayan bir başarı olacağı şekilde tasarlarım. Birincisi işi yapar ve bir anahtar saklar: sağlayıcının olay kimliği ya da istemcinin gönderdiği, veritabanında benzersiz bir anahtar. İkincisi anahtarı bulur ve aynı sonucu döner. İkinci ücret yok. İkinci posta yok. İkinci satır yok.

Güvendiğim parça benzersizlik kısıtıdır. Eklemeden bir an önce uygulama kodundaki kontrol, iki worker birlikte uyandığında yarışı kaybeder. Veritabanı o yarışı kaybetmez. Kaybeden bir çakışma alır ve onu “zaten yapıldı”ya çevirir.

İlk gelişİkinci geliş
SorgulamaAnahtar yeniAnahtar zaten kayıtlı
YazmakTek satır, tek yan etkiYeni satır yok
YanıtlaKabul edildiAynı, zaten kabul edilmiş
Süreç yarıda ölürseGiden kutu mesajı hâlâ borçluİkincisini borçlu değil

Mesaj yazmayla birlikte çıkar, ya da hiç çıkmaz

E-postayı ya da kuyruk iletisini istek içinde ve commit’ten önce göndermek, geri alınan bir işlem için ileti almanın yoludur. Giden iletiyi iş satırıyla aynı işlemde yazarım ve ayrı bir worker onu yayımlar. Süreç ölürse satır ve ileti ya ikisi birden vardır ya da hiçbiri yoktur. Worker yayını yeniden deneyebilir. İşleyici ücreti yeniden denememelidir.

İptal yeni bir kayıttır, düzeltme değil

Bir şeyi geri almak gerektiğinde özgün satırın üzerine yazmam. Denkleştiren bir kayıt neyin değiştiğini söyler ve ilk satırı okunur bırakır. Bir hafta sonra hâlâ 14:02’de neye inanıldığını cevaplayabilirsiniz. Satırı düzenlemek yalnızca şimdi neye inanıldığını söyler, hem de tanık olmadan.

Yeniden deneme işlemi iki kez yaptırabiliyorsa, işlem bitmemiştir.

İşleyiciyi yazmadan önce anahtarı adlandırın

Anahtar, yeniden denemeler arasında gönderenin neyi değiştirdiğini kontrol etmediyseniz “isteğin tamamının özeti” değildir. Bir ödeme sağlayıcısı aynı olay kimliğini yeni bir teslim zaman damgasıyla yeniden gönderir. Damga anahtarın içindeyse ikinci teslim yeni görünür ve önlemek istediğiniz hatayı kurmuş olursunuz. Gönderenin kararlı olacağını söz verdiği kimliği kullanırım. Söz vermezse arayanın bir tane göndermesini isterim ve onsuz gelen çağrıyı reddederim.

Sonra başarısızlığı başarının yazıldığı yere yazarım. Çakışma, gönderen için bir hata sayfası değildir. Yeniden denemeye beklenen yanıttır. Gönderen durabilmelidir. 500 yanıtlarsam üçüncü kez denerler, dördüncü kez de, ve olay aynı işin kuyruğuna döner.

Mutlu yol 200 döndü diye bunu bitmiş saymam. Test aynı yükü yeniden gönderir ve tek bir yan etki olduğunu doğrular. Sonra commit ile yayın arasında worker’ı öldürür ve iletinin hâlâ bir kez çıktığını doğrular. Bu iki test, yerini aldıkları olaydan kısadır. Oradan başlarım.