GH GambleHub

Wzór skrzynki zewnętrznej

Outbox jest wzorcem architektonicznym, w którym usługa domeny zapisuje zmianę firmy i odpowiadające jej zdarzenie w jednej transakcji lokalnej do repozytorium. Publikacja zdarzenia do zewnętrznej magistrali/kolejki odbywa się asynchronicznie za pomocą oddzielnego bezpiecznego procesu (wydawcy), który czyta tabelę 'outbox' i przekazuje rekordy. To podejście eliminuje wyścig „najpierw do bazy danych, potem do autobusu” i zapewnia niezawodną dostawę nawet w przypadku awarii.

1) Kiedy stosować

Pasuje do:
  • Mikroservice i modułowe monolity z wydarzeniami między kontekstami.
  • Wymagane jest, aby zapewnić, że „stan jest ustalony” zdarzenie nie może zostać utracone.
  • Potrzebujemy idempotencji i kontrolowanej ponownej dostawy.
Nie nadaje się:
  • Trudne globalne transakcje na kilku zasobach mają kluczowe znaczenie (lepsze niż TCC/sagi z wyraźnymi umowami).
  • Nie ma dedykowanego źródła prawdy (stan nie jest przechowywany tam, gdzie zdarzenie jest generowane).

2) Cele i właściwości

Zapisz atomowy: rekord domeny + skrzynka zewnętrzna - w jednej transakcji.
Publikacja przynajmniej raz: zezwalamy na powtarzanie, wykluczamy straty.
Idempotencja konsumentów: ochrona przed przejmowaniem strony abonenta.
Efektywny dokładnie raz: osiągnięty przez połączenie outbox + idempotent consumer + dedup.
Clear telemetry - Koreluj transakcje i wydarzenia biznesowe.

3) Schemat danych (przykład)

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) Warstwa aplikacji

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

Jeśli commit się powiedzie, zdarzenie w skrzynce jest gwarantowane. Jeśli aplikacja spadnie po zatwierdzeniu, wydawca nadrobi zaległości.

5) Wydawca (czytelnik → wydawca)

Zadania:
  • Okresowo odczytywane niepublikowane zdarzenia ('published _ at IS NULL' i' available _ at <= now () '), partie.
  • Spróbuj opublikować do magistrali/kolejki; jeśli się powiedzie, zaznacz „published _ at”.
  • W przypadku błędu - zwiększyć 'próby', umieścić 'available _ at' na przyszłość (wykładnicze backoff), napisz 'błąd'.
  • Przestrzegaj ograniczeń dotyczących najemców/kluczy (uczciwość), nie blokuj produktu.
Pseudokoda:
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” eliminuje konkurencję wydawców.

6) Idempotencja i deduplikacja

Po stronie konsumenta (skrzynka odbiorcza/sklep Idempotency):
sql
CREATE TABLE inbox (
consumer_name  TEXT,
event_id    UUID,
processed_at  TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);

Algorytm: podczas otrzymywania zdarzenia najpierw spróbuj 'INSERT' w 'skrzynce odbiorczej'; jeśli istnieje kluczowy konflikt, wydarzenie zostało już załatwione → no-op. Następna jest logika biznesu.

Po stronie wydawcy: 'Idempotence-Key' w nagłówkach (na przykład 'event _ id'), aby autobus/broker/proxy mógł filtrować duplikaty.

7) Porządek i przyczynowość

Lokalne zamówienie przez 'agregate _ id' jest dostarczane przez sortowanie' occured _ at'i publikowanie „według klucza”.
W przypadku autobusów logarytmicznych z partycją - partycja z kluczem 'agregate _ id'/' lokator _ id', tak aby zdarzenia jednego agregatu były w tym samym przegrodzeniu.
Jeśli zamówienie jest krytyczne, należy unikać wyścigów wydawców jednokluczykowych.

8) CDC (zmiana przechwytywania danych)

Zamiast aktywnego wydawcy można użyć CDC: silnik odczytuje dziennik transakcji bazy danych i tłumaczy linie „outbox” na bus.Pls - minimalne obciążenie bazy danych, dokładna sekwencja, brak sondażu. Wady - komplikacje działania i powiązanie ze specyfiką DBMS. Oba podejścia są ważne; wybrać według kompetencji i SLO.

9) Błędy, DLQ i Redrive

Retryyable (network, limits) - zwiększenie 'attempts', odroczenie 'available _ at' (wykładnicze backoff + jitter).
Non-retryyable (invalid scheme/contract) - przeniesiony do DLQ/Dead-Letter Topic z bogatymi metadanymi.
Bezpieczne Redrive: partie, limit stawki, walidacja programu, priorytet poniżej ruchu produkcyjnego.

10) Wielozatrudnienie i limity

Wymagane znaczniki: 'lokator _ id',' plan ',' region '- w' outbox '. nagłówki ".
Uczciwość dla jednego najemcy: wydawca rozpowszechnia „okna” publikacji i ograniczenia prób dla najemców.
Miejsce zamieszkania: przechowywać skrzynkę zewnętrzną w tym samym regionie co dane domeny; publikacja międzyregionalna - tylko agregaty/podsumowania.

11) Bezpieczeństwo i zgodność

Wydanie PII w ładunku/nagłówkach dotyczących polityki lokatora/regionu.
Podpis/szyfrowanie ładunku, jeśli autobus jest obcy.
Audyt wszystkich przejść państwowych: utworzone, opublikowane, błędne, przeredagowane.

12) Obserwowalność

Metryka:
  • Opóźnienie publikacji ("teraz - occurred_at' p50/p95/p99).
  • Wskaźnik sukcesu, wskaźnik błędów, przyczyna dystrybucji.
  • Rozmiar skrzynki zewnętrznej (liczba niepublikowanych), powtórzenia/s
  • Na najemcę wykresy przepustowości i opóźnienia.
Śledzenie:
  • Korelacja 'event _ id'/' aggregate _ id'/' saga _ id'; przęsła „db-tx”, „publish”, „retry”.
  • Adnotacje: 'próba', 'backoff _ ms', 'dlq = true'.
Dzienniki:
  • Krótkie rekordy sukcesu; pełne szczegóły na błąd/przeredagowanie.

13) Testowanie i chaos

Test atomowości: sztucznie „upadek” po dokonaniu transakcji domeny przed publikacją - wydarzenie musi zostać zwolnione później.
Duplikat testu: publikujemy to samo wydarzenie kilka razy - konsument wykonuje dokładnie jeden efekt (skrzynka odbiorcza).
Próba zamówienia: partia zdarzeń według jednego kruszywa - kontrola sekwencji/idempotencji.
Chaos: niepowodzenie maklerskie, wzrost opóźnień w bazie danych, głosiciele podzielonych mózgów, zegar-skew.

14) Szablony konfiguracji (przykład)

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) Integracja z sagami i rekolekcjami

Outbox - „transport bezpieczeństwa” dla etapów sagi: lokalna transakcja zapisuje efekt i polecenie/zdarzenie; publikacja - niezawodne i dozowane.
Zasady powtarzania i cofania muszą być zgodne z „Retry-After” i „Circuit Breaker”; unikać „burzy retray”.

16) Typowe błędy

Piszą zdarzenie po popełnieniu stanu domeny - strata podczas upadku jest możliwa.
Brak indeksów/archiwum w 'outbox' → wzrost latencji publikacji.
Wydawca bez „SKIP LOCKED” lub bez ostrzału - konkurencja i blokowanie.
Brak idempotencji wśród konsumentów - duplikaty i skutki uboczne.
Mieszanie PII bez maskowania w DLQ/logi.
Jedna globalna kolejka wydawnicza bez uczciwości - „hałaśliwy” lokator spowalnia wszystkich.
Brak monitorowania opóźnień → utajona degradacja.

17) Szybki wybór strategii

Poziom początkowy: sondaż z bazy danych, 100-500 partii, pełny jitter backoff, skrzynka odbiorcza dla konsumentów.
Wysokie obciążenie: CDC z dziennika transakcji, shading przez 'lokator _ id/aggregate _ id', WFQ przez najemcę.
Ścisłe zamówienie według kruszywa: publikacja seryjna na klucz (mutex), podział tematu z kluczem.
Zgodność/PII: szyfrowanie ładunku, wydanie DLQ, regionalna skrzynka odbiorcza.

18) Lista kontrolna przedsprzedaży

  • Zmiany domeny i zapisy do 'outbox' występują w tej samej transakcji.
  • Wydawca obsługuje partie, używa „SKIP LOCKED”, backoff z jitterem i limitami.
  • Konsumenci są idempotentni (tablica „skrzynka odbiorcza ”/dziennik deadup).
  • Konfiguracja DLQ i Secure Release.
  • Wskaźniki lag/error i alert na progach p95/p99.
  • Kluczowe zamówienie jest gwarantowane (partie/serie).
  • Archiwum/retencja 'outbox' i jasne opublikowane zapisy.
  • Polityka PII i audyt państwa w okresie przejściowym.
  • Upuść testy między commit i opublikować, duplikaty i zamówienie.
  • Dokumentacja kontraktowa (schematy/wersje/kompatybilność).

Wnioski

Wzór skrzynki zewnętrznej zmienia „kruchy” pakiet „DB” w niezawodny rurociąg: utrwalenie stanu atomowego, gwarantowana (choć „przynajmniej raz”) publikacja, subskrybenci idempotent i kontrolowane redrave. Dzięki odpowiedniej telemetrii, limitom i dyscyplinie schematu daje praktyczne zachowanie dokładnie raz, zmniejszając złożoność transakcji rozproszonych i zwiększając odporność systemu na awarie i obciążenia szczytowe.

Contact

Skontaktuj się z nami

Napisz do nas w każdej sprawie — pytania, wsparcie, konsultacje.Zawsze jesteśmy gotowi pomóc!

Telegram
@Gamble_GC
Rozpocznij integrację

Email jest wymagany. Telegram lub WhatsApp są opcjonalne.

Twoje imię opcjonalne
Email opcjonalne
Temat opcjonalne
Wiadomość opcjonalne
Telegram opcjonalne
@
Jeśli podasz Telegram — odpowiemy także tam, oprócz emaila.
WhatsApp opcjonalne
Format: kod kraju i numer (np. +48XXXXXXXXX).

Klikając przycisk, wyrażasz zgodę na przetwarzanie swoich danych.