Kurumsal yazılım, güvenlik ve mühendislik notları

Outbox Pattern, Idempotency ve Retry: Güvenilir Entegrasyon Rehberi

Middleware ve Entegrasyon Mimarisi ·

Outbox pattern — giden mesajları bekleten gri metal posta kutusu | Aksiyon Soft

Outbox pattern nedir, entegrasyonda neden hayat kurtarır?

Outbox pattern (transactional outbox), bir iş kaydını veritabanına yazarken diğer sistemlere gidecek mesajı da aynı transaction içinde bir “giden kutusu” tablosuna yazmanızı, mesajı ise ayrı bir süreçle güvenle yayınlamanızı sağlayan bir tasarım desenidir. Neden gerekli? Çünkü “önce siparişi kaydet, sonra kuyruğa mesaj at” yaklaşımı, iki adım arasında uygulama çöktüğünde ERP’de onaylı ama faturası hiç kesilmemiş siparişler bırakır. Tersi de olur: mesaj gider ama transaction geri alınır, var olmayan bir sipariş için e-Fatura isteği oluşur.

Bu rehber; ERP, e-ticaret, ödeme sağlayıcısı ve e-Fatura özel entegratörü arasında güvenilir veri akışı kurmak zorunda olan yazılım ekipleri ve teknik liderler için yazıldı. Outbox tablosunun tasarımını, relay’in polling veya CDC (Debezium) ile çalıştırılmasını, en az bir kez teslimi, idempotency anahtarlarını, üstel geri çekilme ve jitter ile yeniden denemeyi, zaman aşımlarını, dead-letter kuyruğunu, circuit breaker’ı ve sıralama sorununu PostgreSQL ve TypeScript örnekleriyle anlatıyor. Mimarinin bütün resmi için kurumsal entegrasyon ve ara katman mimarisi yazımızla birlikte okumanızı öneririz.

Şema: outbox pattern akışı — PostgreSQL transaction, relay, broker, idempotent e-Fatura tüketicisi ve DLQ
Sipariş ve outbox kaydı tek transaction’da yazılır; relay mesajı broker’a taşır, idempotent tüketici tekrarları ayıklar, işlenemeyen mesaj DLQ’ya düşer.

Kısaca

  • Dual-write (veritabanına ve broker’a ayrı ayrı yazmak) iki sistemi tutarsız bırakabilir; outbox bunu tek transaction’a indirger.
  • Relay, outbox’taki kayıtları polling (ör. FOR UPDATE SKIP LOCKED) veya CDC (Debezium) ile yayınlar.
  • Outbox en az bir kez teslim sağlar; bu yüzden tüketiciler mesaj kimliğiyle tekrarları ayıklamalı, yani idempotent olmalıdır.
  • Yeniden deneme; zaman aşımı, üst sınırlı üstel geri çekilme, jitter ve deneme sınırıyla birlikte tasarlanır.
  • İşlenemeyen (zehirli) mesajlar DLQ’ya alınır; DLQ’nun sahibi, alarmı ve yeniden işleme prosedürü olur.

• • •

Dual-write problemi nedir?

Chris Richardson’ın microservices.io sitesindeki tanımıyla sorun şudur: bir servis komutu hem veritabanını güncellemeli hem de bir broker’a mesaj göndermelidir ve bu iki işlem atomik olmalıdır. Veritabanını ve broker’ı kapsayan dağıtık transaction (2PC) ise çoğu zaman mümkün değildir ya da istenmez. Transaction ortasında mesaj göndermek güvenilir değildir, çünkü commit olacağının garantisi yoktur; commit’ten sonra göndermek de güvenilir değildir, çünkü servis tam o anda çökebilir.

Somut örnek: ERP siparişi → e-Fatura

Müşteri siparişi onaylandığında ERP’de sipariş “onaylandı” durumuna geçer ve özel entegratöre fatura isteği gönderilmesi gerekir. Uygulama siparişi commit eder, ardından mesajı gönderirken bağlantı kopar. Sonuç: sipariş onaylı, fatura yok; muhasebe bunu ay sonu mutabakatında fark eder. Outbox ile sipariş güncellemesi ve OrderConfirmed olayı aynı transaction’da yazılır; ya ikisi birden kalıcı olur ya da hiçbiri.

Somut örnek: ödeme geri çağrısı (callback)

Ödeme sağlayıcısı başarılı ödemeyi bir webhook ile bildirir ve sağlayıcılar yanıt alamadıklarında aynı bildirimi tekrar gönderebilir. Uygulamanızın bu bildirimi bir kez “sipariş ödendi” olarak işlemesi, kargo ve fatura süreçlerini bir kez tetiklemesi gerekir. Burada iki desen birlikte çalışır: gelen tarafta idempotent işleme, giden tarafta outbox.

• • •

Transactional outbox nasıl çalışır?

microservices.io’daki desen tanımına göre gönderen servis mesajı, iş varlıklarını güncelleyen transaction’ın parçası olarak veritabanındaki outbox tablosuna yazar; ayrı bir message relay süreci bu mesajları broker’a iletir. Desen üç şey vaat eder: 2PC kullanılmaz, mesajlar yalnızca transaction commit olursa gönderilir ve mesajlar servisin ürettiği sırayla yayınlanır. Aynı kaynak önemli bir uyarıyı da yapar: relay bir mesajı yayınladıktan sonra, bunu kaydedemeden çökerse yeniden başladığında mesajı tekrar yayınlar.

Outbox tablosu tasarımı

Debezium’un Outbox Event Router dokümantasyonu varsayılan yapılandırma için id, aggregatetype, aggregateid, type ve payload sütunlarını bekler: aggregatetype hedef topic’i, aggregateid mesaj anahtarını (dolayısıyla Kafka partition’ındaki sıralamayı) belirler, id ise tekrarları ayıklamak için başlık olarak taşınır. Polling ile çalışacaksanız yayın durumu ve deneme bilgisi için birkaç sütun daha eklemek işinizi kolaylaştırır:

-- Outbox: iş verisiyle aynı veritabanında, aynı transaction'da yazılır
CREATE TABLE outbox (
  id              uuid         PRIMARY KEY,
  aggregatetype   varchar(255) NOT NULL,          -- ör. 'order'
  aggregateid     varchar(255) NOT NULL,          -- ör. sipariş numarası (mesaj anahtarı)
  type            varchar(255) NOT NULL,          -- ör. 'OrderConfirmed'
  payload         jsonb        NOT NULL,
  created_at      timestamptz  NOT NULL DEFAULT now(),
  published_at    timestamptz,                    -- polling relay için
  attempts        integer      NOT NULL DEFAULT 0,
  next_attempt_at timestamptz  NOT NULL DEFAULT now(),
  last_error      text
);

CREATE INDEX outbox_pending_idx
  ON outbox (next_attempt_at)
  WHERE published_at IS NULL;

-- Uygulama: sipariş güncellemesi ve olay tek transaction'da
BEGIN;
UPDATE orders SET status = 'CONFIRMED' WHERE id = $1;
INSERT INTO outbox (id, aggregatetype, aggregateid, type, payload)
VALUES ($2, 'order', $1, 'OrderConfirmed', $3::jsonb);
COMMIT;

Relay: polling mi, CDC mi?

Polling relay, bekleyen kayıtları belirli aralıklarla okuyup yayınlar. PostgreSQL dokümantasyonu, SKIP LOCKED seçeneğinin hemen kilitlenemeyen satırları atladığını ve bunun genel amaçlı sorgular için uygun olmasa da birden fazla tüketicinin kuyruk benzeri bir tabloyu okumasında kilit çekişmesini önlemek için kullanılabileceğini belirtir. Basit, ek altyapı istemez; dezavantajı sorgu yükü ve polling aralığı kadar gecikmedir.

-- Birden fazla relay örneği aynı satırı almadan çalışabilir
SELECT id, aggregatetype, aggregateid, type, payload, attempts
FROM outbox
WHERE published_at IS NULL
  AND next_attempt_at <= now()
ORDER BY created_at
LIMIT 100
FOR UPDATE SKIP LOCKED;

CDC relay ise veritabanının işlem günlüğünü (transaction log) okur. Debezium projesinin 2019 tarihli outbox yazısında Gunnar Morling, log tabanlı CDC’nin polling yaklaşımına göre çok düşük ek yükle ve neredeyse gerçek zamanlı çalıştığını anlatır. Aynı yazıdaki ilginç bir ayrıntı: kayıt eklenip aynı transaction’da silinse bile INSERT olayı günlükte yer aldığı için yayınlanır; tablo sürekli boş kalır ve ayrı bir temizlik işine gerek olmaz. Bedeli, Kafka Connect ve Debezium operasyonunu üstlenmektir.

Confluent’in beş dakikalık anlatımı: dual-write problemi, outbox’ın teslim garantisi ve desenin getirdiği yeni sorunlar (İngilizce).

• • •

Teslim garantileri: en çok bir kez, en az bir kez, fiilen bir kez

Confluent dokümantasyonu üç semantiği şöyle tanımlar: en çok bir kez (at-most-once) teslimde hata olursa mesaj kaybolabilir ama tekrar gönderilmez; en az bir kez (at-least-once) teslimde mesaj kaybolmaz ama birden fazla kez gelebilir; tam bir kez (exactly-once) ise her mesajın yalnızca bir kez teslim edilmesidir. Kafka, 0.11.0.0 sürümünden itibaren idempotent producer ve transaction’larla Kafka içinde tam bir kez işlemeyi destekler; ancak aynı doküman, dış bir sisteme yazarken bu koordinasyonun zorlaştığını ve Kafka’nın varsayılan olarak en az bir kez garanti verdiğini vurgular.

Pratikte bir ERP’den özel entegratöre uzanan zincirde ulaşabileceğiniz hedef, fiilen bir kez (effectively-once) etkidir: mesaj birden fazla kez gelebilir, ama iş sonucu bir kez oluşur. Bunu sağlayan şey broker ayarı değil, tüketicinin idempotent tasarımıdır.

SemantikNe garanti eder?RiskMaliyet / karmaşıklıkUygun senaryo
En çok bir kezTekrar yokMesaj kaybıDüşükMetrik, log, kaybı tolere edilebilen bildirim
En az bir kezKayıp yokTekrar eden mesajOrta: onay, retry, kalıcı kuyrukSipariş, fatura, stok olayları (idempotent tüketiciyle)
Fiilen bir kezKayıp yok, iş etkisi tekTasarım hatası olursa sessiz tekrarYüksek: outbox, mesaj kimliği, tekrar tablosuÖdeme, e-Fatura, muhasebe kayıtları

• • •

Idempotency anahtarı ve tekrar tablosu nasıl tasarlanır?

microservices.io’daki Idempotent Consumer deseni, tüketicinin işlediği mesajların kimliklerini veritabanında saklamasını önerir. Mesaj işlenirken transaction açılır, mesaj kimliği (subscriberId, messageId) birincil anahtarlı bir tabloya eklenir; mesaj daha önce işlendiyse ekleme başarısız olur ve mesaj yok sayılır. RabbitMQ’nun güvenilirlik rehberi de ağ veya düğüm hatasında mesajların yeniden teslim edilebileceğini ve tüketicilerin bunu idempotent tasarımla karşılamasının önerildiğini belirtir.

CREATE TABLE processed_messages (
  consumer_id  text        NOT NULL,   -- ör. 'efatura-adapter'
  message_id   uuid        NOT NULL,   -- outbox.id (mesaj başlığından)
  processed_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (consumer_id, message_id)
);

Dışarıya giden çağrılarda idempotency

Stripe’ın API dokümantasyonu iyi bir referanstır: istemci her yazma isteğine bir Idempotency-Key başlığı ekler; sunucu ilk isteğin durum kodunu ve gövdesini, başarılı olsun olmasın (500 hataları dahil) saklar ve aynı anahtarla gelen sonraki istekler aynı sonucu alır. Stripe anahtar olarak V4 UUID gibi yeterli rastgelelikte değerler önerir, anahtarı en fazla 255 karakterle sınırlar, en az 24 saat geçmiş anahtarların temizlenebileceğini belirtir ve aynı anahtarla farklı parametre gönderilirse hata döner. Anahtara e-posta gibi kişisel veri koymamak da aynı dokümanın uyarısıdır.

Her özel entegratör veya banka API’si böyle bir başlık sunmaz. Sunmuyorsa iki yol vardır: faturaya sizin tarafınızda atanan benzersiz kimlikle “önce sorgula, yoksa gönder” akışı kurmak ya da gönderilen isteği ve sonucu yerel bir tabloda kaydedip tekrar göndermeden önce kontrol etmek. Hangi yolun mümkün olduğu, sağlayıcının dokümantasyonunda keşif aşamasında doğrulanmalıdır. Güncel e-Fatura format değişikliklerinin entegrasyona etkisi için GİB UBL-TR güncellemesi yazımıza bakabilirsiniz.

Grafik: üstel geri çekilme ve jitter ile yeniden deneme bekleme süreleri, hangi hatalarda yeniden denenir
Her denemede bekleme üst sınırı ikiye katlanır ama bir tavanı vardır; jitter beklemeyi rastgele dağıtır. Doğrulama ve yetki hataları yeniden denenmez, doğrudan DLQ’ya gider.

• • •

Retry, timeout ve jitter nasıl ayarlanır?

AWS Builders’ Library’deki “Timeouts, retries, and backoff with jitter” makalesi bu konunun en sağlam kaynaklarından biridir. Temel tavsiyeler şunlardır: her uzak çağrıya hem bağlantı hem istek zaman aşımı koyun; yeniden denemeleri “bencil” kabul edin, çünkü aşırı yüklenmiş bir sistemi daha da yükleyebilir; üstel geri çekilmeyi bir üst sınırla kullanın ve deneme sayısını sınırlayın.

Katmanlı retry tuzağı

Aynı makale çarpıcı bir örnek verir: beş katmanlı bir çağrı zincirinde her katman üç kez yeniden denerse, veritabanı üzerindeki yük 243 katına çıkabilir. Bu yüzden yeniden denemeyi zincirin tek bir noktasında yapın. Outbox mimarisinde bu nokta genellikle relay ve tüketicidir; HTTP istemci kütüphanesinin kendi gizli retry’ını da ayrıca açık bırakmayın.

Jitter: aynı anda geri dönmeyin

Marc Brooker’ın AWS Architecture Blog’daki 2015 tarihli yazısı “Full Jitter”, “Equal Jitter” ve “Decorrelated Jitter” yaklaşımlarını simülasyonla karşılaştırır: jitter’sız üstel geri çekilme hem daha fazla iş hem daha uzun süre gerektirerek açık ara en kötü sonucu verir; Full Jitter daha az çağrı yapar. Kural basittir: bekleme süresini sıfır ile o denemenin üst sınırı arasında rastgele seçin.

Hangi hatalar yeniden denenir?

AWS makalesi, yan etkisi olan API’lerin idempotent değilse yeniden denenmesinin güvenli olmadığını ve istemci hatalarının (4xx) aynı istekle tekrarlanmaması gerektiğini, sunucu hatalarının ise sonraki denemede başarılı olabileceğini belirtir. Zaman aşımı, bağlantı hatası ve geçici sunucu hataları yeniden denenir; doğrulama ve yetki hataları denenmez, doğrudan DLQ’ya ve insan incelemesine gider.

// Relay işçisi (sözde kod): yayınla, işaretle; hata olursa jitter ile ertele
const BASE_MS = 1_000
const CAP_MS = 30_000
const MAX_ATTEMPTS = 8

function backoffWithJitter(attempt: number): number {
  const ceiling = Math.min(CAP_MS, BASE_MS * 2 ** attempt)
  return Math.floor(Math.random() * ceiling) // Full Jitter
}

async function relayBatch(db: Db, broker: Broker): Promise<void> {
  await db.transaction(async (tx) => {
    const rows = await tx.query(SELECT_PENDING_SQL) // FOR UPDATE SKIP LOCKED
    for (const row of rows) {
      try {
        await broker.publish(row.aggregatetype, {
          key: row.aggregateid,               // aynı sipariş → aynı partition
          headers: { 'message-id': row.id },  // tüketici tekrarları bununla ayıklar
          value: row.payload,
        }, { timeoutMs: 5_000 })
        await tx.query('UPDATE outbox SET published_at = now() WHERE id = $1', [row.id])
      } catch (err) {
        const attempts = row.attempts + 1
        if (attempts >= MAX_ATTEMPTS) {
          await moveToDeadLetter(tx, row, err)  // alarm + insan incelemesi
          continue
        }
        await tx.query(
          `UPDATE outbox
              SET attempts = $2,
                  next_attempt_at = now() + ($3 || ' milliseconds')::interval,
                  last_error = $4
            WHERE id = $1`,
          [row.id, attempts, backoffWithJitter(attempts), String(err)],
        )
      }
    }
  })
}
Mavi kumlu kum saati — entegrasyon çağrılarında zaman aşımı ve bekleme süresi
Zaman aşımı olmayan bir çağrı, kaynakları süresiz tutar. Bağlantı ve istek zaman aşımı her uzak çağrıda ayrı ayrı tanımlanmalıdır.

• • •

Dead-letter kuyruğu, zehirli mesajlar ve circuit breaker

Zehirli mesaj (poison message), kaç kez denenirse denensin işlenemeyecek mesajdır: bozuk JSON, eşlemesi olmayan bir ürün kodu, vergi numarası hatalı bir cari. Böyle bir mesajı sonsuza kadar yeniden denemek kuyruğu tıkar ve arkasındaki sağlıklı mesajları bekletir. RabbitMQ, bir mesajın yeniden kuyruğa alınmadan reddedilmesi, TTL süresinin dolması, kuyruk uzunluk sınırının aşılması veya quorum kuyruklarda teslim sınırının geçilmesi durumlarında dead-letter exchange’e yönlendirilmesini destekler.

DLQ işletme kuralları

  • DLQ’ya düşen her mesaj; orijinal mesaj kimliği, hata nedeni, deneme sayısı ve correlation id ile saklanır.
  • DLQ derinliği için alarm vardır ve alarmın bir sahibi (kişi veya nöbet rotası) bulunur.
  • Hata giderildikten sonra mesajın yeniden işlenmesi idempotent olduğu için güvenlidir; prosedür runbook’ta yazılıdır.
  • DLQ’daki mesajlar süresiz bekletilmez; saklama süresi ve kişisel veri içeriği KVKK açısından değerlendirilir.

Circuit breaker

Martin Fowler’ın 2014 tarihli yazısında anlattığı circuit breaker, korunan uzak çağrıyı bir nesneyle sarar; hata sayısı eşiği geçtiğinde devre “açılır” ve sonraki çağrılar uzak sisteme hiç gitmeden hata döner. Belirli bir süre sonra “yarı açık” duruma geçilir ve deneme çağrısı başarılı olursa devre kapanır. Fowler, durum değişikliklerinin loglanmasını ve izlenmesini de önerir. AWS makalesi ise circuit breaker’ların test edilmesi zor modal davranış getirebildiğini, yerel bir token bucket ile retry’ları sınırlamanın bir alternatif olduğunu not eder. Özel entegratör bakımdayken relay’in faturaları DLQ’ya boca etmek yerine beklemesi, bu desenin en somut faydasıdır.

Elektrik panosunda devre kesiciler — yazılımda circuit breaker deseninin benzetmesi
Elektrik panosundaki sigorta gibi: hata eşiği aşılınca devre açılır, karşı sistem toparlanana kadar yük gönderilmez, sonra kontrollü bir deneme yapılır.

• • •

Sıralama, saga ve tüketici tarafı

Sıralama ne zaman önemlidir?

“Sipariş oluşturuldu”dan önce “sipariş iptal edildi” işlenirse sorun çıkar. Debezium, outbox kaydındaki aggregateid değerini mesaj anahtarı yapar; Kafka da aynı anahtarlı olayları aynı partition’a yazıp o partition’da yazılış sırasını korur. Polling relay’i birden fazla örnekle ve SKIP LOCKED ile çalıştırırsanız, aynı siparişe ait iki olay farklı örneklerce farklı sırada yayınlanabilir. Kesin sıra gerekiyorsa ya tek relay örneği, ya anahtar bazında bölümlenmiş relay’ler ya da CDC tercih edilmeli; tüketici de olaydaki sürüm numarasına bakarak eski olayı yok sayabilmelidir.

Saga ile birlikte outbox

Birden fazla servise yayılan iş süreçlerinde microservices.io, her biri kendi veritabanını güncelleyip bir sonraki adımı tetikleyen yerel transaction’lardan oluşan saga desenini önerir; bir adım iş kuralı nedeniyle başarısız olursa önceki adımlar telafi (compensating) transaction’larıyla geri alınır. Saga’nın güvenilir çalışması için her adımın veritabanını güncelleyip mesajı atomik olarak yayınlaması gerekir; yani saga’nın altında genellikle outbox vardır.

Tüketici sözde kodu

// e-Fatura adaptörü: aynı mesaj ikinci kez gelirse hiçbir şey yapmaz
async function handleOrderConfirmed(msg: Message): Promise<void> {
  await db.transaction(async (tx) => {
    const res = await tx.query(
      `INSERT INTO processed_messages (consumer_id, message_id)
       VALUES ('efatura-adapter', $1)
       ON CONFLICT DO NOTHING`,
      [msg.headers['message-id']],
    )
    if (res.rowCount === 0) return // tekrar: zaten işlendi

    const invoice = mapOrderToInvoice(msg.value) // kanonik model → fatura
    await tx.query(
      'INSERT INTO invoice_requests (order_id, payload, status) VALUES ($1, $2, $3)',
      [invoice.orderId, invoice, 'PENDING'],
    )
  })
  // Sağlayıcıya gönderim ayrı bir işçide, invoice_requests üzerinden ve
  // sağlayıcının desteklediği idempotency yöntemiyle yapılır.
}

• • •

Kontrol listesi: outbox ve retry tasarımı

  • İş verisi ve outbox kaydı aynı veritabanında, aynı transaction’da mı yazılıyor?
  • Her mesajın benzersiz bir kimliği var ve broker başlığında taşınıyor mu?
  • Her tüketici bu kimlikle tekrarları ayıklıyor mu (tekrar tablosu veya doğal anahtar)?
  • Her uzak çağrıda bağlantı ve istek zaman aşımı tanımlı mı?
  • Retry yalnızca tek bir katmanda, üst sınırlı üstel geri çekilme ve jitter ile mi yapılıyor?
  • Doğrulama ve yetki hataları retry’a girmeden DLQ’ya mı gidiyor?
  • DLQ için alarm, sahip ve yeniden işleme prosedürü runbook’ta yazılı mı?
  • Sıralama gereken olaylar için anahtar ve relay stratejisi belirlendi mi?
  • Bekleyen outbox kaydı sayısı ve en eski kaydın yaşı izleniyor mu?

Bu metriklerin nasıl toplanacağını kurumsal uygulamalarda gözlemlenebilirlik yazımızda, outbox tablosu büyüdüğünde PostgreSQL tarafında dikkat edilecekleri ise PostgreSQL okuma ağırlıklı iş yükleri rehberimizde bulabilirsiniz.

Aksiyon Soft nasıl yardımcı olur?

API ve entegrasyon projelerimizde outbox, idempotent tüketici, retry politikası ve DLQ süreçlerini ilk sprintten itibaren tasarımın parçası yapıyoruz; mevcut entegrasyonlarınızda tekrar eden fatura veya kaybolan sipariş sorunları varsa, önce akışı ölçüp en riskli noktadan başlıyoruz. Canlı sistemlerin izlenmesi, DLQ takibi ve olay müdahalesi için bakım ve destek hizmetimiz devreye girer. Mimari bütünü API ve veri entegrasyon platformu çözüm sayfamızda inceleyebilirsiniz.

Çalışma modelimiz: keşifte akış ve hata senaryoları çıkarılır, ilk kritik akışla MVP kurulur, her sprintte çalışan sürüm demo edilir, canlıya geçişten sonra hypercare ve SLA’lı bakım gelir. Merkezimiz Samsun’dadır; Türkiye genelindeki ekiplerle uzaktan çalışır, gerektiğinde planlı saha ziyareti yaparız.

Sık sorulan sorular

Outbox pattern için Kafka şart mı?

Hayır. Outbox, broker’dan bağımsız bir desendir; relay mesajları RabbitMQ’ya, Kafka’ya veya bir HTTP uç noktasına iletebilir. Debezium ile CDC kullanmak istiyorsanız Kafka Connect ekosistemi doğal bir seçimdir, ama polling relay her broker ile çalışır.

Outbox tablosu büyüyüp performansı düşürmez mi?

Polling yaklaşımında yayınlanmış kayıtlar düzenli olarak silinmeli veya arşivlenmelidir; bekleyen kayıtlara kısmi indeks eklemek sorguyu hızlı tutar. CDC yaklaşımında kayıt eklenip hemen silinebilir; Debezium’un outbox yazısında anlatıldığı gibi tablo pratikte boş kalır.

Exactly-once mümkün değil mi?

Kafka, kendi topic’leri arasında transaction’larla tam bir kez işlemeyi destekler. Ancak zincirde özel entegratör, banka veya e-posta servisi gibi dış sistemler olduğunda hedeflenen şey fiilen bir kez etkidir: mesaj birden fazla gelebilir, idempotent tüketici sayesinde iş sonucu bir kez oluşur.

Kaç kez yeniden denemeliyiz?

Evrensel bir sayı yoktur; karşı sistemin toparlanma süresine ve işin aciliyetine göre belirlenir. Önemli olan sınırlı deneme, üst sınırlı üstel geri çekilme, jitter ve sınıra ulaşınca DLQ ile alarmdır. Değerler keşifte yazılır, canlıda metriklere bakılarak ayarlanır.

Webhook ile gelen ödeme bildirimleri nasıl ele alınmalı?

Bildirimi hemen ve hafifçe kabul edip kalıcı olarak kaydedin, asıl işi asenkron yapın. Sağlayıcının bildirim kimliğini tekrar tablosunda kullanarak aynı bildirimin ikinci kez işlenmesini engelleyin; siparişi “ödendi” yapan güncelleme ile kargo/fatura olayını aynı transaction’da outbox’a yazın.

Mevcut sisteme outbox eklemek büyük bir yeniden yazım gerektirir mi?

Genellikle hayır. En riskli akıştan başlayıp mesaj gönderen kod noktasını outbox’a yazacak şekilde değiştirmek ve bir relay eklemek yeterlidir. Diğer akışlar kademeli olarak taşınır; tüketicilere tekrar tablosu eklemek de çoğu zaman küçük bir değişikliktir.

Projenizi konuşalım

Tekrar kesilen faturalar, ERP’ye düşmeyen siparişler veya kimsenin bakmadığı hata kuyrukları yaşıyorsanız, kısa bir keşif görüşmesiyle akışınızı birlikte inceleyelim. İletişim formundan bağlı sistemleri ve sorunun nerede görüldüğünü yazmanız yeterli.

Kaynaklar

Blog ve haberlere abone olun

Yeni yazılar yayınlandığında e-posta alın. İstediğiniz zaman abonelikten çıkabilirsiniz.

İlgili içerikler