in

İdempotency Nedir? API’lerde Tekrarlanan İstekleri Yönetmek

Bir kullanıcı “Siparişi oluştur” düğmesine basar. Sunucu kaydı tamamlar, fakat yanıt telefona ulaşmadan bağlantı kesilir. Kullanıcı yeniden dener. İkinci isteği yeni bir sipariş sayarsanız aynı işlem iki kez gerçekleşebilir. Bu sorunu anlamak için yazılım geliştiricilerin karşısına çıkan temel kavramlardan biri idempotency, Türkçede kullanılan adıyla idempotanstır.

Bu yazıda kavramı HTTP yöntemleriyle ilişkilendirip örnek bir servis randevusu üzerinden inceleyeceğiz. Amaç, her isteği sorgusuz biçimde tekrar etmek değil; tekrarın hangi koşullarda aynı iş sonucunu koruduğunu açıkça tasarlamaktır.

İdempotans ne demektir?

Bir işlemin aynı koşullarda birden fazla uygulanması, hedeflenen etki açısından bir kez uygulanmasıyla aynı sonucu bırakıyorsa işlem idempotenttir. Örneğin bir kaydın durumunu “tamamlandı” olarak ayarlamak ile sayacını bir artırmak farklı davranır. İlkinde aynı değer tekrar atanır; ikincisinde her çalıştırma toplamı değiştirir.

Buradaki ölçüt sunucunun hedeflenen durumudur. Her isteğin yanıt gövdesinin veya durum kodunun aynı olması gerekmez. Bir kaynağı ilk silme isteği başarılı olabilir; sonraki istek kaynak artık bulunmadığı için farklı yanıt verebilir. Kaynağın silinmiş olması açısından hedef korunur. Ayrıca her çağrının erişim günlüğüne yazılması gibi yan etkiler bulunabilir. MDN açıklaması, bu ayrımı özellikle vurgular.

HTTP yöntemleri hangi garantiyi verir?

HTTP standardı RFC 9110, güvenli yöntemlerin yanı sıra PUT ve DELETE yöntemlerini idempotent olarak tanımlar. GET, sunucudaki veriyi okumak amacıyla kullanılır. PUT, belirtilen kaynağın durumunu gönderilen temsile göre oluşturma veya değiştirme anlamı taşır. DELETE ise hedef kaynakla mevcut ilişkilendirmenin kaldırılmasını ister.

POST ve PATCH için yöntem adından hareketle genel bir idempotans garantisi çıkarılamaz. Bununla birlikte, bir uygulama kendi POST uç noktasını ek mekanizmalarla tekrar denemeye dayanıklı tasarlayabilir. Tersine, URL başına PUT yazmak hatalı iş mantığını otomatik olarak düzeltmez. Uygulamanın davranışı kullanılan yöntemin anlamıyla tutarlı olmalıdır.

Örneğin aşağıdaki iki hayalî istekten biri mevcut randevuyu değiştirmeyi, diğeri yeni randevu oluşturmayı gösterir:

PUT /api/randevular/42
{"durum": "onaylandi"}

POST /api/randevular
{"arac_id": 42, "saat": "2026-10-15T10:00:00+03:00"}

İlk işlem belirli bir randevunun durumunu ayarlamayı, ikincisi yeni randevu oluşturmayı ifade eder. Bunlar açıklama amaçlı HTTP örnekleridir; çalışan bir API uygulaması veya test edilmiş entegrasyon kodu değildir.

Bir randevu iki kez oluşursa ne olur?

Örnek bir oto servis uygulaması düşünelim. Müşteri aynı araç için aynı saatte tek bakım randevusu ister. İlk isteğin yanıtı kaybolunca uygulama yeniden gönderim yapar. Sunucu her istekte yeni satır açıyorsa danışmanın ekranında iki randevu belirir. Müşteri tek işlem yaptığını düşünürken atölye kapasitesi iki kez ayrılmış görünür.

Bu örnekte düğmeyi ilk tıklamadan sonra devre dışı bırakmak yararlı bir arayüz önlemidir. Ancak sayfa yenilenmesi, mobil bağlantının kopması veya başka bir istemcinin aynı işlemi göndermesi gibi durumları tek başına çözmez. Tasarım sorusu şudur: Sunucu, gelen isteğin yeni bir iş mi yoksa önceki işin tekrarı mı olduğunu nasıl ayırt edecek?

İdempotency key nasıl kullanılır?

Yaygın yaklaşım, mantıksal işlem için bir anahtar üretmek ve aynı işlemin tekrarlarında bu anahtarı korumaktır. İstemci yeni randevu girişimine bir kimlik verir; yanıt gelmediği için yeniden denediğinde aynı kimliği gönderir. Gerçekten yeni bir randevu açmak istediğinde yeni anahtar oluşturur.

Stripe’ın resmî API belgesi, bunun somut bir örneğini sunar: desteklenen isteklerde anahtar kullanılır ve aynı anahtarla yapılan tekrarlar kayıtlı sonuçla ilişkilendirilir. Aynı anahtarın farklı parametrelerle tekrar kullanılması denetlenir. Bu davranışı tüm API’lerin kendiliğinden sunduğunu varsaymamak gerekir; süreler ve hata davranışları sağlayıcının sözleşmesine bağlıdır.

Bizim randevu örneğimiz için önerilen akış şöyledir:

  1. İstemci mantıksal işlem başlamadan önce bir anahtar üretir.
  2. Sunucu kullanıcıyı doğrular ve isteğin içeriğini kontrol eder.
  3. Anahtar, kullanıcı veya işletme kapsamıyla birlikte aranır.
  4. Tamamlanmış aynı işlem varsa önceki sonuç sunulur.
  5. İşlem yeniyse randevu ve tekrarları tanımlayan kayıt tutarlı biçimde oluşturulur.
  6. Aynı anahtar farklı içerikle gelirse açık bir hata döndürülür.

Veritabanı tarafında hangi ayrıntı önemlidir?

“Önce anahtar var mı bak, yoksa ekle” yaklaşımını tek başına yeterli kabul etmeyin. İki eşzamanlı istek, kayıt henüz oluşmadan kontrolü geçebilir. Örnek tasarımımızda anahtar sahipliğini veritabanı düzeyinde korumak gerekir.

PostgreSQL benzersizlik kısıtları, bir sütun grubunun aynı değer kombinasyonuyla tekrar kaydedilmesini engelleyebilir. İlgili alanların NULL kabul etmemesi de bu örnekte önemlidir. Kavramsal bir anahtar, (isletme_id, islem_turu, idempotency_key) bileşiminden oluşabilir. Randevu ile sonuç kaydının aynı veritabanı işleminde tutulması, kısmen kaydedilmiş durumları azaltmak için değerlendirilebilir.

Haricî takvim, SMS veya başka bir servis çağrısı eklenince tek bir yerel veritabanı işlemi tüm sistemi kapsamaz. Bu nedenle randevu oluşurken bildirim gönderimi başarısız olursa ne yapılacağı ayrıca belirlenmelidir. İdempotans anahtarını her koşulda “tam olarak bir kez çalıştırma” garantisi gibi sunmak yanıltıcıdır.

Uygulamadan önce sorulacak sorular

Bu örnek için yalnızca başarılı senaryoyu denemek yeterli olmaz. Aynı anahtarlı iki isteği eşzamanlı gönderdiğinizde kaç randevu oluşuyor? Aynı anahtarla farklı araç gönderilirse ne oluyor? Yanıt kaybolup işlem yeniden denendiğinde kullanıcı hangi sonucu görüyor? Sunucu yeniden başlarsa tekrar bilgisi korunuyor mu?

Anahtarların ne kadar saklanacağını da işinize göre belirleyin. Saklama süresi dolduktan sonra çok geç gelen bir tekrar yeni işlem sayılabilir. Bunun kabul edilebilir olup olmadığını ürün davranışı belirlemelidir. Ayrıca anahtar tahmin edilemese bile yetkilendirme kontrolünün yerini tutmaz; başka kullanıcının sonucuna erişim ayrı olarak engellenmelidir.

Bir API tasarlarken en yararlı başlangıç, “aynı istek iki kez gelirse ne olur?” sorusunu her veri değiştiren işlem için sormaktır. Cevabı belirsiz olan uç noktaları belirleyin; iş kimliğini, tekrar politikasını ve hata durumlarını birlikte tanımlayın. Böylece bağlantı kesintisi, kullanıcı açısından yeni bir kayıt karmaşasına dönüşmeden yönetilebilir.

Yararlı Oldu Mu?

Bir yanıt yazın

MVC, MVP, MVVM, MVVM-C ve VIPER mimari kalıplarını birbirinden ayıran temel özellikler nedir?