Idempotency Nedir? Güvenilir API Tasarımı
Kullanıcı ödeme butonuna iki kez bastığında çift çekim mi oluyor? Ağ hatası sonrası retry yapıldığında sipariş tekrar mı oluşuyor? Idempotency ile aynı işlemi tekrar etmenin güvenli olduğundan emin olun.
Idempotency Tanımı
Bir işlem idempotenttir, aynı girdiyle kaç kez çağrılırsa çağrılsın aynı sonucu üretiyorsa.
Idempotent: f(x) = f(f(x))
PUT /users/42 {name: "Ali"} → Kaç kez çağrılırsa çağrılsın, sonuç aynı
Non-Idempotent: POST /orders {item: "Laptop"} → Her çağrıda yeni sipariş oluşur!
HTTP Metotlarının Idempotency'si
| Metot | Idempotent | Açıklama | |-------|-----------|----------| | GET | ✅ Evet | Veri okur, değiştirmez | | PUT | ✅ Evet | Kaynağı tamamen günceller | | DELETE | ✅ Evet | Silinen kaynak tekrar silinmez | | HEAD | ✅ Evet | GET gibi, body yok | | POST | ❌ Hayır | Her çağrıda yeni kaynak oluşturabilir | | PATCH | ❌ Genellikle | Increment gibi operasyonlar non-idempotent |
Idempotency Key
POST isteklerini idempotent yapmak için unique bir anahtar kullanın:
// İstemci tarafı
const response = await fetch('/api/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': 'pay_abc123_attempt1' // Unique key
},
body: JSON.stringify({
amount: 1500,
currency: 'TRY',
customerId: '42'
})
});
Sunucu Tarafı Implementasyonu
async function processPayment(req: Request) {
const idempotencyKey = req.headers['idempotency-key'];
// 1. Daha önce işlendi mi kontrol et
const existing = await redis.get(`idempotency:${idempotencyKey}`);
if (existing) {
return JSON.parse(existing); // Aynı yanıtı döndür
}
// 2. İşlemi gerçekleştir
const result = await paymentGateway.charge(req.body);
// 3. Sonucu sakla (24 saat TTL)
await redis.set(
`idempotency:${idempotencyKey}`,
JSON.stringify(result),
'EX', 86400
);
return result;
}
Gerçek Dünya Örnekleri
Stripe
POST /v1/charges
Idempotency-Key: unique-key-123
Sipariş Sistemi
// Kötü: Her retry yeni sipariş oluşturur
POST /orders → Sipariş #1001
POST /orders → Sipariş #1002 (aynı sipariş, çift!)
// İyi: Idempotency key ile
POST /orders (key: "order-abc") → Sipariş #1001
POST /orders (key: "order-abc") → Sipariş #1001 (aynı sonuç döner)
Idempotent Tasarım Desenleri
1. Upsert (INSERT or UPDATE)
INSERT INTO users (id, name, email)
VALUES (42, 'Ali', 'ali@example.com')
ON CONFLICT (id) DO UPDATE SET name = 'Ali', email = 'ali@example.com';
2. Conditional Update
UPDATE inventory SET stock = stock - 1
WHERE product_id = 123 AND stock > 0
AND last_update_id != 'tx_abc123'; -- Aynı transaction tekrar etmez
3. Deduplication
// Message queue consumer
async function handleMessage(message: Message) {
const processed = await redis.sismember('processed_messages', message.id);
if (processed) return; // Zaten işlendi
await processOrder(message.data);
await redis.sadd('processed_messages', message.id);
}
Ne Zaman Idempotency Gerekli?
- ✅ Ödeme işlemleri (çift çekim önleme)
- ✅ Sipariş oluşturma (çift sipariş önleme)
- ✅ E-posta gönderme (çift bildirim önleme)
- ✅ Message queue consumer'ları (at-least-once delivery)
- ✅ Webhook handler'ları (provider retry yapabilir)
Best Practices
- Tüm POST endpoint'lerinde idempotency key destekleyin
- Key'i istemci oluştursun — UUID veya anlamlı string
- Sonucu cache'leyin — 24-48 saat TTL ile
- Race condition'a dikkat — İlk isteği işlerken ikincisi gelirse mutex kullanın
- HTTP 409 Conflict — Key tekrar kullanıldığında ancak farklı body ile
- Loglama — Duplicate istekleri loglayarak takip edin
Sonuç
Idempotency, dağıtık sistemlerde güvenilirliğin temelidir. Ağ hataları, retry'lar ve çift tıklamalar kaçınılmazdır — sisteminiz buna hazır olmalıdır. Özellikle ödeme ve sipariş gibi kritik işlemlerde idempotency key kullanmak hayati önem taşır.
Idempotency ve API tasarımını LabLudus platformunda öğrenin.