Outbox үлгүсү
Outbox - бул архитектуралык үлгү, анда домендик кызмат өзүнүн кампасында бир жергиликтүү транзакцияда бизнес өзгөрүшүн жана тиешелүү окуяны жазат. Окуяны тышкы шинага/кезекке жарыялоо "outbox" таблицасын окуган жана жазууларды ретрансляциялаган өзүнчө коопсуз процесс (publisher) менен синхрондуу аткарылат. Бул ыкма жарыш жок "биринчи DD, андан кийин дөңгөлөк", ал тургай, ийгиликсиз учурда ишенимдүү жеткирүүнү камсыз кылат.
1) Качан колдонуу керек
Ылайыктуу:- Микросервистер жана контексттердин ортосундагы окуялар менен модулдук монолиттер.
- Бул кепилдик талап кылынат, "абалы жазылган окуя жоголуп кетиши мүмкүн эмес".
- Демпотенттик жана контролдук кайра жеткирүү керек.
- Бир нече ресурстарда катуу глобалдык транзакциялар маанилүү (так келишимдер менен TSS/сагадан жакшыраак).
- Эч кандай атайын чындык булагы (мамлекет окуя түзүлгөн жерде сакталган эмес).
2) Максаттары жана касиеттери
Atomic write: домен жазуусу + outbox - бир бүтүмүндө.
At-least-once жарыялоо: кайталап жол, жоготуу жок.
Керектөөчүлөрдүн демпотенттүүлүгү: абоненттер тарапта дубльдан коргоо.
Натыйжалуу exactly-once: outbox + idempotent consumer + dedup айкалышы менен жетишилет.
Так телеметрия: бизнес-иш-чаралар менен байланыш.
3) Маалыматтар схемасы (мисал)
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) Транзакциялык шаблон (application layer)
pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys
Эгерде коммит ийгиликтүү болсо - outbox окуясы бар деп кепилдик берилет. Эгерде тиркеме коммиттен кийин куласа, анда коомчулук аны кууп жетет.
5) Publisher (reader → publisher)
Милдеттери:- Мезгил-мезгили менен жарыяланбаган окуяларды ('published _ at IS NULL' жана 'available _ at <= now ()'), батч.
- шиналар/кезек жарыялоого аракет; ийгиликтүү болсо, 'published _ at' деп белгилөө керек.
- ката - көбөйтүү 'attempts', келечекке 'available _ at' коюп (exponential backoff), 'error' жазуу.
- Тенанттар/ачкычтар боюнча лимиттерди урматтоо (fairness), продуктивдүү тоскоолдук кылбоо.
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) Демпотенттүүлүк жана дедупликация
Керектөөчү тарапта (Inbox/Idempotency store):sql
CREATE TABLE inbox (
consumer_name TEXT,
event_id UUID,
processed_at TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);
Алгоритм: окуяны алганда - биринчи аракет 'INSERT' in 'inbox'; эгерде ачкыч чыр-чатак - окуя буга чейин иштелип чыккан → "no-op". Андан ары - бизнес-логика.
коомдук тарапта: 'Idempotency-Key' жылы headers (мисалы, 'event _ id'), ошондуктан шиналар/брокер/прокси дубликаттарын чыпкалап алат.
7) Тартиби жана себептери
'aggregate _ id' боюнча жергиликтүү тартип 'occurred _ at' сорттоо жана 'ачкыч боюнча' жарыялоо менен камсыз кылынат.
Партиялаштыруу менен лог-шиналар үчүн - бир агрегаттын окуялары бир партишенде болушу үчүн 'aggregate _ id '/' tenant _ id' ачкычы менен партиялаңыз.
Эгерде тартип оор болсо, бир ачкыч боюнча эл аралык жарыштан качыңыз.
8) CDC (Change Data Capture)
Активдүү жарнаманын ордуна CDC колдонсо болот: кыймылдаткыч DD транзакцияларынын журналын окуйт жана 'outbox' саптары шинага которулат. Плюсы - минималдуу жүк DD, так ырааттуулугу, жок polling. Кемчиликтери - операцияны татаалдаштыруу жана СУБДнын өзгөчөлүгүнө байлоо. Эки ыкма тең валиддик; компетенттүүлүк жана SLO боюнча тандоо.
9) Каталар, DLQ жана Редрайв
Retryable (тармак, лимиттер) - көбөйтүү 'attempts', кийинкиге калтыруу 'available _ at' (exponential backoff + життер).
Non-retryable (нөлдүк схема/келишим) - DLQ/Dead-Letter Topic бай метадеректер менен которулат.
Коопсуз редрайв: батчи, rate-limit, схема валидациясы, прод-трафиктен төмөн артыкчылык.
10) Көп-тенанттуулук жана лимиттер
Милдеттүү tags: 'tenant _ id', 'plan', 'region' - 'outbox'. headers`.
Per-tenant fairness: pablisher бөлүштүрөт "терезе" жарыялоо жана лимиттери аракет ижарачылар.
Residency: домендик маалыматтар бир аймакта outbox сактоо; аймактар аралык жарыялоо - агрегаттар/отчеттор гана.
11) Коопсуздук жана шайкештик
PII-өзгөртүүлөр жана толуктоолор менен payload/башчысы tenant/аймак саясаты боюнча.
Кол коюу/шина "бөтөн" болсо, пайдалуу жүктү шифрлөө.
Бардык мамлекеттик өткөөл аудити: түзүлгөн, жарыяланган, ката, редрайв.
12) Байкоо
Метрикасы:- Жарыяланган лаг ('now - occurred_at' p50/p95/p99).
- Ийгиликтин үлүшү, каталардын үлүшү, себептердин бөлүштүрүлүшү.
- outbox көлөмү (жарыяланбаган саны), аракет/сек.
- throughput жана lag.
- 'event _ id '/' aggregate _ id '/' saga _ id' корреляциясы; "db-tx", "publish", "retry".
- Түшүндүрмөлөр: 'attempt', 'backoff _ ms', 'dlq = true'.
- Ийгиликке кыскача жазуулар; ката/redrave толук маалымат.
13) сыноо жана башаламандык
Atomicity сыноо: жарыяланганга чейин домендик транзакция коммитинен кийин жасалма "жыгылып" - окуя кийинчерээк чыгып кетүүгө милдеттүү.
Duplicate сыноо: бир эле иш-чараны бир нече жолу жарыялайбыз - консьмер так бир эффектти (inbox) аткарат.
Order сыноо: бир агрегат боюнча окуялардын пакети - ырааттуулугун/ыктымалдыгын текшерүү.
Chaos: брокердин баш тартуусу, БДнын латенттүүлүгүнүн өсүшү, коомчулуктун бөлүнүшү, clock-skew.
14) Конфигурациялык үлгүлөр (мисал)
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) Дастан жана Retrains менен бириктирүү
Outbox - саганын кадамдар үчүн "коопсуздук транспорт": жергиликтүү бүтүм таасир жана команда/окуя жазат; жарыялоо - ишенимдүү жана дозалануучу.
Кайталоо жана backoff саясаты 'Retry-After' жана Circuit Breaker менен макулдашылышы керек; "retrais бороонунан" качыңыз.
16) типтүү каталар
Домендик абалдын коммитинен кийинки окуяны жазыңыз - жыгылганда жоготуу болушу мүмкүн.
Эч кандай индекстер/архив 'outbox' → жарыялоо кечигүү өсүшү.
Publisher жок 'SKIP LOCKED' же эч кандай шардана - атаандаштык жана бөгөт коюу.
Керектөөчүлөрдүн демпотенттигинин жоктугу - дубль жана терс таасирлери.
аралаштыруу PII DLQ/Логтордо жашыруу жок.
fairness жок жарыялоо бир дүйнөлүк кезек - "ызы-чуу" Тенант ар бир тоскоолдук кылат.
Жок мониторинг Лаги → жашыруун бузулуулар.
17) Тез тандоо стратегиясы
Старттык деңгээл: DDдан поллинг, 100-500 батчи, full-jitter backoff, консумерлерде inbox.
Жогорку жүк: CDC бүтүм журналынан, 'tenant _ id/aggregate _ id' боюнча шардана, ижарачылар үчүн WFQ.
Агрегаттын катуу тартиби: сериялык жарыялоо per key (mutex), топикти ачкыч менен партиялаштыруу.
Комплаенс/PII: шифрлөө payload, DLQ редакторлору, аймактык outbox.
18) Азык-түлүктүн алдындагы чек-тизме
- Домендик өзгөрүүлөр жана 'outbox' жазуусу бир транзакцияда болот.
- Publisher батчи иштетет, 'SKIP LOCKED', Jitter менен backoff жана лимиттерди колдонот.
- Consumers idempotent (таблица 'inbox '/дедуп журналы).
- орнотулган DLQ жана коопсуз redrave.
- P95/P99 босогосунда боюнча Лага/ката жана Алерта өлчөмдөрү.
- Ачкыч тартиби кепилденген (партия/сериясы).
- Archive/retenshn 'outbox' жана жарыяланган жазууларды тазалоо.
- PII-саясат жана мамлекеттик өтүү аудит.
- Committee жана жарыялоо, кайталоо жана тартиби ортосунда кулап тесттер.
- Иш-чаранын келишимдеринин документтери (схемалар/версиялар/шайкештик).
Корутунду
Outbox-үлгү "алсыз" байламта "БД шина" ишенимдүү конвейер айлантат: атомдук абалын бекитүү, кепилденген (жок дегенде "бир жолу" болсо да) жарыялоо, демпотенттик абоненттер жана көзөмөлгө алынган редрайв. Туура телеметрия менен, чеги жана тартиби схемалар, ал практикалык exactly-once жүрүм-турумун берет, бөлүштүрүлгөн бүтүмдөрдүн татаалдыгын азайтуу жана каталар жана жогорку жүктөр үчүн системанын туруктуулугун жогорулатуу.