Bayiden gelen mesaj hep aynı cümleyle başlar: "Tek sipariş verdim, bakiyemden iki kez düştü." Çoğu kişi bunu duyunca müşteriyi suçlar; "iki kez tıklamıştır" der, farkı elle iade eder, konuyu kapatır. Ama kayıtlara dikkatle bakarsan çoğu çift düşümde iki tıklama olmadığını görürsün. Tek istek var, iki kayıt var. Arada bir şey isteği senin adına tekrar etmiş.
İşte anlaşılmayan nokta şu: modern web yığınında isteği tekrar eden sen değilsin. Tarayıcı, vekil sunucu, ödeme sağlayıcının bildirim servisi, kuyruk işçisi ya da senin kendi yeniden deneme mantığın tekrar eder. Bu tekrarlar tasarım gereğidir ve engellenemez. Engellenebilecek tek şey, aynı işin iki kez muhasebeleşmesidir. Bu yazıda çift düşümün beş gerçek kaynağını ve bir e-pin satış scripti içinde bunu mimari olarak imkânsız kılan deseni anlatıyorum.
Çift tıklama gerçekten suçlu mu, yoksa günah keçisi mi?
Çift tıklama vardır, ama tek başına açıklaması zayıftır. Çünkü iki isteği bir buton kilidiyle engellemeye çalışmak, sorunu tarayıcıya havale etmek demektir. Bağlantı kopan bir kullanıcı sayfayı yeniler, ödeme sağlayıcı yanıt alamadığı bildirimi tekrar gönderir, mobil ağ aynı isteği yeniden iletir. Butonu kilitlemen bunların hiçbirini durdurmaz.
Doğru bakış şu: arayüz tarafındaki her önlem kolaylıktır, sunucu tarafındaki benzersizlik ise garantidir. Bir epin yazılım kurulumunda çift düşüm yaşıyorsan, eksik olan garantidir. Kolaylık katmanını ne kadar güçlendirirsen güçlendir, garanti yoksa sorun belirli aralıklarla geri döner.
Aynı saniyede iki istek: kilitsiz okumanın bedeli
Bakiye düşümü çoğu kurulumda üç adımdır: oku, karşılaştır, yaz. Bu üç adım kilitsiz yapılırsa aynı saniyeye düşen iki istek de aynı başlangıç bakiyesini okur. İkisi de yeterli bakiye görür, ikisi de sipariş açar, ikisi de düşüm yazar. Sonuç: tek siparişlik mal, iki siparişlik para.
Kilit burada tercih değil zorunluluktur. Bakiye satırı işlem içinde kilitli okunmalı, düşüm aynı işlem içinde yazılmalı ve işlem onaylanana kadar başka hiçbir istek o satırı okuyamamalıdır. Bunun daha keskin bir biçimi, düşümü tek bir koşullu güncelleme ifadesine indirmektir: bakiye ancak yeterliyse azalır ve etkilenen satır sayısı sıfırsa istek reddedilir. Tek ifade, tek doğru sonuç.
| Tekrar kaynağı | Nasıl tetiklenir | Tipik iz | Doğru savunma |
|---|---|---|---|
| Kullanıcı yenilemesi | Form yeniden gönderilir | Saniyeler arayla aynı sepet | İstek anahtarı ile tekilleştirme |
| Ödeme bildirimi tekrarı | Sağlayıcı yanıt alamaz | Aynı işlem numarası birden fazla kayıt | Sağlayıcı işlem numarasına benzersiz kısıt |
| Kuyruk işçisi tekrarı | İş onaylanmadan kopar | Aynı iş kimliği iki kez | İş kimliği bazlı işlenmiş kaydı |
| Eşzamanlı istek | Aynı anda iki oturum | Milisaniye farkla iki hareket | Satır kilidi veya koşullu güncelleme |
| İç yeniden deneme | Zaman aşımı sonrası tekrar çağrı | Başarısız sanılan başarılı işlem | Yeniden denemeyi idempotent uçla yap |
| Manuel müdahale | Operatör siparişi elle tamamlar | İki farklı kullanıcıdan hareket | Durum geçişini tek yönlü kılmak |
Idempotency anahtarı: bir kez yaz, kaç kez sorulursa sorulsun
Bu sorunun kalıcı çözümü tek bir fikirde toplanır: her para hareketinin, isteğin kendisinden türeyen bir kimliği olmalı. Sepet oluşturulurken üretilen bir anahtar, siparişle birlikte gönderilir ve hareket tablosunda bu anahtar üzerinde benzersiz kısıt bulunur. İkinci istek geldiğinde veritabanı onu kabul etmez; sistem hata döndürmez, ilk işlemin sonucunu aynen döndürür.
Bu desenin güzelliği, tekrarları engellemeye çalışmaktan vazgeçmesidir. Tekrarlar gelsin; hepsi aynı sonucu üretir. Bir dijital ürün satış scripti için bu, ödeme sağlayıcıları ve otomatik teslimat kuyruklarıyla çalışırken pazarlık konusu olmayan bir gerekliliktir. Anahtar yoksa, sağlayıcının her tekrar bildirimi yeni bir satış gibi muhasebeleşir.
Çift düşüm vakalarının kaynak dağılımı
- Bildirim ve kuyruk tekrarları 50%
- Eşzamanlı kilitsiz yazım 22%
- Kullanıcı yenilemesi 18%
- Manuel müdahale 10%
Ödeme bildirimleri neden bilerek birden fazla gelir?
Sağlayıcı sana bildirim gönderir ve başarılı yanıt bekler. Yanıt gecikirse, 200 dönmezse ya da ağ koparsa aynı bildirimi tekrar gönderir. Bu bir arıza değil, garantili teslimatın doğal bedeli. Senin tarafta yapılması gereken tek şey, sağlayıcının işlem numarasını kayıt anahtarı olarak kullanmak ve aynı numarayı ikinci kez gördüğünde "zaten işlendi" diyerek hızlıca başarı dönmek.
Sık yapılan hata, bildirimi işlerken önce bakiyeyi yazıp sonra işlenmişlik kaydını atmaktır. Arada bir kopma olursa bakiye yazılmış ama işaret konmamış olur; tekrar geldiğinde ikinci kez yazılır. Sıra tersine çevrilmeli: işlenmişlik kaydı ile finansal hareket aynı işlem içinde yazılmalı, böylece ikisi ya birlikte var olur ya hiç olmaz.
Düzeltme yaparken ikinci bir hata üretmemek
Çift düşümü fark ettiğinde ilk refleks bakiyeyi elle artırmaktır. Bunu hareket kaydı yazmadan yaparsan tabloların bir daha tutmaz ve bir sonraki denetimde aynı vakayı yeniden keşfedersin. Doğru yol, fazla düşümü açıkça ters bir hareketle iptal etmek, ilgili siparişi iptal durumuna almak ve varsa ayrılmış kodu havuza geri döndürmektir.
Ardından mutlaka bir tespit işi kur: aynı kullanıcıda kısa aralıkta aynı tutarlı iki hareketi arayan basit bir sorgu, bu sorunu sen duymadan önce yakalar. Olgun bir e-pin satış yazılımı bu tespiti kendi içinde barındırır ve şüpheli çiftleri yöneticiye bildirir.
Aynı sipariş isteğini bilinçli olarak iki kez gönder. Sistem ikinci istekte ilk siparişin sonucunu döndürüyorsa korumalısın; ikinci bir sipariş numarası üretiyorsa idempotency anahtarın yok demektir.
Neyi şart koşarsan bu sorun bir daha görünmez?
Çift düşüm, disiplinle tamamen ortadan kaldırılabilen nadir hatalardan biridir. Gereken şey uzun bir özellik listesi değil, aşağıdaki kısa sözleşmeye uyan bir mimari. Her satırı kendi kurulumunda işaretle; eksik kalanlar seni gelecekte arayacak şikâyetlerin listesidir.
| # | Kontrol | Etkisi | Öncelik |
|---|---|---|---|
| 1 | Her para hareketinde benzersiz istek anahtarı | Tekrarlar zararsızlaşır | Kritik |
| 2 | Sağlayıcı işlem numarasında benzersiz kısıt | Bildirim tekrarı çift yazmaz | Kritik |
| 3 | İşlenmişlik kaydı ile hareket aynı işlemde | Yarım durum oluşmaz | Kritik |
| 4 | Koşullu güncelleme ile bakiye düşümü | Eşzamanlılık çözülür | Kritik |
| 5 | Durum geçişleri tek yönlü | Tamamlanan sipariş yeniden işlenmez | Yüksek |
| 6 | Kuyruk işleri iş kimliğiyle tekilleştirilir | İşçi tekrarı güvenli olur | Yüksek |
| 7 | Kısa aralıkta aynı tutar tespit uyarısı | Sen duymadan yakalanır | Orta |
| 8 | Manuel düzeltmeler ters hareketle yapılır | Denetim izi korunur | Yüksek |
Tekrarları engelleme; zararsız hale getir
Pinova'nın cüzdan ve sipariş katmanı istek anahtarı, benzersiz kısıt ve koşullu düşüm üzerine kuruludur. Teknik kapsamı özellikler bölümünde, paket seçeneklerini fiyat bölümünde bulabilirsin.
Butonu kilitlemek çift düşümü çözer mi?
Hayır, yalnızca azaltır. Ağ kopmaları, sayfa yenilemeleri ve ödeme bildirimi tekrarları arayüzden bağımsız gelir. Garanti sunucu tarafındaki benzersizlik kısıtıdır.
Idempotency anahtarını nerede üretmeliyim?
İstemci tarafında, sepet ya da ödeme niyeti oluşturulduğu anda üretilip sunucuya taşınmalı. Sunucuda üretirsen her tekrar yeni anahtar alır ve koruma çalışmaz.
Aynı müşteri gerçekten iki kez almak isterse ne olur?
İkinci sipariş yeni bir anahtarla, yani yeni bir sepet ya da ödeme niyetiyle gelir. Bu yüzden meşru tekrar satın almalar engellenmez; yalnızca aynı isteğin kopyaları engellenir.
Fazla düşen bakiyeyi nasıl düzeltmeliyim?
Ters bir hareket kaydıyla. Bakiye sütununu doğrudan elle değiştirmek hareket toplamıyla arasındaki bağı koparır ve mutabakatı kalıcı biçimde bozar.
Çift düşüm bir şans işi değil, mimari eksikliğin ölçülebilir sonucudur; doğru kurulmuş bir dijital ürün satış yazılımı aynı isteği yüz kez alsa bile tek kayıt üretir. Kendi akışını yukarıdaki testle dene, ardından aynı senaryoyu canlı demo üzerinde çalıştır ve ikinci isteğin nasıl sessizce aynı sonuca döndüğünü kendi gözünle gör.

