Outbox patterni
Outbox - bu me’moriy pattern bo’lib, unda domen xizmati biznes o’zgarishini va tegishli voqeani o’z omboridagi bitta lokal tranzaksiyada yozib oladi. Hodisani tashqi shina/navbatga joylashtirish «outbox» jadvalini o’qiyotgan va yozuvlarni qayta uzatuvchi alohida xavfsiz jarayon (publisher) orqali amalga oshiriladi. Bunday yondashuv poygani «avval DBda, keyin shinada» bartaraf etadi va muvaffaqiyatsizlikka uchragan taqdirda ham ishonchli yetkazib berishni taʼminlaydi.
1) Ushbu Nizomning
Mos keladi:- Mikroservislar va kontekstlar orasidagi voqealar bilan modulli monolitlar.
- «Holati qayd etilgan voqea yoʻqolishi mumkin emas».
- Dempotentlik va nazoratli qayta yetkazib berish kerak.
- Bir nechta resurslarda qattiq global tranzaksiyalar juda muhim (aniq shartnomalar bilan TSS/saga yaxshiroq).
- Tanlangan haqiqat manbai yoʻq (state hodisa yaratiladigan joyda saqlanmagan).
2) Maqsad va xossalar
Atomic write: domen yozuvi + outbox - bitta tranzaksiyada.
At-least-once nashri: takrorlashga ruxsat beramiz, yo’qotishni istisno qilamiz.
Iste’molchilarning idempotentligi: obunachilar tomonidan dubllardan himoya qilish.
Samarali exactly-once: outbox + idempotent consumer + dedup kombinatsiyasi yordamida erishiladi.
Aniq telemetriya: biznes operatsiyalar va voqealarning korrelyatsiyasi.
3) Ma’lumotlar sxemasi (misol)
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) Tranzaksion shablon (application layer)
pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys
Agar kommit muvaffaqiyatli boʻlsa, outbox hodisasi mavjud boʻlishi kafolatlangan. Agar dastur kommitdan keyin yiqilsa, ommaga yetib boradi.
5) Publisher (reader → publisher)
Vazifalar:- Nashr etilmagan voqealarni vaqti-vaqti bilan oʻqish (’published _ at IS NULL’va’available _ at <= now ()’), batch.
- Shina/navbatga joylashtirishga harakat qilish; muvaffaqiyatli bo’lganda’published _ at’deb belgilash.
- Xato boʻlsa,’attempts’ni kattalashtiring,’available _ at’ni kelajakka qoʻying (exponential backoff),’error’deb yozing.
- Tenant/kalit limitlarini hurmat qilish, mahsulotni blokirovka qilmaslik.
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) Idempotentlik va deduplikatsiya
Iste’molchi tomonida (Inbox/Idempotency store):sql
CREATE TABLE inbox (
consumer_name TEXT,
event_id UUID,
processed_at TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);
Algoritm: hodisani olishda - avval’INSERT’ning’inbox’ga urinishi; agar kalit kelishmovchiligi boʻlsa - hodisa allaqachon qayta ishlangan → «no-op». Keyingi o’rinlarda biznes-mantiq.
Reklama tomonida:’Idempotency-Key’headers (masalan,’event _ id’) shina/broker/proksilar dublikatlarni filtrlay olishlari uchun.
7) Tartib va sabablar
’aggregate _ id’ boʻyicha lokal tartib’occurred _ at’ni saralash va’kalit boʻyicha’nashr etish orqali taʼminlanadi.
Partiyalashtirilgan log-shinalar uchun bitta agregat voqealari bitta partishenda bo’lishi uchun’aggregate _ id ’/’ tenant _ id’kaliti bilan partiyalashtiring.
Agar tartib o’ta og’ir bo’lsa, birma-bir ommaviy poygalardan qoching.
8) CDC (Change Data Capture)
Aktiv reklama oʻrniga CDC ishlatish mumkin: dvigatel DD tranzaksiya jurnalini oʻqib, «outbox» satrlarini shinaga uzatadi. Ijobiy tomonlari - MBga minimal yuk, aniq ketma-ketlik, polling yo’qligi. Kamchiliklar - operatsiyani murakkablashtirish va DBMS xususiyatlariga bog’lash. Ikkala yondashuv ham validdir; vakolatlari va SLO bo’yicha tanlang.
9) Xatolar, DLQ va redrayv
Retryable (tarmoq, limitlar) -’attempts’ni ko’paytiramiz,’available _ at’ni (exponential backoff + jitter) qoldiramiz.
Non-retryable (nolid sxema/kontrakt) - DLQ/Dead-Letter Topic ga boy meta-ma’lumotlar bilan ko’chiriladi.
Xavfsiz redrayv: batchi, rate-limit, sxema validatsiyasi, prod-trafikdan past ustunlik.
10) Multi-tenantlik va limitlar
Majburiy teglar:’tenant _ id’,’plan’,’region’-’outbox’. headers`.
Per-tenant fairness: publicher nashrlar «oynalari» va ijarachilar bo’yicha urinishlar limitlarini taqsimlaydi.
Residency: outboxni domen maʼlumotlari joylashgan hududda saqlang; mintaqalararo e’lon qilish - faqat agregatlar/ma’lumotlar.
11) Xavfsizlik va muvofiqlik
payload/headers’da tenant/mintaqa siyosati bo’yicha PII tahriri.
Agar shina «begona» bo’lsa, foydali yuklamaning imzosi/shifrlanishi.
Barcha holat oʻtishlari auditi: yaratilgan, nashr etilgan, xato, tahrir qilingan.
12) Kuzatish
Metriklar:- Nashr davri (’now - occurred_at' p50/p95/p99).
- Muvaffaqiyatlar ulushi, xatolar ulushi, sabablar taqsimoti.
- Outbox hajmi (nashr etilmaganlar soni), urinishlar/sek.
- throughput va lag.
- Korrelyatsiya’event _ id ’/’ aggregate _ id ’/’ saga _ id’; «db-tx», «publish», «retry» spanlari.
- Izohlar:’attempt’,’backoff _ ms’,’dlq = true’.
- Muvaffaqiyat uchun qisqacha yozuvlar; xato/tahrir uchun toʻliq tafsilotlar.
13) Test va tartibsizlik
Atomicity test: domen tranzaksiyasi kommitidan keyin sun’iy ravishda «tushamiz» - voqea e’lon qilinishidan oldin keyinroq chiqishi shart.
Duplicate test: biz bir xil tadbirni bir necha marta e’lon qilamiz - konsumer aynan bitta effektni (inbox) bajaradi.
Order test: bitta agregat bo’yicha voqealar to’plami - ketma-ketlik/o’zgaruvchanlikni tekshirish.
Chaos: brokerning rad etishi, DB latentligining oshishi, split-brain publichers, clock-skew.
14) Konfiguratsiya shablonlari (misol)
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) Dostonlar va retrajlar bilan integratsiya
Outbox - saga qadamlari uchun «xavfsizlik transporti»: lokal tranzaksiya effekt va buyruq/hodisani yozadi; nashr - ishonchli va dozalanadigan.
Takrorlash va backoff siyosati’Retry-After’va Circuit Breaker bilan kelishilgan bo’lishi kerak; «retray boʻronidan» qoching.
16) Tipik xatolar
Hodisa domen holati kommitidan keyin yoziladi - yiqilganda yoʻqotish mumkin.
’outbox’ → da hech qanday indeks/arxiv mavjud emas.
Jamoatchilik’SKIP LOCKED’yoki shardatsiyasiz - raqobat va blokirovka.
Iste’molchilarda idempotentlik yo’qligi - dubli va nojo’ya ta’sirlar.
DLQ/loglarda yashirmasdan PII aralashtirish.
Fairness’siz nashr etishning yagona global navbati - «shovqinli» tenant barchani sekinlashtiradi.
Lag monitoringi yo’qligi → yashirin degradatsiyalar.
17) Strategiyani tez tanlash
Boshlang’ich darajasi: DBdan polling, 100-500 batchi, full-jitter backoff, inbox.
Yuqori yuk: tranzaksiyalar jurnalidan CDC,’tenant _ id/aggregate _ id’bo’yicha shardlash, ijarachilar bo’yicha WFQ.
Agregat bo’yicha qat’iy tartib: per key (mutex) ni seriyali e’lon qilish, topikni kalit bilan partiyalashtirish.
Komplayens/PII: payload shifrlash, DLQ tahriri, mintaqaviy outbox.
18) Sotishdan oldingi chek-varaq
- Domen oʻzgarishlari va «outbox» dagi yozuv bitta operatsiyada amalga oshiriladi.
- Publisher batchlarni qayta ishlaydi,’SKIP LOCKED’, jitter bilan backoff va limitlardan foydalanadi.
- Konsumerlar idempotentdir («inbox» jadvali/dedup jurnali).
- DLQ va xavfsiz tahrirlash moslamalari.
- Lag/xato metrikalari va p95/p99 chegaralari bo’yicha alertlar.
- Kalit tartibi kafolatlangan (partiyaviy/seriyali).
- Arxiv/retenshn’outbox’va nashr etilgan yozuvlarni tozalash.
- PII-siyosat va holatlar o’tishining auditi.
- Kommit va nashr o’rtasidagi yiqilish testlari, dublikatlar va tartib.
- Hodisa shartnomalari hujjatlari (sxemalar/versiyalar/muvofiqlik).
Xulosa
Outbox-pattern «moʻrt» DB «shina» ni ishonchli konveyerga aylantiradi: atomik holatni mustahkamlash, kafolatlangan (kamida bir marta) nashr etish, idempotent obunachilar va nazorat qilinadigan redrayv. Sxemalarning to’g "ri telemetriyasi, limitlari va intizomida u taqsimlangan tranzaksiyalarning murakkabligini kamaytirib, tizimning nosozliklar va eng yuqori yuklarga chidamliligini oshirib, amaliy exactly-once xulq-atvorini beradi.