Giden kutusu deseni
Outbox, bir etki alanı hizmetinin bir iş değişikliği ve ilgili olayı bir yerel işlemde deposuna yazdığı mimari bir kalıptır. Olayın harici veri yoluna/kuyruğa yayınlanması, 'dış kutu' tablosunu okuyan ve kayıtları aktaran ayrı bir güvenli işlem (yayıncı) tarafından eşzamansız olarak gerçekleştirilir. Bu yaklaşım,'önce veritabanına, sonra otobüse "yarışını ortadan kaldırır ve arıza durumunda bile güvenilir teslimat sağlar.
1) Ne zaman başvurulur
Sığdır:- Bağlamlar arası olaylara sahip mikro hizmetler ve modüler monolitler.
- "Devletin sabit olması ↔ olayın kaybedilememesi" sağlanmalıdır.
- Bize idempotans ve kontrollü yeniden teslimat lazım.
- Çeşitli kaynaklar üzerindeki zorlu küresel işlemler kritiktir (açık sözleşmelerle TTK/destanlardan daha iyidir).
- Gerçeğin adanmış bir kaynağı yoktur (olayın üretildiği yerde durum depolanmaz).
2) Hedefler ve özellikler
Atomik yazma: etki alanı kaydı + giden kutusu - bir işlemde.
En az bir kez yayın: tekrara izin veriyoruz, kaybı hariç tutuyoruz.
Tüketici idempotence: abone tarafında alır karşı koruma.
Tam olarak bir kez etkili: outbox + idempotent consumer + dedup kombinasyonu ile elde edilir.
Net telemetri - Ticari işlemleri ve olayları ilişkilendirin.
3) Veri şeması (örnek)
sql
-- Domain table (example: orders)
CREATE TABLE orders (
id UUID PRIMARY KEY,
tenant_id TEXT NOT NULL,
status TEXT NOT NULL,
total_amount NUMERIC(12,2) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT now()
);
-- Outbox
CREATE TABLE outbox (
id UUID PRIMARY KEY, -- event_id aggregate_type TEXT NOT NULL, -- 'order'
aggregate_id UUID NOT NULL, -- order_id tenant_id TEXT NOT NULL,
type TEXT NOT NULL, -- 'OrderCreated'
payload JSONB NOT NULL, -- serialized headers event JSONB NOT NULL DEFAULT '{}':: jsonb,
occurred_at TIMESTAMP NOT NULL, -- time in domain transaction available_at TIMESTAMP NOT NULL, -- earliest publish time (backoff)
published_at TIMESTAMP, - is filled by the attempts INT NOT NULL DEFAULT 0,
error TEXT
);
CREATE INDEX ON outbox (available_at) WHERE published_at IS NULL;
CREATE INDEX ON outbox (tenant_id, available_at) WHERE published_at IS NULL;
4) Uygulama katmanı
pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys
İşlem başarılı olursa, giden kutusundaki olayın var olduğu garanti edilir. Uygulama bir taahhütten sonra düşerse, yayıncı yetişir.
5) Yayıncı (okuyucu - yayıncı)
Görevler:- Periyodik olarak yayınlanmamış olayları okuyun ('published _ at IS NULL've' available _ at <= now () '), gruplar.
- Otobüs/sıraya yayınlamaya çalışın; Başarılı olursa, 'published _ at' işaretini işaretleyin.
- Hata durumunda - 'girişimleri' artırın, gelecek için 'available _ at' koyun (üstel geri alma), 'hata' yazın.
- Kiracılar/anahtarlar üzerindeki sınırlara saygı gösterin (adalet), ürünü engellemeyin.
pseudo loop:
events = select from outbox where published_at is null and available_at <= now()
order by occurred_at limit BATCH_SIZE for update skip locked
for e in events:
try:
broker. publish(topicFor(e), serialize(e. payload), headers(e))
markPublished(e. id, now())
except Retryable:
backoff = computeBackoff(e. attempts)
reschedule(e. id, now()+backoff, attempts+1, last_error)
except NonRetryable:
moveToDLQ (e) or markError (e) # by sleep (POLL_INTERVAL) policy
6) Idempotency ve veri tekilleştirme
Tüketici tarafında (Inbox/Idempotency store):sql
CREATE TABLE inbox (
consumer_name TEXT,
event_id UUID,
processed_at TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);
Algoritma: Bir olayı alırken, önce 'gelen kutusuna' INSERT 'yazmayı deneyin; Önemli bir çatışma varsa, olay zaten ele alındı - no-op. Sırada iş mantığı var.
Yayıncı tarafında: Başlıklarda 'Idempotency-Key' (örneğin, 'event _ id'), böylece bus/broker/proxy kopyaları filtreleyebilir.
7) Düzen ve nedensellik
'Agregate _ id'ile yerel düzen,' ocured _ at 'sıralanarak ve "anahtarla" yayınlanarak sağlanır.
Bölümlemeli log-veriyolları için - 'agregate _ id'/' tenant _ id' anahtarıyla bölümleme, böylece bir toplamın olayları aynı bölümlemede olur.
Sipariş kritikse, çapraz akışlı tek tuşlu yayıncı yarışlarından kaçının.
8) CDC (Veri Yakalama Değişikliği)
Etkin bir yayıncı yerine CDC'yi kullanabilirsiniz: Motor, veritabanı işlem günlüğünü okur ve 'giden kutusu' satırlarını otobüse çevirir. artıları - veritabanı üzerinde minimum yük, tam sıra, yoklama yok. Dezavantajları - operasyonun komplikasyonu ve DBMS'nin özelliklerine bir bağ. Her iki yaklaşım da geçerlidir; Yetkinliklere ve SLO'ya göre seçim yapın.
9) Hatalar, DLQ ve Redrive
Retryable (ağ, limitler) - 'girişimleri' artırın, 'available _ at'i erteleyin (üstel geri dönüş + jitter).
Geri alınamaz (geçersiz şema/sözleşme) - zengin meta verilerle DLQ/Dead-Letter Topic'e aktarılır.
Güvenli Redrive: Gruplar, Oran Sınırı, Planın Doğrulanması, Üretim Trafiğinin Altındaki Öncelik.
10) Çok kiracılık ve limitler
Gerekli etiketler: 'tenant _ id', 'plan', 'bölge' - 'giden kutusunda. Başlıklar.
Kiracı başına adalet: Yayıncı, yayınların "pencerelerini've girişimlerin sınırlarını kiracılara dağıtır.
Yerleşim: çıkış kutusunu etki alanı verileriyle aynı bölgede saklamak; Bölgeler arası yayın - yalnızca toplamlar/özetler.
11) Güvenlik ve uyumluluk
Kiracı/bölge politikasında yük/başlıklarda PII baskısı.
Veri yolu yabancı ise yükün imzası/şifrelenmesi.
Tüm durum geçişlerini denetleyin: oluşturulan, yayınlanan, hata, yeniden yazma.
12) Gözlemlenebilirlik
Metrikler:- Yayın gecikmesi ('şimdi - occurred_at' p50/p95/p99).
- Başarı oranı, hata oranı, neden dağılımı.
- Giden kutusu boyutu (yayınlanmamış sayısı), yeniden dener/sn
- Kiracı başına grafikler verim ve gecikme.
- Korelasyon 'event _ id'/' aggregate _ id'/' saga _ id'; "db-tx", "yayınla", "yeniden dene".
- Ek açıklamalar: 'girişim', 'backoff _ ms', 'dlq = true'.
- Başarı için kısa kayıtlar; Hata/redrave başına tüm ayrıntılar.
13) Test ve kaos
Atomisite testi: Yayınlanmadan önce bir etki alanı işlemi yaptıktan sonra yapay olarak "düşüş" - olay daha sonra serbest bırakılmalıdır.
Yinelenen test: Aynı olayı birkaç kez yayınlıyoruz - tüketici tam olarak bir efekt (gelen kutusu) gerçekleştiriyor.
Sipariş testi: bir agrega ile olayların toplu - dizi/idempotans kontrolü.
Kaos: komisyoncu hatası, veritabanı gecikmesinde artış, bölünmüş beyin yayıncıları, saat eğriliği.
14) Yapılandırma şablonları (örnek)
yaml outbox:
poll_interval_ms: 200 batch_size: 200 order_by: occurred_at backoff:
strategy: exponential_full_jitter initial_ms: 250 max_ms: 10_000 max_attempts: 20 fairness:
per_tenant_parallelism: 4 per_key_serial: true
publisher:
rate_limit_per_sec: 500 headers:
idempotency_key: event_id schema_version: v3 dlq:
enabled: true topic: myapp. events. dlq include_metadata:
- error
- attempts
- source_table
- tenant_id
- aggregate_id
15) Sagalar ve geri çekilmelerle entegrasyon
Giden kutusu - saga adımları için "güvenlik taşıma": yerel işlem efekt ve komut/olay yazar; yayın - güvenilir ve dozlu.
Tekrarlama ve geri alma politikaları 'Retry-After've Devre Kesici ile tutarlı olmalıdır; "Retray fırtınası'ndan kaçının.
16) Tipik hatalar
Etki alanı durumu işledikten sonra bir olay yazarlar - düşme sırasında kayıp mümkündür.
'Outbox'ta dizin/arşiv yok - yayınlama gecikmesi artar.
'SKIP LOCKED' olmadan veya sharding olmadan yayıncı - rekabet ve engelleme.
Tüketiciler arasında idempotency eksikliği - kopyalar ve yan etkiler.
DLQ/günlüklerde maskelemeden PII karıştırma.
Adil olmayan tek bir küresel yayıncılık kuyruğu - "gürültülü'bir kiracı herkesi yavaşlatır.
Gecikme izleme eksikliği - gizli bozulma.
17) Hızlı strateji seçimi
Başlangıç seviyesi: Veritabanından yoklama, 100-500 parti, tam jitter backoff, tüketiciler için gelen kutusu.
Yüksek yük: İşlem günlüğünden CDC, 'tenant _ id/aggregate _ id'ile sharding, kiracı tarafından WFQ.
Toplama göre katı düzen: anahtar başına seri yayın (mutex), konunun bir anahtarla bölünmesi.
Uyumluluk/PII: yük şifreleme, DLQ sürümü, bölgesel giden kutusu.
18) Satış öncesi kontrol listesi
- Etki alanı değişiklikleri ve 'outbox'olarak yazılması aynı işlemde gerçekleşir.
- Yayıncı toplu işler, 'SKIP LOCKED' kullanır, jitter ve limitlerle geri çekilir.
- Tüketiciler idempotent (tablo 'gelen kutusu'/deadup log).
- DLQ ve Secure Release yapılandırılmıştır.
- p95/p99 eşiklerinde gecikme/hata ve uyarı metrikleri.
- Anahtar sırası garantilidir (partiler/diziler).
- Arşiv/saklama 'dış kutu've yayınlanan kayıtları temizleyin.
- PII politikaları ve devlet geçiş denetimi.
- Commit ve yayınlama, kopyalar ve sipariş arasındaki testleri bırakın.
- Olay sözleşmesi belgeleri (şemalar/sürümler/uyumluluk).
Sonuç
Giden kutusu deseni, "DB ↔ veri yolu'nun" kırılgan "demetini güvenilir bir boru hattına dönüştürür: atomik durum fiksasyonu, garantili ('en az bir kez" olsa da) yayın, idempotent aboneler ve kontrollü redrave. Uygun telemetri, limitler ve şema disiplini ile, dağıtılmış işlemlerin karmaşıklığını azaltarak ve sistemin çökmelere ve pik yüklere karşı direncini artırarak pratik tam olarak bir kez davranış sağlar.