Tüm sistemler çalışıyor Giriş Yap →
bilgi-arşivi/ Sipariş İşlemleri/ Mükerrer Siparişleri Önleme: Idempotency, Kilit ve Güvenli Retry
Sipariş İşlemleri

Mükerrer Siparişleri Önleme: Idempotency, Kilit ve Güvenli Retry

Bir sipariş formunda çift tıklama, mobil bağlantı kesintisi, API zaman aşımı veya worker’ın yeniden başlaması aynı işlemin birden fazla kez gönderilmesine neden olabilir. RoketBayim API dokümantasyonunda istemci tarafından sağlanan bir idempotency key alanı belirtilmediği için mükerrer sipariş koruması bayi sisteminin kendi katmanında kurulmalıdır.

Bu rehber; yerel işlem kimliği, benzersiz veritabanı kısıtı, dağıtık kilit, kontrollü retry ve belirsiz sonuç yönetimiyle siparişlerin yalnız bir kez oluşturulmasına yönelik teknik bir mimari sunar.

Idempotency nedir?

Idempotency, aynı iş isteği birden fazla kez gelse bile iş sonucunun bir kez oluşmasını hedefleyen tasarım ilkesidir. Kullanıcının aynı sipariş düğmesine iki kez basması iki HTTP isteği üretebilir; fakat sistem aynı yerel işlem kimliğini tanıyarak ikinci isteği yeni bir uzak siparişe dönüştürmemelidir.

Buradaki amaç API çağrısını sihirli biçimde geri almak değil, çağrı yapılmadan önce ve sonra yerel kayıt üzerinden tekrarları kontrol etmektir.

Mükerrer sipariş kaynakları

  • Kullanıcının düğmeye tekrar basması
  • Tarayıcının veya mobil uygulamanın isteği yeniden göndermesi
  • Reverse proxy ya da iş kuyruğunun retry uygulaması
  • Worker işlemi tamamladıktan sonra onay mesajını kaydedemeden kapanması
  • API zaman aşımı sonrası uygulamanın otomatik tekrar göndermesi
  • Aynı müşteri siparişinin iki farklı ekip tarafından işlenmesi
  • Toplu sipariş yanıtında başarılı satırların yeniden kuyruğa alınması

Yerel idempotency anahtarı tasarımı

Her iş siparişi için değişmez ve benzersiz bir yerel anahtar üretin. Bu değer müşteri sipariş ID’si, UUID veya iş sisteminizdeki benzersiz kayıt olabilir. Aynı gerçek iş tekrar geldiğinde aynı anahtar kullanılmalıdır.

{
  "idempotency_key": "customer-8451-line-2",
  "service": 1,
  "link": "https://example.com/post",
  "quantity": 1000
}

Rastgele anahtarı her tıklamada yeniden üretmek koruma sağlamaz. Anahtar kullanıcı arayüzü isteğine değil, gerçek iş siparişine bağlı olmalıdır.

Veritabanı benzersiz kısıtı

Yalnız uygulama kodunda “önce var mı kontrol et” sorgusu yarış koşuluna açıktır. İki worker aynı anda kayıt olmadığını görüp ikisi de sipariş oluşturabilir. idempotency_key alanına veritabanı seviyesinde UNIQUE kısıtı ekleyin.

orders.idempotency_key UNIQUE

İlk istek kaydı oluşturur; ikinci istek benzersiz kısıta takılır ve mevcut kaydı döndürür. Bu yaklaşım tek sunucu ve dağıtık worker senaryolarında temel güvenlik katmanıdır.

Önerilen gönderim akışı

1
Yerel kaydı oluşturun. idempotency_key ile draft veya validated sipariş yazın.
2
Benzersizliği doğrulayın. Kayıt zaten varsa yeni API çağrısı yerine mevcut sonucu döndürün.
3
Gönderim sahipliği alın. Durumu atomik olarak validated değerinden submitting değerine geçirin.
4
API isteğini gönderin. İstek özeti ve gönderim zamanını kaydedin.
5
Order ID’yi yazın. Başarılı yanıtta provider_order_id değerini aynı kayda bağlayın.
6
Belirsiz sonucu ayırın. Zaman aşımında otomatik yeniden gönderim yerine unknown durumuna geçin.

Atomik durum geçişi

Worker, yalnız durum validated ise gönderim hakkı almalıdır. Aşağıdaki mantık tek bir atomik UPDATE ile uygulanabilir:

UPDATE orders
SET technical_status = 'submitting'
WHERE id = :id
  AND technical_status = 'validated'

Etkilenen satır sayısı sıfırsa başka bir worker sahipliği almış veya sipariş daha önce gönderilmiştir. Bu worker API çağrısı yapmadan çıkmalıdır.

Dağıtık kilit ne zaman gerekir?

Veritabanı atomik geçişi çoğu senaryoda yeterlidir. Bir sipariş üzerinde uzun süren çok adımlı işlem yapılıyorsa kısa ömürlü dağıtık kilit kullanılabilir. Kilit anahtarı yerel sipariş ID’sine bağlanmalı ve otomatik sona erme süresi olmalıdır.

Kilit tek başına doğruluk garantisi değildir. Worker kilit süresi dolduktan sonra çalışmaya devam edebilir. Bu nedenle veritabanı benzersiz kısıtı ve durum kontrolü yine korunmalıdır.

Retry politikası

Hangi hatalar tekrar edilmemeli?

Service not found, minimum miktar, geçersiz link veya yetersiz bakiye gibi iş kuralı hataları aynı veriyle tekrar gönderilmemelidir. Kullanıcı veya operasyon ekibi veriyi düzeltmeden retry yalnız aynı hatayı üretir.

Hangi hatalar kontrollü tekrar edilebilir?

API’ye hiç bağlanılamadığı açıkça bilinen DNS veya bağlantı kurulumu hataları kontrollü retry adayı olabilir. Ancak isteğin uzak sunucuya ulaşıp ulaşmadığı belirsizse otomatik tekrar mükerrer sipariş riski taşır.

Backoff nasıl uygulanmalı?

Tekrar edilebilir teknik hatalarda sabit hızlı döngü yerine artan bekleme kullanın. Örneğin ilk denemeden sonra kısa, sonraki denemelerde daha uzun aralık belirleyin ve maksimum deneme sayısı koyun. Jitter eklemek çok sayıda worker’ın aynı anda tekrar bağlanmasını önler.

Zaman aşımı neden özel durumdur?

İstemci zaman aşımı, RoketBayim’in isteği almadığı anlamına gelmez. API siparişi oluşturup order ID yanıtını gönderirken bağlantı kesilmiş olabilir. Bu kaydı doğrudan retry kuyruğuna atmak iki sipariş oluşturabilir.

add isteğinde belirsiz zaman aşımını körlemesine tekrar etmeyin. Kaydı unknown durumuna alın ve operasyonel mutabakat uygulayın.

Belirsiz sipariş mutabakatı

API dokümantasyonunda istemci referansıyla sipariş arama işlemi belirtilmediği için unknown kayıtların otomatik eşleştirilmesi sınırlı olabilir. Gönderim zamanı, kullanıcı, servis, link, miktar ve bakiye hareketi gibi yerel kanıtları saklayın. Operasyon ekibi paneldeki yakın zamanlı siparişlerle karşılaştırabilir.

Kesin eşleşme bulunamazsa yeni sipariş kararını otomatik sistem değil yetkili kişi vermelidir. Bu süreç müşteri beklentisi ve olası çift teslimat riskiyle birlikte değerlendirilir.

Arayüz düzeyinde koruma

  • Sipariş gönderilirken düğmeyi devre dışı bırakın.
  • Kullanıcıya “işleniyor” durumu gösterin.
  • Sayfa yenilendiğinde mevcut yerel sipariş kaydını yeniden yükleyin.
  • Aynı formun tarayıcı geri tuşuyla tekrar gönderilmesini engelleyin.
  • Başarılı sonuçta order ID veya yerel referansı gösterin.

Arayüz önlemleri kullanıcı deneyimini geliştirir ancak güvenlik sınırı değildir. Asıl koruma sunucu ve veritabanı katmanında olmalıdır.

Toplu siparişte idempotency

add_bulk en fazla 100 sipariş kabul eder ve işlem atomik değildir. Her satıra kendi yerel idempotency anahtarını verin. Yanıttaki sıra numarasını yerel satırla eşleştirin; order alanı dönen satırı başarılı, error dönen satırı hatalı olarak kaydedin.

İkinci denemede yalnız kesin hatalı ve düzeltilmiş satırlar ele alınmalıdır. Başarılı order ID bulunan satırlar yeni batch’e eklenmemelidir.

Kuyruk sisteminde teslim semantiği

Çoğu iş kuyruğu “at least once” teslimat yapar; aynı mesaj worker’a birden fazla kez ulaşabilir. Mesaj tüketicisi idempotent olmalıdır. Mesaj kimliği veya yerel sipariş anahtarı veritabanında kontrol edilmeden API çağrısı yapılmamalıdır.

Worker başarıyla API çağrısı yaptıktan sonra mesaj onaylanmadan kapanırsa kuyruk aynı mesajı tekrar verebilir. provider_order_id ve technical_status kaydı ikinci tüketimin yeni çağrı yapmasını engeller.

İzleme ve alarm

  • Aynı idempotency key için gelen tekrar istek sayısı
  • submitting durumunda uzun kalan kayıtlar
  • unknown sipariş sayısı ve yaşı
  • Benzersiz kısıt ihlalleri
  • Aynı yerel siparişe bağlanan birden fazla provider order ID
  • Retry sayısı sınırı aşan işler

Test senaryoları

  • Kullanıcının düğmeye hızlıca iki kez basması
  • Aynı sipariş mesajının iki worker’a verilmesi
  • API başarılı yanıtından hemen sonra worker’ın kapanması
  • Bağlantı kurulmadan önce oluşan hata
  • İstek gönderildikten sonra zaman aşımı
  • Kesin iş kuralı hatasının retry edilmemesi
  • Toplu yanıtta yalnız hatalı satırların yeniden işlenmesi
  • Kilit süresi dolarken ikinci worker’ın devreye girmesi

Örnek karar matrisi

order alındı

provider_order_id kaydedilir; hiçbir koşulda aynı yerel sipariş yeniden gönderilmez.

kesin error alındı

failed yapılır; veri düzeltilmeden retry edilmez.

sonuç belirsiz

unknown yapılır; otomatik tekrar yerine mutabakat kuyruğuna alınır.

Canlıya geçiş önerisi

Önce idempotency kaydı ve benzersiz kısıtı ekleyin, ardından worker sahiplik geçişini uygulayın. Retry davranışını kademeli açın ve unknown siparişler için operasyon prosedürü hazırlamadan otomatik retry kullanmayın.

En güvenli kural: kesin order ID varsa tekrar yok; kesin error varsa düzeltmeden tekrar yok; sonuç belirsizse otomatik tekrar yok.

Idempotency kayıt tablosu

Büyük sistemlerde idempotency bilgisini sipariş tablosundan ayrı tutmak yararlı olabilir. Anahtar, istek özeti, oluşturma zamanı, mevcut sonuç ve ilişkilendirilen yerel sipariş ID saklanır. Aynı anahtar farklı servis, link veya miktarla gelirse sessizce eski sonucu döndürmek yerine çakışma hatası verin.

{
  "key": "customer-8451-line-2",
  "request_hash": "sha256:...",
  "local_order_id": "L-1042",
  "status": "tracking",
  "provider_order_id": 23501
}

request_hash hassas veriyi doğrudan içermeden servis, normalize link ve miktarın aynı kalıp kalmadığını kontrol eder. Hash üretiminde alan sırasını ve normalizasyon kuralını sabitleyin.

Anahtar yaşam süresi

Idempotency kayıtlarını çok erken silmek, eski bir istemci retry’ının yeni sipariş oluşturmasına neden olabilir. En az müşteri siparişinin operasyonel yaşam süresi ve makul yeniden gönderim penceresi boyunca saklayın. Kalıcı sipariş referansı kullanılıyorsa anahtarın kalıcı olması daha güvenlidir.

Anahtar tablosu büyüdüğünde yalnız teknik istek kaydını arşivleyebilirsiniz; ancak yerel sipariş ile benzersiz iş referansı arasındaki bağ korunmalıdır.

Outbox yaklaşımı

Kullanıcı siparişini veritabanına yazıp kuyruk mesajı yayınlarken iki farklı sistem kullanılır. Veritabanı başarılı, mesaj yayını başarısız olursa sipariş işlenmez. Outbox deseninde sipariş ve gönderilecek olay aynı veritabanı işlemi içinde yazılır. Ayrı publisher outbox kayıtlarını kuyruğa taşır.

Publisher aynı olayı iki kez gönderebilir; bu nedenle tüketici yine idempotent olmalıdır. Outbox kaydı kayıp işi önler, benzersiz sipariş kısıtı mükerrer işi önler.

Inbox yaklaşımı

Kuyruk tüketicisi mesaj kimliğini inbox tablosuna benzersiz olarak yazar. Aynı mesaj tekrar geldiğinde daha önce işlendiği anlaşılır. Mesaj kimliği ile iş idempotency anahtarı farklı kavramlardır: aynı iş farklı mesaj kimliğiyle gelebileceği için her iki kontrol de gerekebilir.

Retry bütçesi

Her katmanın ayrı ayrı üç retry yapması toplam deneme sayısını katlayabilir. İstemci, proxy, kuyruk ve worker retry davranışlarını birlikte belgeleyin. Sipariş oluşturma çağrısında tek merkezi retry sahibi belirleyin ve toplam bütçeyi sınırlayın.

Status gibi güvenli okuma çağrıları add çağrısından farklı değerlendirilebilir. Durum sorgusu tekrar edildiğinde yeni sipariş oluşturmaz; yine de yoğunluk ve gecikme için backoff uygulanmalıdır.

Kaos ve yarış testi

Test ortamında API çağrısından önce, çağrı sırasında ve order ID kaydından hemen sonra worker’ı kasıtlı durdurun. İki worker’ı aynı siparişe eş zamanlı başlatın ve yalnız birinin gönderim yaptığını doğrulayın. Veritabanı bağlantısını keserek benzersiz kısıt ve recovery davranışını inceleyin.

Başarı ölçütü yalnız testin hata vermemesi değildir. Her senaryoda en fazla bir provider order ID oluşmalı, yerel kayıt açıklanabilir durumda kalmalı ve yeniden işleme kararı denetim izine yazılmalıdır.

Güvenli yönetim ekranı

Operasyon personeli idempotency key, istek hash’i, teknik durum, provider order ID ve deneme geçmişini tek ekranda görebilmelidir. “Retry” düğmesi yalnız kesin olarak gönderilmemiş ve tekrar edilebilir teknik hataya sahip kayıtlarda etkin olmalıdır.

Unknown kayıtta düğme yerine “mutabakata gönder” işlemi sunun. provider_order_id bulunan kayıtta yeniden gönderim tamamen engellenmelidir. Yetkili kişi istisnai olarak yeni sipariş oluşturursa sistem yeni yerel ID üretmeli ve eski kayıtla ilişkiyi olay geçmişine yazmalıdır.

Tüm manuel kararlar gerekçe ve kullanıcı kimliğiyle saklanmalıdır.

Bu kayıtlar düzenli güvenlik incelemesine dahil edilmelidir.