GH GambleHub

Outbox nümunəsi

Outbox, domen xidmətinin iş dəyişikliyini və müvafiq hadisəni öz anbarında bir yerli əməliyyatda qeyd etdiyi bir memarlıq nümunəsidir. Hadisənin xarici şinə/növbəyə dərc edilməsi «outbox» cədvəlini oxuyan və qeydləri yayımlayan ayrı bir təhlükəsiz proses (publisher) vasitəsilə asenxron şəkildə həyata keçirilir. Bu yanaşma «əvvəlcə DB-də, sonra şində» yarışı aradan qaldırır və uğursuzluqlar baş verdikdə belə etibarlı çatdırılma təmin edir.

1) Nə zaman tətbiq olunur

Uyğun:
  • Kontekstlər arasında hadisələr olan mikroservislər və modul monolitlər.
  • Zəmanət tələb olunur ki, «hal qeydə alınıb, hadisə itirilə bilməz».
  • İdempotentlik və nəzarət olunan təkrar çatdırılma lazımdır.
Uyğun deyil:
  • Bir neçə resursda sərt qlobal əməliyyatlar kritik əhəmiyyət kəsb edir (açıq müqavilələrlə daha yaxşı TSS/dastan).
  • Xüsusi həqiqət mənbəyi yoxdur (state hadisənin yarandığı yerdə saxlanılmır).

2) Məqsədləri və xüsusiyyətləri

Atomic write: domen qeydiyyatı + outbox - bir əməliyyatda.
At-least-once nəşri: təkrar etməyə icazə veririk, itkini istisna edirik.
İstehlakçı idempotentliyi: abunəçilər tərəfində dubllardan qorunma.
Effektiv exactly-once: outbox + idempotent consumer + dedup kombinasiyası ilə əldə edilir.
Aydın telemetriya: biznes əməliyyatları və hadisələrin korrelyasiyası.

3) Verilənlər sxemi (nümunə)

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) Əməliyyat şablonu (application layer)

pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys

Əgər kommit uğurlu olarsa, outbox hadisəsi mövcuddur. Əgər proqram kommitdən sonra düşsə, ictimaiyyət onu tutacaq.

5) Publisher (reader → publisher)

Tapşırıqlar:
  • Yayımlanmamış hadisələri vaxtaşırı oxumaq ('published _ at IS NULL' və 'available _ at <= now ()'), batches.
  • Şin/növbə dərc etməyə çalışın; müvəffəqiyyətlə - 'published _ at' qeyd edin.
  • Səhv olarsa - 'attempts' artırmaq, 'available _ at' gələcəyə qoymaq (exponential backoff), 'error' yazmaq.
  • Tenant/açar limitlərinə hörmət edin (fairness), məhsuldarlığı bloklamayın.
Psevdokod:
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
💡 'FOR UPDATE SKIP LOCKED' publisistlərin rəqabətini istisna edir.

6) İdempotentlik və duplikasiya

İstehlakçı tərəfində (Inbox/Idempotency store):
sql
CREATE TABLE inbox (
consumer_name  TEXT,
event_id    UUID,
processed_at  TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);

Alqoritm: hadisə alındıqda - əvvəlcə 'INSERT' in 'inbox' cəhdi; açar münaqişəsi olarsa - hadisə artıq işlənmişdir → «no-op». Sonrakı - biznes məntiqi.

İctimai tərəfdə: 'Idempotency-Key' başlıqlarında (məsələn, 'event _ id'), belə ki, şin/broker/proxy dublikatları süzə bilər.

7) Sifariş və səbəb

'aggregate _ id' yerli qaydası 'occurred _ at' çeşidlənməsi və «açar vasitəsilə» dərc edilməsi ilə təmin edilir.
Partiyalaşdırma ilə log-şinlər üçün - 'aggregate _ id '/' tenant _ id' açarı ilə partiyalaşdırın ki, bir aqreqatın hadisələri bir partişendə olsun.
Sifariş kritikdirsə, ictimaiyyətin bir açar üzrə axınlararası yarışlarından çəkinin.

8) CDC (Change Data Capture)

Aktiv reklam əvəzinə CDC istifadə edə bilərsiniz: mühərrik DB əməliyyat jurnalını oxuyur və 'outbox' sətirlərini şinə ötürür. Üstünlüklər - minimum DB yükü, dəqiq ardıcıllıq, pollinq olmaması. Mənfi cəhətləri - əməliyyatın mürəkkəbləşməsi və DBB xüsusiyyətlərinə bağlanması. Hər iki yanaşma validdir; səlahiyyət və SLO seçin.

9) Səhvlər, DLQ və redrave

Retryable (şəbəkə, limitlər) - 'attempts' artırmaq, 'available _ at' (exponential backoff + jitter) təxirə salmaq.
Qeyri-retryable - zəngin metadata malik DLQ/Dead-Letter Topic-ə köçürülür.
Təhlükəsiz redrave: batchi, rate-limit, sxem validasiyası, proyad trafikdən aşağı prioritet.

10) Çox tenantlıq və limitlər

Məcburi etiketlər: 'tenant _ id', 'plan', 'region' - in 'outbox. headers`.
Per-tenant fairness: publisist «pəncərə» nəşrləri və kirayəçilər üçün cəhd limitləri paylayır.
Residency: domen məlumatlarının olduğu eyni bölgədə outbox saxlayın; regionlararası nəşr - yalnız aqreqatlar/hesabatlar.

11) Təhlükəsizlik və uyğunluq

tenant/region siyasəti payload/headers PII-edition.
Şin «yad» olduqda faydalı yük imza/şifrələmə.
Bütün dövlət keçidlərinin auditi: yaradıldı, nəşr olundu, səhv, redrave.

12) Müşahidə

Metriklər:
  • Post Lag ('now - occurred_at' p50/p95/p99).
  • Uğurların payı, səhvlərin payı, səbəblərin bölüşdürülməsi.
  • outbox ölçüsü (yayımlanmamış sayı), cəhd/san.
  • throughput və lag.
Treysinq:
  • Korrelyasiya 'event _ id '/' aggregate _ id '/' saga _ id'; «db-tx», «publish», «retry» yataqları.
  • Şərhlər: 'attempt', 'backoff _ ms', 'dlq = true'.
Loqi:
  • Uğur üçün qısa qeydlər; səhv/redrave üçün tam detallar.

13) Test və xaos

Atomicity Test: dərc əvvəl domen əməliyyatı kommit sonra süni «düşür» - hadisə sonra getmək lazımdır.
Duplicate test: Eyni hadisəni bir neçə dəfə dərc edirik - konsumer tam bir effekti (inbox) yerinə yetirir.
Order test: Bir aqreqat üzrə hadisələr paketi - ardıcıllıq/idempotentlik testi.
Chaos: broker rədd, DB gizli artım, split-brain publichers, clock-skew.

14) Konfiqurasiya şablonları (nümunə)

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) Dastan və retrajlarla inteqrasiya

Outbox - dastan addımları üçün «təhlükəsizlik nəqliyyatı»: lokal əməliyyat effekti və komanda/hadisə yazır; nəşr - etibarlı və dozalı.
Təkrarlama və backoff siyasətləri 'Retry-After' və Circuit Breaker ilə razılaşdırılmalıdır; «retraj fırtınasından» qaçın.

16) Tipik səhvlər

Domain State Committen sonra bir hadisə yazın - yıxıldıqda itki mümkündür.
Heç bir indekslər/arxiv 'outbox' → nəşr gecikmə artım.
Heç bir 'SKIP LOCKED' və ya charding olmadan reklam - rəqabət və bloklama.
İstehlakçılarda idempotentliyin olmaması - ikiqat və yan təsirləri.
DLQ/log maskasız PII qarışdırılması.
fairness olmadan vahid qlobal nəşr növbəsi - «səs-küylü» tenant hər kəsi yavaşlatır.
Lag monitorinqinin olmaması → gizli deqradasiyalar.

17) Sürətli strategiya seçimi

Başlanğıc səviyyəsi: DB-dən polling, 100-500 batches, full-jitter backoff, konsumerlərdə inbox.
Yüksək yük: Əməliyyat jurnalından CDC, 'tenant _ id/aggregate _ id', kirayəçilər üçün WFQ.
Aqreqat üzrə ciddi sifariş: per key (mutex) seriya nəşri, topik açarı ilə partizan.
Complayens/PII: payload şifrələmə, DLQ redaktə, regional outbox.

18) Satış öncəsi yoxlama siyahısı

  • Domen dəyişikliyi və 'outbox' yazısı bir əməliyyatda baş verir.
  • Publisher batches emal edir, 'SKIP LOCKED', jitter və limitləri ilə backoff istifadə edir.
  • Konsumerlər idempotentdir (cədvəl 'inbox '/dedup jurnalı).
  • DLQ və təhlükəsiz redrave konfiqurasiya.
  • P95/p99 həddinə görə lag/səhv və alertlər.
  • Açar qaydasına zəmanət verilir (partiyalar/seriyalar).
  • Arxiv/retenshn 'outbox' və nəşr qeydlərin təmizlənməsi.
  • PII siyasətləri və status keçidlərinin auditi.
  • Kommit və nəşr, təkrarlanan və sifariş arasında düşmə testləri.
  • Hadisə müqavilələrinin sənədləşdirilməsi (sxemlər/versiyalar/uyğunluq).

Nəticə

Outbox-pattern «kövrək» bağlayıcı «BD şina» etibarlı konveyer çevirir: atom vəziyyəti fiksasiya, zəmanətli (hətta «ən azı bir dəfə») nəşr, idempotent abunəçilər və nəzarət redrayv. Düzgün telemetriya, limitlər və sxemlərin nizam-intizamı ilə o, praktik exactly-once davranış verir, paylanmış əməliyyatların mürəkkəbliyini azaldır və sistemin uğursuzluqlara və pik yüklərə davamlılığını artırır.

Contact

Bizimlə əlaqə

Hər hansı sualınız və ya dəstək ehtiyacınız varsa — bizimlə əlaqə saxlayın.Həmişə köməyə hazırıq!

Telegram
@Gamble_GC
İnteqrasiyaya başla

Email — məcburidir. Telegram və ya WhatsApp — istəyə bağlıdır.

Adınız istəyə bağlı
Email istəyə bağlı
Mövzu istəyə bağlı
Mesaj istəyə bağlı
Telegram istəyə bağlı
@
Əgər Telegram daxil etsəniz — Email ilə yanaşı orada da cavab verəcəyik.
WhatsApp istəyə bağlı
Format: ölkə kodu + nömrə (məsələn, +994XXXXXXXXX).

Düyməyə basmaqla məlumatların işlənməsinə razılıq vermiş olursunuz.