GH GambleHub

Outbox-μοτίβο

Outbox είναι ένα αρχιτεκτονικό μοτίβο στο οποίο μια υπηρεσία τομέα γράφει μια επιχειρηματική αλλαγή και το αντίστοιχο γεγονός σε μια τοπική συναλλαγή στο αποθετήριό της. Η δημοσίευση της εκδήλωσης στο εξωτερικό λεωφορείο/ουρά εκτελείται ασύγχρονα από μια ξεχωριστή ασφαλή διαδικασία (εκδότης) που διαβάζει τον πίνακα 'outbox' και μεταδίδει τα αρχεία. Αυτή η προσέγγιση εξαλείφει τον αγώνα «πρώτα στη βάση δεδομένων, μετά στο λεωφορείο» και παρέχει αξιόπιστη παράδοση ακόμη και σε περίπτωση αποτυχίας.

1) Πότε να υποβάλετε αίτηση

Προσαρμοσμένο:
  • Μικροϋπηρεσίες και αρθρωτοί μονόλιθοι με γεγονότα μεταξύ των πλαισίων.
  • Απαιτείται να εξασφαλιστεί ότι «το κράτος είναι σταθερό ↔ το γεγονός δεν μπορεί να χαθεί».
  • Χρειαζόμαστε ιδεολογία και ελεγχόμενη εκ νέου παράδοση.
Ακατάλληλο:
  • Οι σκληρές παγκόσμιες συναλλαγές με διάφορους πόρους είναι κρίσιμης σημασίας (καλύτερα από το TCC/sagas με ρητές συμβάσεις).
  • Δεν υπάρχει ειδική πηγή αλήθειας (η κατάσταση δεν αποθηκεύεται εκεί όπου δημιουργείται το γεγονός).

2) Στόχοι και ιδιότητες

Ατομική εγγραφή: domain record + outbox - σε μία συναλλαγή.
Τουλάχιστον μία φορά τη δημοσίευση: επιτρέπουμε την επανάληψη, αποκλείουμε την απώλεια.
Ιδιαιτερότητα των καταναλωτών: προστασία έναντι της ανάληψης από την πλευρά των συνδρομητών.
Αποτελεσματική ακριβώς μία φορά: επιτυγχάνεται με το συνδυασμό outbox + idempotent καταναλωτή + 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) Στρώμα εφαρμογής

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

Αν η δέσμευση είναι επιτυχής, το γεγονός στο outbox είναι εγγυημένο ότι υπάρχει. Εάν η αίτηση πέσει μετά τη δέσμευση, ο εκδότης θα καλύψει τη διαφορά.

5) Εκδότης (αναγνώστης → εκδότης)

Καθήκοντα:
  • Περιοδική ανάγνωση αδημοσίευτων γεγονότων ('published _ at IS NULL' and 'available _ at <= now ()'), παρτίδες.
  • Προσπαθήστε να δημοσιεύσετε το λεωφορείο/σειρά αναμονής. εάν έχει επιτυχία, το σήμα "published _ a .
  • Σε περίπτωση σφάλματος - αύξηση 'προσπαθειών', βάλτε 'διαθέσιμο _ στο' για το μέλλον (εκθετική εφεδρεία), γράψτε 'σφάλμα'.
  • Τήρηση των ορίων για τους ενοικιαστές/κλειδιά (δίκαιη μεταχείριση), μην εμποδίζετε το προϊόν.
Ψευδοκώδικας:
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» εξαλείφει τον ανταγωνισμό εκδοτών.

6) Ιδιαιτερότητα και αποπροσανατολισμός

Από την πλευρά των καταναλωτών (κατάστημα Inbox/Idempotency):
sql
CREATE TABLE inbox (
consumer_name  TEXT,
event_id    UUID,
processed_at  TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);

Αλγόριθμος: όταν λαμβάνετε ένα γεγονός, προσπαθήστε πρώτα να 'ΕΙΣΆΓΕΤΕ' στα 'εισερχόμενα'. εάν υπάρχει σύγκρουση-κλειδί, το γεγονός έχει ήδη αντιμετωπιστεί → μη-επιχείρηση. Επόμενο είναι η επιχειρηματική λογική.

Από την πλευρά του εκδότη: 'Idempotency-Key' σε κεφαλίδες (για παράδειγμα, 'event _ id') έτσι ώστε το λεωφορείο/μεσίτης/διαμεσολαβητής να μπορεί να φιλτράρει αντίγραφα.

7) Τάξη και αιτιώδης συνάφεια

Τοπική τάξη από 'agregate _ i is παρέχεται από τη διαλογή' occured _ at 'και τη δημοσίευση «ανά κλειδί».
Για τα ξύλινα λεωφορεία με διαχωρισμό - κατάτμηση με το πλήκτρο 'agregate _ i /' ενοικιαστής _ id' έτσι ώστε τα γεγονότα ενός συνόλου να βρίσκονται στην ίδια κατάτμηση.
Αν η τάξη είναι κρίσιμη, αποφύγετε τους αγώνες ενός μόνο κλειδιού.

8) CDC (Καταγραφή μεταβολών δεδομένων)

Αντί για έναν ενεργό εκδότη, μπορείτε να χρησιμοποιήσετε το CDC: ο κινητήρας διαβάζει το ημερολόγιο συναλλαγών της βάσης δεδομένων και μεταφράζει τις γραμμές 'outbox' στο λεωφορείο. Μειονεκτήματα - επιπλοκή της λειτουργίας και σχέση με τις ιδιαιτερότητες του συστήματος DBMS. Και οι δύο προσεγγίσεις είναι έγκυρες. επιλέγουν από ικανότητες και SLO.

9) Σφάλματα, DLQ και Redrive

Αναδρομική (δίκτυο, όρια) - αύξηση των 'attempts', αναβολή της 'available _ at' (εκθετική backoff + jitter).
Μη ανακτήσιμο (άκυρο σύστημα/σύμβαση) - μεταβιβάζεται στο DLQ/Dead-Letter Topic με πλούσια μεταδεδομένα.
Safe Redrive: παρτίδες, όριο ταχύτητας, επικύρωση του συστήματος, προτεραιότητα κάτω από την κυκλοφορία παραγωγής.

10) Πολυπλοκότητα και όρια

Απαιτούμενες ετικέτες: 'ενοικιαστής _ id', 'σχέδιο', 'περιφέρεια' - σε 'outbox. κεφαλίδες ".
Δίκαιη μεταχείριση των ενοίκων: ο εκδότης διανέμει τα «παράθυρα» των εκδόσεων και τα όρια των προσπαθειών των ενοίκων.
Κατοικία: αποθήκευση των εξερχόμενων στην ίδια περιοχή με τα δεδομένα τομέα. διαπεριφερειακή δημοσίευση - συγκεντρωτικά στοιχεία/περιλήψεις μόνο.

11) Ασφάλεια και συμμόρφωση

PII έκδοση σε ωφέλιμο φορτίο/κεφαλίδες σχετικά με την πολιτική ενοικιαστών/περιφερειών.
Υπογραφή/κρυπτογράφηση του ωφέλιμου φορτίου εάν το λεωφορείο είναι ξένο.
Έλεγχος όλων των μεταβάσεων: δημιουργήθηκε, δημοσιεύθηκε, σφάλμα, ανακατασκευή.

12) Παρατηρησιμότητα

Μετρήσεις:
  • Υστέρηση δημοσίευσης ('τώρα - occurred_at' p50/p95/p99).
  • Ποσοστό επιτυχίας, ποσοστό σφάλματος, αιτία κατανομής.
  • Μέγεθος κιβωτίου εξόδου (αριθμός αδημοσίευτων κιβωτίων), διορθώσεις/sec
  • Διαγράμματα ανά μισθωτή με διακίνηση και υστέρηση.
Ιχνηλάτηση:
  • Συσχέτιση "event _ i /' cregate _ i /' saga _ id ', «db-tx», «publish», «retriby».
  • Σημειώσεις: 'προσπάθεια', 'backoff _ m ,' dlq = αλήθεια '.
Αρχεία καταγραφής:
  • Σύντομες καταγραφές επιτυχίας· πλήρεις λεπτομέρειες ανά σφάλμα/ανακατασκευή.

13) Δοκιμές και χάος

Δοκιμή ατομικότητας: τεχνητά «πτώση» μετά από μια συναλλαγή τομέα πριν από τη δημοσίευση - το γεγονός πρέπει να κυκλοφορήσει αργότερα.
Διπλή δοκιμή: δημοσιεύουμε το ίδιο γεγονός αρκετές φορές - ο καταναλωτής εκτελεί ακριβώς ένα αποτέλεσμα (εισερχόμενα).
Δοκιμή παραγγελίας: παρτίδα γεγονότων κατά ένα σύνολο - έλεγχος ακολουθίας/ταυτότητας.
Χάος: αποτυχία μεσίτη, αύξηση της καθυστέρησης βάσης δεδομένων, εκδότες split-brain, ρολόι-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) Ενσωμάτωση με σάγκα και υποχώρηση

Outbox - «μεταφορά ασφαλείας» για στάδια saga: η τοπική συναλλαγή γράφει εφέ και εντολή/γεγονός. δημοσίευση - αξιόπιστη και σε δόση.
Οι επαναλαμβανόμενες και εφεδρικές πολιτικές πρέπει να είναι συνεπείς με το «Retry-After» και το «Circuit Breaker». να αποφύγει την «καταιγίδα επαναπροώθησης».

16) Τυπικά σφάλματα

Γράφουν ένα γεγονός μετά τη δέσμευση της κατάστασης τομέα - η απώλεια κατά τη διάρκεια μιας πτώσης είναι δυνατή.
Δεν υπάρχουν ευρετήρια/αρχεία στο 'outbox' → αύξηση καθυστέρησης δημοσίευσης.
Εκδότης χωρίς «SKIP LOCKED» ή χωρίς sharing - ανταγωνισμός και μπλοκάρισμα.
Έλλειψη ευελιξίας μεταξύ των καταναλωτών - αντίγραφα και παρενέργειες.
Ανάμειξη PII χωρίς συγκάλυψη σε DLQ/logs.
Μια ενιαία παγκόσμια σειρά αναμονής χωρίς δικαιοσύνη - ένας «θορυβώδης» ενοικιαστής επιβραδύνει τους πάντες.
Έλλειψη παρακολούθησης της υστέρησης → λανθάνουσα υποβάθμιση.

17) Γρήγορη επιλογή στρατηγικής

Επίπεδο εκκίνησης: δημοσκόπηση από τη βάση δεδομένων, 100-500 παρτίδες, πλήρης οπισθοδρόμηση, εισερχόμενα για τους καταναλωτές.
Υψηλό φορτίο: ΚΕΕΛΠΝΟ από το ημερολόγιο συναλλαγών, που φέρει την ένδειξη «ενοικιαστής _ id/σύνολο _ id», WFQ ανά ενοικιαστή.
Αυστηρή σειρά κατά σύνολο: σειριακή δημοσίευση ανά κλειδί (mutex), κατάτμηση του θέματος με κλειδί.
Συμμόρφωση/PII: κρυπτογράφηση ωφέλιμου φορτίου, έκδοση DLQ, περιφερειακό outbox.

18) Κατάλογος ελέγχου πριν από την πώληση

  • Αλλαγές τομέα και γράφει στο 'outbox' συμβαίνουν στην ίδια συναλλαγή.
  • Ο εκδότης χειρίζεται παρτίδες, χρησιμοποιεί το «SKIP LOCKED», backoff με νευρικότητα και όρια.
  • Οι καταναλωτές είναι ευδιάκριτοι (επιτραπέζια «inbox »/deadup log).
  • Το DLQ και το Secure Release είναι ρυθμισμένα.
  • Lag/σφάλμα και μετρήσεις συναγερμού στα όρια p95/p99.
  • Η βασική σειρά είναι εγγυημένη (παρτίδες/σειρές).
  • Αρχείο/διατήρηση 'outbox' και σαφή δημοσιευμένα αρχεία.
  • Πολιτικές PII και έλεγχος της κρατικής μετάβασης.
  • Δοκιμές πτώσης μεταξύ της δέσμευσης και της δημοσίευσης, των αντιγράφων και της παραγγελίας.
  • Τεκμηρίωση σύμβασης γεγονότων (σχήματα/εκδόσεις/συμβατότητα).

Συμπέρασμα

Το outbox μοτίβο μετατρέπει το «εύθραυστο» πακέτο του «DB ↔ λεωφορείου» σε έναν αξιόπιστο αγωγό: ατομική στερέωση κατάστασης, εγγυημένη (αν και «τουλάχιστον μία φορά») δημοσίευση, idempotent συνδρομητές και ελεγχόμενη redrave. Με σωστή τηλεμετρία, όρια και πειθαρχία σχημάτων, δίνει πρακτική συμπεριφορά ακριβώς μία φορά, μειώνοντας την πολυπλοκότητα των κατανεμημένων συναλλαγών και αυξάνοντας την ανθεκτικότητα του συστήματος σε συντριβές και φορτία αιχμής.

Contact

Επικοινωνήστε μαζί μας

Επικοινωνήστε για οποιαδήποτε βοήθεια ή πληροφορία.Είμαστε πάντα στη διάθεσή σας.

Telegram
@Gamble_GC
Έναρξη ολοκλήρωσης

Το Email είναι υποχρεωτικό. Telegram ή WhatsApp — προαιρετικά.

Το όνομά σας προαιρετικό
Email προαιρετικό
Θέμα προαιρετικό
Μήνυμα προαιρετικό
Telegram προαιρετικό
@
Αν εισαγάγετε Telegram — θα απαντήσουμε και εκεί.
WhatsApp προαιρετικό
Μορφή: κωδικός χώρας + αριθμός (π.χ. +30XXXXXXXXX).

Πατώντας «Αποστολή» συμφωνείτε με την επεξεργασία δεδομένων.