İdempotentlik Nedir? Muhasebe ve Ödeme API'leri Nందుకు Neden İhtiyaç Duyar?
Yeniden denenen bir isteğin neden mükerrer bir fatura yarattığını, idempotentlik anahtarının neleri garanti ettiğini, ele alması gereken hata modlarını ve bunun doğru şekilde nasıl uygulanacağını açıklayın.
Bir işlem, iki kez gerçekleştirildiğinde tek bir kez gerçekleştirilmesiyle aynı etkiyi yaratıyorsa idempotent (işlem tekrarına dayanıklı) kabul edilir. Bir kaydı okumak doğası gereği idempotenttir. Bir fatura oluşturmak ise öyle değildir, fonksiyonu iki kez çağırırsanız elinizde iki fatura, iki alacak ve neden iki kez faturalandırıldığını soran bir müşteri kalır.
Bu açık önemlidir, çünkü ağlar belirli ve sinir bozucu bir şekilde başarısız olur: istek başarılı olur ancak yanıt kaybolur. İstemci bir zaman aşımı (timeout) görür ve isteğin "hiç ulaşmadığını" mı yoksa "ulaştığını, çalıştığını ve yanıtın kaybolduğunu" mu ayırt edemez. Güvenli görünen tek seçeneği yeniden denemektir ve mükerrer kayıtları yaratan da işte bu yeniden denemedir.
Muhasebe bunu neden daha da kötüleştirir?
Çoğu sistemde mükerrer bir kayıt sadece küçük bir rahatsızlıktır. Defteri kebirde ise bu, kartopu etkisi yaratan bir sorundur:
- Mükerrer bir fatura, boşluksuz bir seriden (gapless sequence) bir numara tüketir; dolayısıyla onu silmek bir seçenek değildir. İmmutability (değiştirilemezlik) kuralı gereği, hatayı düzeltmek için müşterinizin göreceği bir alacak dekontu (credit note) gerekir.
- Mükerrer kayıt yevmiye maddeleri (journal entries) kaydeder; bu nedenle biri fark edene kadar gelir, KDV (VAT) ve alacakların hepsi fazladan görünür.
- Bu fatura KDV beyannamesine ve Litvanya'da i.SAF kayıtlarına girer.
- Bir ödemeyi tetiklediyse, fiilen para transferi gerçekleşmiştir.
"Sadece mükerrer kayıtları kontrol et" yaklaşımı da işe yaramaz, çünkü aynı müşteriye aynı gün aynı tutarda gönderilen iki tamamen bağımsız fatura son derece normaldir. Sistem, niyeti içerikten çıkaramaz, istemcinin bunu açıkça belirtmesi gerekir.
İdempotentlik anahtarları nasıl çalışır?
İstemci, her mantıksal işlem için benzersiz bir anahtar üretir ve bunu istekle birlikte gönderir:
POST /v1/sales/invoices/create
Idempotency-Key: 8f14e45f-ea1b-4c2b-9f3a-2d7c1e0b5a91
Sunucu şu garantileri verir:
Bu anahtarla yapılan ilk istek - normal şekilde işlenir, sonuç anahtarla eşleştirilerek saklanır.
Aynı anahtar ve aynı payload ile yeniden deneme - yeniden yürütülmez; saklanan yanıt döndürülür.
Aynı anahtar, farklı payload - reddedilir. Anahtar, bunun aynı işlem olduğuna dair bir iddiadır; farklı bir gövde aynı olmadığını gösterir ve eski sonucu sessizce döndürmek bir hatadan daha kötü olurdu.
Orijinal istek hala çalışırken yeniden deneme - eşzamanlı (concurrent) olarak yürütülmek yerine yeniden denenebilir bir hatayla reddedilir.
Madde, saf (naive) uygulamaların gözden kaçırdığı konudur. 5. saniyede zaman aşımına uğrayan ve 6. saniyede yeniden deneyen bir istemci, kendi devam eden (in-flight) isteğiyle çakışacaktır. Eşzamanlılık koruması olmadan her iki istek de ilerler ve mekanizmanın önlemek için var olduğu mükerrer kayıtlar oluşur.
Anahtarları doğru şekilde üretmek
Anahtar, denemeyi değil işlemi tanımlamalıdır:
- Doğru: Kullanıcı "fatura kes" butonuna tıkladığında bir kez oluşturulan ve bu tıklamanın her yeniden denemesinde yeniden kullanılan bir UUID.
- Doğru: Kendi alanınızdan (domain) türetilen deterministik bir anahtar:
order-1041-invoice. - Yanlış: Her HTTP denemesi için yeni bir UUID. Her yeniden deneme yeni bir anahtar alır, dolayısıyla her yeniden deneme yeni bir işlemdir ve hiçbir şey uygulamış olmazsınız.
- Yanlış: Payload'un bir hash değeri. Bu durumda gerçekten aynı olan iki işlem birbiriyle çakışır ve ikincisi sessizce yutulur.
Anahtarların bir saklama (retention) süresine ihtiyacı vardır. Bu süre, gerçekçi yeniden deneme pencerelerini ve istemci kesintilerini kapsayacak kadar uzun olmalı, ancak sonsuza kadar sürmemelidir.
Neleri çözmez?
İdempotentlik, aynı isteğin mükerrer olarak yürütülmesine karşı koruma sağlar. Şunlara karşı koruma sağlamaz:
- Farklı oturumlardan aynı faturayı oluşturan iki farklı kullanıcı. Bu bir iş kuralı (business-rule) sorunudır ve her sipariş için bir fatura gibi benzersizlik kısıtlamalarıyla (uniqueness constraints) çözülür.
- Farklı anahtarlara sahip mantıksal mükerrer kayıtlar.
- İşlem içindeki determinizm eksiklikleri. İşleyici (handler) bir zaman damgası veya numara üretiyorsa, saklanan yanıt ilkini yakalar; bu istenen davranıştır ancak anlamakta fayda vardır.
Bu yaklaşım, veritabanı kısıtlamalarının yerini almaz, onları tamamlar. Nordlet'in defteri her ikisini de kullanır. İdempotentlik anahtarları yeniden denenen istekleri durdururken; benzersiz indeksler (unique indexes), ödeme kimliği (payout ID) başına mükerrer mutabakat partilerini (settlement batches), tedarikçi ve belge numarası başına mükerrer alış faturalarını ve varlık ile dönem başına mükerrer amortisman (depreciation) çalıştırılmalarını engeller.
Nordlet bunu nasıl uygular?
Durum değiştiren (state-changing) herhangi bir istekte Idempotency-Key üstbilgisini (header) gönderin. Bu, en fazla 255 karakter uzunluğunda boş olmayan bir dize olmalıdır.
Belirli bir anahtarla yapılan ilk istek yürütülür ve sonucu saklanır. Aynı anahtar ve aynı payload ile yapılan bir yeniden deneme, yeniden yürütülmeden saklanan yanıtı döndürür; böylece çağrı kaç kez tekrarlanırsa tekrarlansın fatura tam olarak bir kez oluşturulur.
İki hata modu sessiz kalmak yerine açıkça belirtilir:
- Aynı anahtar, farklı payload → reddedilir: "Idempotency-Key was already used with a different request payload." Bu, bir istemcinin tutarı değiştirirken anahtarı yeniden kullandığı gerçekten tehlikeli durumu yakalar.
- Aynı anahtar, orijinal istek hala devam ediyor → yeniden denenebilir olarak reddedilir: "A request with this Idempotency-Key is still being processed, retry shortly." Eşzamanlı mükerrer yürütme olmaz.
Anahtarlar şirket bazında kapsamlandırılır (scoped), bu nedenle aynı UUID'yi üreten iki kiracı (tenant) asla çakışmaz.
Tamamlayıcı garanti diğer yönde çalışır. Webhook teslimi en az bir kez (at-least-once) gerçekleştirilir; yani uç noktanız (endpoint) bazen aynı eventi iki kez alabilir. Bu durum, teslimatın aslında başarılı olduğu bir zaman aşımından sonra veya yeniden teslimatta gerçekleşir. Her event bir ID taşır ve işleyiciniz (handler) işlenen ID'leri kaydedip mükerrerleri yoksaymalıdır. İdempotentlik, bir entegrasyonun her iki tarafının da ihtiyaç duyduğu bir özelliktir; API kuralları kılavuzu istek tarafını ayrıntılı olarak ele almaktadır.
FAQ
İdempotentlik anahtarı nedir?
Sunucunun, aynı mantıksal işlemin yeniden denendiğini tanımasını sağlayan, istemci tarafından oluşturulan ve istekle birlikte gönderilen bir tanımlayıcıdır. İlk istek yürütülür; aynı anahtarla yapılan yeniden denemeler, tekrar yürütülmek yerine saklanan sonucu döndürür.
Muhasebe API'leri neden idempotentliğe ihtiyaç duyar?
Çünkü mükerrer bir yazma işlemi, gerçek sonuçları olan gerçek bir belge yaratır. Bu durum, sıralı bir numara tüketen, deftere kaydedilen ve KDV beyannamesine giren ikinci bir faturadır. Mükerrer bir okumanın aksine, bu işlem öylece silinemez; bir alacak dekontu ile düzeltilmesi gerekir.
Her yeniden deneme için yeni bir anahtar oluşturmalı mıyım?
Hayır, bu mekanizmayı tamamen işlevsiz hale getirir. Her mantıksal işlem için bir anahtar oluşturun ve bu işlemin her yeniden denemesinde onu yeniden kullanın. Her deneme için yeni bir anahtar, her denemenin yeni bir işlem olarak ele alınması anlamına gelir.
Farklı bir payload ile bir anahtarı yeniden kullanırsam ne olur?
İstek reddedilir. Anahtar bunun aynı işlem olduğunu iddia eder, dolayısıyla değiştirilmiş bir gövde bir şeylerin yanlış olduğunu gösterir. Bu durum büyük olasılıkla istemcideki bir anahtar yeniden kullanım hatasıdır. Önceki sonucu sessizce döndürmek çok daha kötü olurdu.
İdempotentlik tüm mükerrer kayıtları önler mi?
Hayır. Aynı isteğin mükerrer olarak yürütülmesini engeller. Aynı faturayı bağımsız olarak oluşturan iki farklı kullanıcı iki farklı istekte bulunmuştur. Bu, bir idempotentlik anahtarının görebileceği bir şey değil, benzersizlik kısıtlamaları gerektiren bir iş kuralı sorunudır.