Çift Tıklama Felaketi
Finansal veya işlem odaklı SaaS API’leri geliştirirken, en tehlikeli hatalardan biri “çift tıklama”dır. Bir kullanıcı yavaş bir 3G bağlantısındadır. “İade İşlemini Gerçekleştir” butonuna tıklar, hemen bir şey olmayınca tekrar tıklar. Tarayıcısı, sunucunuza milisaniyeler içinde iki aynı POST isteği gönderir.
Her iki istek, ilk istek işlenmeden önce sunucuya ulaşır; bu nedenle standart doğrulama kuralları her iki istekte de geçer. Kullanıcının hesabı iki kez iade edilir. Veri çoğaltımını ve finansal kayıpları önlemek için, işlem son noktalarınız, tekrar eden isteklere karşı matematiksel olarak dayanıklı olmalıdır. API İdempotensliği uygulamanız gerekmektedir.
Çözüm: İdempotenslik Anahtarları
İdempotent bir sistemde, tam olarak aynı isteği birden fazla kez yapmak, bir kez yapmanın aynı sonucunu üretir. Bunu, ön uç istemcisinin benzersiz bir UUID (İdempotenslik Anahtarı) oluşturmasını ve bunu HTTP Header’larında iletmesini isteyerek gerçekleştiririz (Idempotency-Key: ).
Arka uç, isteği aldığında, bu belirli anahtar için hızlı ve atomik bir cache katmanını (Redis gibi) kontrol eder. Anahtar mevcutsa, sunucu bu tam isteği zaten işlediğini veya hala işlenmekte olduğunu bilir ve tekrar eden isteği güvenli bir şekilde iptal eder.
İdempotanslık Middleware’ini Tasarlamak
Laravel API rotalarımızı, bu anahtarları Redis atomik kilitler kullanarak değerlendiren bir middleware oluşturarak sorunsuz bir şekilde koruyabiliriz.
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;
class EnforceIdempotency
{
public function handle(Request $request, Closure $next)
{
// 1. Sadece durum değiştiren metodlarda uygulayın
if (in_array($request->method(), ['GET', 'HEAD', 'OPTIONS'])) {
return $next($request);
}
// 2. İstek başlıklarından anahtarı çıkarın
$idempotencyKey = $request->header('Idempotency-Key');
if (!$idempotencyKey) {
return response()->json(['error' => 'İdempotenslik Anahtarı başlığı gereklidir.'], 400);
}
$cacheKey = "idempotency_request:{$idempotencyKey}";
// 3. ✅ KURUMSAL MODEL: Atomik Kilit
// Cache::add() yalnızca anahtar daha önce mevcut değilse true döner.
// Bu, Redis'te atomik bir işlemdir, yarış koşullarını önler.
$lockAcquired = Cache::add($cacheKey, 'processing', now()->addHours(24));
if (!$lockAcquired) {
// Bu anahtara sahip bir istek zaten çalışmakta ya da yakın zamanda çalıştı!
return response()->json([
'message' => 'Tekrar eden istek tespit edildi. İşlem güvenli bir şekilde iptal edildi.'
], Response::HTTP_CONFLICT);
}
// 4. Güvenli, kilitli isteği ile devam edin
$response = $next($request);
return $response;
}
}
Mühendislik ROI’si
Middleware katmanında İdempotenslik Anahtarlarını zorlayarak, kullanıcı sabırsızlığı veya dalgalı ağ tekrarları nedeniyle meydana gelen çift işlem hatalarını tamamen ortadan kaldırırsınız. Arka uç, matematiksel olarak tahmin edilebilir hale gelir, müşterinizin finansal verilerini korur ve veritabanınızdaki çift ücretlerin manuel olarak düzeltilmesi için operasyonel yükü önemli ölçüde azaltır.
Kaynak: Orijinal Makale


