Outbox patterni
Outbox, domen hyzmatynyň öz ammaryndaky bir lokal amalda işewürlik üýtgemelerini we degişli wakany ýazýan binagärlik nusgasydyr. Wakany daşarky tekerde/nobata çap etmek "outbox" tablisasyny okaýan we ýazgylary yzyna iberýän aýratyn howpsuz proses (publisher) arkaly asinxron ýerine ýetirilýär. Bu çemeleşme "ilki DB-de, soň tekerde" ýaryşy ýok edýär we şowsuzlyklara garamazdan ygtybarly eltmegi üpjün edýär.
1) Haçan ulanmaly
Laýyk:- Kontekstleriň arasyndaky wakalar bilen mikroservisler we modully monolitler.
- "Ýagdaýyň hasaba alnandygyny we wakanyň ýitip bilmejekdigini" kepillendirmek talap edilýär.
- Idempotentlik we gözegçilik edilýän gaýtadan eltip bermek zerurdyr.
- Birnäçe çeşmelerde gaty global amallar möhümdir (aç-açan şertnamalar bilen TSS/saga has gowudyr).
- Saýlanan hakykat çeşmesi ýok (state wakanyň döreýän ýerinde saklanmaýar).
2) Maksatlary we häsiýetleri
Atomic write: domen ýazgysy + outbox - bir amalda.
At-least-once neşir: gaýtalamaga ýol berýäris, ýitgini aradan aýyrýarys.
Sarp edijileriň idempotentligi: abonentleriň tarapynda dubllardan goramak.
Netijeli exactly-once: outbox + idempotent consumer + dedup kombinasiýasy arkaly gazanylýar.
Takyk telemetriýa: işewürlik amallarynyň we wakalaryň baglanyşygy.
3) Maglumatlaryň shemasy (mysal)
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) Amal şablony (application layer)
pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys
Eger kommit üstünlikli bolsa, outbox-daky wakanyň bardygyna kepillik berilýär. Eger programma kommitden soň ýykylsa, mahabatlandyryjy ýeter.
5) Publisher (reader → publisher)
Maksatlar:- Çap edilmedik wakalary wagtal-wagtal okamak ('published _ at IS NULL' we 'available _ at <= now ()').
- Teker/nobata ýerleşdirmäge synanyşyň; Üstünlikli bolsa, 'published _ at' belläň.
- Hata edilse, 'attempts' -i köpeltmek, 'available _ at' -y geljege goýmak (exponential backoff), 'error' -y ýazmak.
- Tenantlar/açarlar boýunça çäklere hormat goýmak (fairness), öndürijiligi blokirlemezlik.
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 we duplikasiýa
Sarp edijiniň tarapynda (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: wakany alanyňyzda - ilki 'INSERT' -de 'inbox' synanyşygy; açar gapma-garşylygy ýüze çyksa - waka eýýäm işlenipdir → "no-op". Ondan soň - işewürlik logikasy.
Köpçüligiň gapdalynda: 'Idempotency-Key' -de (mysal üçin 'event _ id'), şina/broker/proksi dublikatlary süzüp biler ýaly.
7) Tertibi we sebäpliligi
'aggregate _ id' boýunça ýerli tertip 'occurred _ at' sortlamak we 'açar boýunça' çap etmek arkaly üpjün edilýär.
Partiýa ýerleşdirilen log-şinler üçin bir agregatyň wakalary bir partişende bolar ýaly 'aggregate _ id '/' tenant _ id' açary bilen partiýa goýuň.
Eger tertip möhüm bolsa, bir açardan köpçüligiň akymara ýaryşlaryndan gaça duruň.
8) CDC (Change Data Capture)
Işjeň köpçüligiň ýerine CDC ulanylyp bilner: hereketlendiriji DB amallar magazineurnalyny okaýar we 'outbox' setirlerini tekere ýaýradýar. Plýuslar - DB-e iň az ýük, takyk yzygiderlilik, pollingiň ýoklugy. Minuslar - operasiýany çylşyrymlaşdyrmak we DBB aýratynlyklaryna baglamak. Iki çemeleşme hem dogry; başarnyklar we SLO boýunça saýlaň.
9) Ýalňyşlyklar, DLQ we redrave
Retryable (tor, çäkler) - 'attempts' -i köpeldýäris, 'available _ at' -y yza süýşürýäris (exponential backoff + jitter).
Retryable däl (nolalid shema/kontrakt) - baý meta-maglumatlar bilen DLQ/Dead-Letter Topic-e geçirýäris.
Howpsuz redaktirleme: batçi, rate-limit, shema tassyklamasy, prod-traffigiň aşagyndaky ileri tutulýan ugur.
10) Köp tenantlyk we çäkler
Hökmany bellikler: 'tenant _ id', 'plan', 'region' - içinde 'outbox. headers`.
Per-tenant fairness: publişer neşirleriň "penjirelerini" we kärendeçiler boýunça synanyşyklaryň çäklerini paýlaýar.
Residency: outbox-y domen maglumatlary bolan sebitde saklaň; sebitara neşir etmek - diňe agregatlar/hasabatlar.
11) Howpsuzlyk we laýyklyk
PII-tenant/sebit syýasaty boýunça payload/headers-de redaksiýa.
Şina "keseki" bolsa, peýdaly ýüküň goly/şifrlemegi.
Ýagdaýyň ähli geçişleriniň barlagy: döredildi, çap edildi, ýalňyşlyk, redaktirlendi.
12) Gözegçilik etmek
Metrikler:- Çap möhleti ('now - occurred_at' p50/p95/p99).
- Üstünlikleriň paýy, ýalňyşlyklaryň paýy, sebäpleriň paýlanyşy.
- Outbox ululygy (çap edilmedik sany), synanyşyklar/sek.
- Throughput we lag.
- 'event _ id '/' aggregate _ id '/' saga _ id' baglanyşygy; "db-tx", "publish", "retry" ýataklary.
- Düşündirişler: 'attempt', 'backoff _ ms', 'dlq = true'.
- Üstünlik üçin gysgaça ýazgylar; doly jikme-jiklikler/redrave.
13) Synag we bulam-bujarlyk
Atomicity synagy: çap edilmezden ozal domen amalynyň jemlenişinden soň emeli usulda "ýykylýar" - waka soň çykmaga borçludyr.
Duplicate test: Şol bir çäräni birnäçe gezek çap edýäris - konsumer takyk bir effekti (inbox) ýerine ýetirýär.
Order synagy: bir agregat boýunça wakalaryň bukjasy - yzygiderliligiň/idempotentligiň barlagy.
Chaos: dellalyň ret etmegi, DB-iň gizlinliginiň ýokarlanmagy, köpçüligiň bölünmegi, clock-skew.
14) Konfigurasiýa şablonlary (mysal)
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) Saglar we retralar bilen integrasiýa
Outbox - saga ädimleri üçin "howpsuzlyk ulagy": ýerli geleşik effekti we buýrugy/wakany ýazýar; neşir - ygtybarly we dozaly.
Gaýtalama we backoff syýasatlary 'Retry-After' we Circuit Breaker bilen utgaşdyrylmalydyr; "retraý tupanyndan" gaça duruň.
16) Adaty ýalňyşlyklar
Domen ýagdaýy kommitinden soň bir waka ýazýarlar - ýykylanda ýitgiler bolup biler.
'outbox' -da indeksler/arhiw ýok → neşiriň gijikdirilmeginiň ýokarlanmagy.
Jemgyýetçilik 'SKIP LOCKED' -siz ýa-da şardlamasyz - bäsdeşlik we blokirleme.
Sarp edijilerde biperwaýlygyň ýoklugy - goşa we zyýanly täsirleri.
DLQ/loglarda gizlenmezden PII garyşdyrmak.
Fairness bolmasa neşiriň ýeke-täk global nobaty - "şowhunly" tenant hemmeleri haýalladýar.
Lagyň gözegçiliginiň ýoklugy → gizlin zaýalanmalar.
17) Strategiýany çalt saýlamak
Başlangyç derejesi: DB-den polling, 100-500 batçi, full-jitter backoff, konsumerlerde inbox.
Ýokary ýük: Geleşikler žurnalyndan CDC, kärendeçiler boýunça 'tenant _ id/aggregate _ id', WFQ boýunça şardlamak.
Agregat boýunça berk tertip: per key (mutex) seriýaly neşir etmek, topiki açar bilen partiýalaşdyrmak.
Complayens/PII: şifrlemek payload, redaksiýa DLQ, sebitleýin outbox.
18) Azyk önüminden öň çek-sanawy
- Domen üýtgemeleri we 'outbox' ýazgysy bir amalda bolýar.
- Publisher batchy gaýtadan işleýär, 'SKIP LOCKED', jitter bilen backoff we çäkleri ulanýar.
- Konsumerler idempotentdir ('inbox '/dedup magazineurnaly).
- DLQ we ygtybarly redaktirleme sazlandy.
- Lagyň/ýalňyşlyklaryň metrikleri we p95/p99 çäkleri boýunça alertler.
- Açaryň tertibi kepillendirilýär (partiýa/seriýa).
- Arşiv/retenshn 'outbox' we çap edilen ýazgylary arassalamak.
- PII-syýasatlar we ýagdaýlaryň geçişleriniň barlagy.
- Kommit bilen neşiriň arasynda düşmek üçin synaglar, dublikatlar we tertip.
- Wakanyň şertnamalarynyň resminamalary (shemalar/wersiýalar/laýyklyk).
Netije
"Outbox-pattern" "DB" şinanyň "" gowşak "toparyny ygtybarly konweýer konweýerine öwürýär: ýagdaýyň atom taýdan kesgitlenmegi, kepillendirilen (iň bolmanda" bir gezek "bolsa-da) neşir, idempotent abonentler we gözegçilik edilýän redaksiýa. Dogry telemetriýa, çäkler we shemalaryň düzgüni bilen, paýlanan amallaryň çylşyrymlylygyny peseldip, ulgamyň şowsuzlyklara we iň ýokary ýüklere garşylygyny ýokarlandyryp, amaly exactly-once özüni alyp barşyny berýär.