TB
← Tüm yazılar

Idempotent yazma islemleri

Dagitik sistemlerde idempotent API tasarımı, tekrar denemelerde veri tutarlılığı ve idempotency key mekanizmalarinin uygulanmasi.

Dagitik sistemlerde ag timeout, yük dengeleyici retry ve istemci tekrar denemeleri kacinilmazdir. Ayni yazma istegi birden fazla kez sunucuya ulasabilir. Idempotency, ayni islemin tekrarlanmasinin sistem durumunu tek uygulama ile ayni birakmasini garanti eder. Odeme, siparis, stok rezervasyonu gibi kritik islemlerde idempotent tasarım veri tutarlılığı icin zorunludur. Idempotency tasarım karari erken alinmali; sonradan eklemek mevcut istemcilerin tumunun guncellenmesini gerektirir.

HTTP idempotency temelleri

HTTP acisindan GET, PUT, DELETE idempotent kabul edilir. POST genelde degildir: iki kez POST iki kaynak olusturabilir. Idempotent yazma POST'u idempotent hale getirmek anlamina gelir: istemci benzersiz anahtar gonderir, sunucu ayni anahtarla gelen tekrarlarda ilk sonucu doner. PATCH idempotency tasarima baglidir; JSON Patch operasyonlari tekrarlaninca farklı sonuç uretebilir.

Idempotency-Key header

POST /v1/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{ "amount": 1000, "currency": "TRY", "orderId": "ord-99" }

Stripe, PayPal ve bircok odeme API'si bu header'i kullanır. Sunucu anahtari ve istek ozetini saklar. Ayni anahtar + ayni govde ile gelen istek cache yaniti doner. Farklı govde + ayni anahtar 409 Conflict ile reddedilir. Anahtar formati UUID v4 veya istemci urettigi yüksek entropili string olmalidir.

POST idempotency store modeli

Idempotency kaydi tipik alanlar:

  • Key: Istemci urettigi benzersiz deger.
  • RequestHash: Govde fingerprint (SHA-256).
  • ResponseStatus: Ilk yanıt HTTP kodu.
  • ResponseBody: Ilk başarılı yanıt govdesi.
  • ExpiresAt: TTL (örneğin 24 saat).
  • State: processing, completed, failed.

Redis veya PostgreSQL tablosu kullanılır. Unique constraint key uzerinde race condition onler. Processing durumunda gelen paralel istekler bekler veya 409 alir.

Işlem akisi ve race condition

  1. Istek gelir, Idempotency-Key okunur.
  2. Kayıt var mi kontrol edilir.
  3. Varsa completed: cache yanıt donulur.
  4. Yoksa: processing kaydi atomic insert ile oluşturulur.
  5. Is mantigi çalışır, sonuç kaydedilir, state completed olur.
  6. Yanıt istemciye donulur.

Paralel ayni key ile iki istek gelirse biri unique violation yakalar ve digerinin tamamlanmasini bekler. Uzun suren islemlerde polling endpoint veya webhook tercih edilir.

Odeme senaryosu

Cift odeme en kotu senaryodur. Idempotency key zorunlu tutulur. Işlem referansi banka mutabakatinda eslestirilir. Partial failure'da durum sorgulama endpoint'i (GET /payments/{id}) istemciye netlik verir. Odeme saglayicisi timeout dondugunde istemci ayni key ile retry yapar; sunucu odeme durumunu saglayicidan sorgular veya pending birakir.

Outbox pattern ve mesaj idempotency

Veritabanı yazma ve event yayini atomik olmali. Transactional outbox tablosuna event yazilir; ayri worker Kuyruga publish eder. Kuyruk tuketicilerinde de idempotency gerekir: mesaj tekrar teslim edilebilir. Consumer mesaj ID'sini islenen tablosuna yazar; duplicate atlanir. At-least-once delivery + idempotent consumer = effectively once semantics.

ASP.NET Core middleware ornegi

public sealed class IdempotencyMiddleware
{
    private readonly RequestDelegate _next;

    public IdempotencyMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext ctx, IIdempotencyStore store)
    {
        if (ctx.Request.Method != HttpMethods.Post)
        {
            await _next(ctx);
            return;
        }

        var key = ctx.Request.Headers["Idempotency-Key"].FirstOrDefault();
        if (string.IsNullOrEmpty(key))
        {
            await _next(ctx);
            return;
        }

        var cached = await store.GetAsync(key, ctx.RequestAborted);
        if (cached is not null)
        {
            ctx.Response.StatusCode = cached.StatusCode;
            ctx.Response.ContentType = cached.ContentType;
            await ctx.Response.WriteAsync(cached.Body);
            return;
        }

        await using var scope = await store.TryBeginAsync(key, ctx.RequestAborted);
        if (scope is null)
        {
            ctx.Response.StatusCode = StatusCodes.Status409Conflict;
            return;
        }

        var originalBody = ctx.Response.Body;
        await using var buffer = new MemoryStream();
        ctx.Response.Body = buffer;
        await _next(ctx);
        buffer.Position = 0;
        var body = await new StreamReader(buffer).ReadToEndAsync();
        await store.CompleteAsync(key, ctx.Response.StatusCode,
            ctx.Response.ContentType ?? "application/json", body);
        buffer.Position = 0;
        await buffer.CopyToAsync(originalBody);
    }
}

PUT, PATCH ve DELETE

PUT zaten idempotent: ayni tam govde tekrar gonderilince sonuç ayni. DELETE ikinci cagride 404 veya 204 politika ile tutarlı olmali. Soft delete'te ikinci silme ayni durumu korur. Upsert pattern PUT ile desteklenir; istemci tüm temsili gonderir.

Istemci rehberligi ve test

Dokumantasyonda: UUID v4 uretin, ayni işlem icin ayni key kullanın, retry'da key degistirmeyin, TTL suresi belirtin. Test senaryolari: ayni key iki kez POST tek kayıt; ayni key farklı govde 409; paralel ayni key tek işlem; TTL sonrasi politika. Performans: Redis sub-ms lookup; büyük response cache bellek tuketir.

Güvenlik ve idempotency vs deduplication

Key tahmin edilememeli. Baska kullanıcının key'i ile istek 403. Rate limit key enumeration onler. Deduplication genel tekrar tespiti; idempotency istemci kontrollu anahtar ile garanti. Event sourcing'de aggregate version optimistic lock sağlar.

Saga ve uzun suren işlemler

Cok adimli is akislarinda her adim kendi idempotency key'ine sahip olabilir veya ust işlem key'i alt adimlara propagate edilir. Saga compensating transaction'lari da idempotent olmalidir; aksi halde geri alma tekrarinda veri bozulur. Choreography tabanli sistemlerde mesaj basliklarinda correlation ve idempotency key birlikte tasinir.

Veritabanı seviyesinde garantiler

Unique constraint business key uzerinde (örneğin externalReferenceId) son savunma hattidir. Idempotency store ile birlikte kullanıldığında cift kayıt riski minimize edilir. Serializable isolation pahalidir; çoğu senaryoda optimistic concurrency ve idempotency key yeterlidir.

Webhook retry ve idempotency

Giden webhook'larda alici taraf 5xx dondugunde gonderici retry yapar. Webhook govdesine eventId eklenir; alici islenen event tablosunda duplicate kontrolu yapar. Imza dogrulamasi (HMAC) ile govde butunlugu korunur. Retry backoff exponential olmali; alici tarafinda idempotent handler zorunludur.

Siparis oluşturma ornegi

POST /orders
Idempotency-Key: client-req-7a2b
{ "customerId": "c1", "lines": [{ "sku": "A1", "qty": 2 }] }

// Ilk cagri: 201 + orderId
// Retry ayni key: 201 + ayni orderId, yeni kayit yok

Siparis numarasi sunucu üretir; istemci key ile bağlantı kurar. Stok dusumu ve odeme çağrısı tek transaction veya saga ile yönetilir. Partial failure'da istemci GET ile durum sorgular.

Idempotency store temizligi

TTL dolan kayitlar arka plan job ile silinir. Büyük response body'ler sikistirilarak saklanabilir. Completed kayitlar icin cold storage arsivleme audit amacli tutulabilir. Store kapasitesi izlenmeli; Redis memory limit asiminda eviction politikasi dikkatle secilmeli.

Natural idempotency anahtarlari

Bazi işlemler dogal olarak idempotent anahtara sahiptir: banka işlem referansi, e-fatura UUID, marketplace listing ID. Istemci kendi key üretmek yerine bu is mantigi anahtarini gonderebilir. Sunucu unique constraint ile cift kaydi engeller. Natural key ile Idempotency-Key birlestirilebilir: store'da ikisi de indexlenir.

Monitoring ve alerting

Idempotency cache hit orani izlenmelidir. Ani artis ag sorunu veya istemci retry bug'ina isaret eder. 409 Conflict orani yuksekse istemci ayni key ile farklı govde gonderiyor demektir. Processing state'te takili kayitlar icin timeout ve cleanup job tanimlanir. Dashboard'da key basina latency ve store boyutu gorulur.

Distributed lock alternatifi

Bazi ekipler idempotency yerine distributed lock (Redis Redlock) kullanır. Lock işlem suresince cift calismayi onler ancak lock sahibi cokerse deadlock riski vardir. Idempotency key genelde daha basit ve stateless worker'larla uyumludur. Hibrit model: lock + idempotency store birlikte kullanılabilir.

Her kritik POST endpoint icin idempotency zorunlulugu OpenAPI extension ile isaretlenebilir: x-idempotency-required: true.