Outbox შაბლონი
Outbox არის არქიტექტურული ნიმუში, რომელშიც აფეთქების ღუმელის სერვისი წერს ბიზნეს ცვლილებას და შესაბამის მოვლენას ერთ ადგილობრივ გარიგებაში, მის საცავში. ღონისძიების გამოქვეყნება გარე საბურავზე/რიგში ხორციელდება ასინქრონულად ცალკეული უსაფრთხო პროცესით (publisher), კითხულობს outbox ცხრილს და სარელეო ჩანაწერებს. ეს მიდგომა გამორიცხავს რბოლას „ჯერ BD- ში, შემდეგ საბურავზე“ და უზრუნველყოფს საიმედო მიწოდებას, თუნდაც წარუმატებლობის შემთხვევაში.
1) გამოყენებისას
შესაფერისია:- მიკროსერვისი და მოდულური მონოლითები კონტექსტებს შორის მოვლენებით.
- აუცილებელია გარანტია, რომ „მდგომარეობა დაფიქსირდა. მოვლენა არ შეიძლება დაიკარგოს“.
- ჩვენ გვჭირდება idempotence და კონტროლირებადი განმეორებითი მიწოდება.
- კრიტიკულად მკაცრი გლობალური გარიგებები რამდენიმე რესურსზე (უკეთესია TSS/საგა აშკარა კონტრაქტებით).
- არ არსებობს ჭეშმარიტების განსაკუთრებული წყარო (სახელმწიფო ინახება არა იქ, სადაც მოვლენა წარმოიქმნება).
2) მიზნები და თვისებები
Atomic write: დომენის ჩაწერა + outbox - ერთ გარიგებაში.
At-least-once პუბლიკაცია: ჩვენ ვუშვებთ განმეორებას, აღმოფხვრის ზარალს.
მომხმარებელთა იდემპოტენტურობა: აბონენტების მხარეს დუბლებისგან დაცვა.
ეფექტური exactly-once: მიიღწევა გარე + 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) გარიგების შაბლონი
pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys
თუ კომუნა წარმატებულია, გარედან მოვლენა გარანტირებულია. თუ აპლიკაცია დაეცემა კომიტის შემდეგ, პაბლიცერი დაიჭერს.
5) პაბლიშერი
დავალებები:- პერიოდულად წაიკითხეთ გამოუქვეყნებელი მოვლენები ('published _ at IS NULL' და 'available _ at <= now ()'), ბრძოლები.
- შეეცადეთ გამოაქვეყნოთ საბურავი/ხაზი; წარმატების მისაღწევად - აღნიშნეთ 'published _ at'.
- შეცდომით - გაზარდეთ 'attempts', დააყენეთ 'available _ at' მომავლისთვის (ექსპონენტური ბაჩოფი), დაწერეთ 'error'.
- პატივისცემა ტენიანობის/კლავიშების შესახებ, არ დაბლოკოს.
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) Idempotence და deduplication
მომხმარებლის მხარეს (Inbox/Idempotence Store):sql
CREATE TABLE inbox (
consumer_name TEXT,
event_id UUID,
processed_at TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);
ალგორითმი: ღონისძიების მიღებისას - პირველი მცდელობა 'INSERT' inbox '; თუ საკვანძო კონფლიქტი - მოვლენა უკვე დამუშავებულია „არა-op“. შემდეგი არის ბიზნეს ლოგიკა.
პაბლიცერის მხარეს: 'Idempotency-Key' headers (მაგალითად, 'event _ id') ისე, რომ საბურავები/ბროკერი/მარიონეტული შეიძლება გაფილტრული იყოს დუბლიკატებით.
7) წესრიგი და მიზეზი
ადგილობრივი ბრძანება 'agregate _ id' უზრუნველყოფილია 'occurred _ at' დახარისხებით და „გასაღების“ გამოქვეყნებით.
განაწილების მქონე ლოგის საბურავებისთვის - დააკოპირეთ 'aggregate _ id '/' tenant _ id' გასაღები ისე, რომ ერთი განყოფილების მოვლენები ერთ ნაწილად იყოს.
თუ შეკვეთა კრიტიკულია, თავიდან აიცილეთ საზოგადოების რბოლა ერთი გასაღებით.
8) CDC (Change Data Capture)
აქტიური საზოგადოების ნაცვლად, შეგიძლიათ გამოიყენოთ CDC: ძრავა კითხულობს BD გარიგების ჟურნალს და „outbox“ ხაზებს გადასცემს საბურავზე. უპირატესობები - მინიმალური დატვირთვა BD- ზე, ზუსტი თანმიმდევრობა, ნახევარგამოყოფის არარსებობა. უარყოფითი მხარეა ოპერაციის გართულება და DBMS- ის სპეციფიკის დაკავშირება. ორივე მიდგომა რეალურია; შეარჩიეთ კომპეტენციები და SLO.
9) შეცდომები, DLQ და რადარი
Retryable (ქსელი, ლიმიტები) - ჩვენ ვზრდით 'attempts- ს', გადადით 'available _ at' (exponential backoff + gitter).
Non-retryable (შეუქცევადი სქემა/კონტრაქტი) - ჩვენ გადავცემთ DLQ/Dead-Letter Topic- ს მდიდარი მეტამონაცემებით.
უსაფრთხო redrive: batch, rate-limit, სქემის შესაბამისობა, პრიორიტეტი პროდ-ტრაფიკის ქვემოთ.
10) მულტფილმები და ლიმიტები
სავალდებულო ჭდეები: 'tenant _ id', 'plan', 'region' - outbox- ში. headers`.
Per-tenant fairness: საზოგადოება ანაწილებს პუბლიკაციების „ფანჯრებს“ და იჯარის მცდელობების შეზღუდვებს.
Residence: შეინახეთ გარე ბოქსი იმავე რეგიონში, სადაც დომენის მონაცემები; რეგიონალური პუბლიკაცია - მხოლოდ დანაყოფები/მოხსენებები.
11) უსაფრთხოება და შესაბამისობა
PII გამოცემა payload/headers ტენანტის/რეგიონის პოლიტიკის შესახებ.
დატვირთვის ხელმოწერა/დაშიფვრა, თუ საბურავი „უცხოა“.
სახელმწიფო ყველა გადასვლის აუდიტი: შეიქმნა, გამოქვეყნდა შეცდომა, რედრივი.
12) დაკვირვება
მეტრიკა:- პუბლიკაციის lag ('now - occurred _ at' p50/p95/p99).
- წარმატების წილი, შეცდომების წილი, მიზეზების განაწილება.
- Outbox- ის ზომა (გამოუქვეყნებელი), მცდელობები/წმ.
- throughput და lag per- ჩრდილოვანი გრაფიკები.
- 'event _ id '/' aggregate _ id '/' saga _ id' კორელაცია; სპანი „db-tx“, „publish“, „retry“.
- მენიუ: 'attempt', 'backoff _ ms', 'dlq = ნამდვილი'.
- მოკლე წარმატების ჩანაწერები; შეცდომის/რედაქციის სრული დეტალები.
13) ტესტირება და ქაოსი
ატომიური ტესტი: ხელოვნურად „დაეცემა“ აფეთქების ღუმელის გარიგების შემდეგ, გამოქვეყნებამდე - ღონისძიება მოგვიანებით უნდა გამოვიდეს.
Duplicate ტესტი: ჩვენ რამდენჯერმე ვაქვეყნებთ ერთსა და იმავე მოვლენას - კონსიმერი ზუსტად ერთ ეფექტს ასრულებს (inbox).
რიგის ტესტი: ერთი განყოფილების მოვლენების პაკეტი - თანმიმდევრობის/idempotent- ის შემოწმება.
ქაოსი: ბროკერის უარყოფა, BD- ს ლატენტობის ზრდა, split-brain საზოგადოება, 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) ინტეგრაცია საგნებთან და რუტინებთან
Outbox - „უსაფრთხოების ტრანსპორტი“ საგის ნაბიჯებისთვის: ადგილობრივი გარიგება წერს ეფექტს და გუნდს/მოვლენას; პუბლიკაცია საიმედო და დოზირებულია.
გამეორებისა და ბაჩოფის პოლიტიკოსები უნდა შეთანხმდნენ 'Retry-After' და Circuit Breaker- თან; მოერიდეთ „ქარიშხლის ქარიშხალს“.
16) ტიპიური შეცდომები
ისინი მოვლენას წერენ აფეთქების ღუმელის კომიტის შემდეგ - დანაკარგი შესაძლებელია შემოდგომაზე.
არ არსებობს ინდექსები/არქივები „outbox“ - ში, პუბლიკაციის შეფერხების ზრდა.
Pablisher 'SKIP LOCKED' გარეშე ან შარდვის გარეშე - კონკურენცია და დაბლოკვა.
მომხმარებლებს შორის იდემპოტენტურობის არარსებობა დუბლები და გვერდითი მოვლენებია.
PII ნაზავი შენიღბვის გარეშე DLQ/ლოგებში.
პუბლიკაციის ერთიანი გლობალური ეტაპი fairness- ის გარეშე - „ხმაურიანი“ ტენანტი ყველას აფერხებს.
ლაგამის მონიტორინგის არარსებობა ფარული დეგრადაციაა.
17) სტრატეგიის სწრაფი შერჩევა
საწყისი დონე: DD- დან ნახევარი, 100-500, backoff full-jitter, inbox Consumers.
მაღალი დატვირთვა: CDC გარიგების ჟურნალიდან, შარდვა 'tenant _ id/aggregate _ id', WFQ ქირაობისთვის.
მკაცრი განყოფილება: per key (mutex) სერიული გამოცემა, ტოპიკის განლაგება გასაღებით.
შესაბამისობა/PII: payload დაშიფვრა, გამოცემა DLQ- ში, რეგიონალური გარე ბოქსი.
18) ჩეკის სია გაყიდვამდე
- დომენის ცვლილებები და ჩაწერა „Outbox“ - ში ხდება ერთ გარიგებაში.
- პაბლიცერი ამუშავებს ბუჩქებს, იყენებს 'SKIP LOCKED', backoff ერთად ჯიტერი და ლიმიტები.
- Consumers idempotent (ცხრილი 'inbox '/დედაპლატის ჟურნალი).
- DLQ და უსაფრთხო რადარი.
- lage/შეცდომების მეტრიკა და ალერტები p95/p99 ზღურბლზე.
- საკვანძო წესრიგი გარანტირებულია (წვეულებები/სერიები).
- არქივი/რეტენსია 'outbox' და გამოქვეყნებული ჩანაწერების გაწმენდა.
- PII პოლიტიკა და სახელმწიფოების გადასვლის აუდიტი.
- ტესტები კომუნასა და პუბლიკაციას შორის დაცემის, დუბლიკატებისა და წესრიგის შესახებ.
- ღონისძიების კონტრაქტების დოკუმენტაცია (სქემები/ვერსიები/თავსებადობა).
დასკვნა
Outbox პატრონი BD-Shina- ს „მყიფე“ თაიგულს საიმედო კონვეიერად აქცევს: სახელმწიფოს ატომური ფიქსაცია გარანტირებულია (თუმც „ყოველ ჯერზე ერთხელ მაინც“) პუბლიკაციაში, imptent აბონენტები და კონტროლირებადი რედაქცია. სწორი ტელემეტრიის, ლიმიტებისა და სქემების დისციპლინით, იგი იძლევა პრაქტიკულ ექსაქტილ-ერთ ქცევას, ამცირებს განაწილებული გარიგების სირთულეს და ზრდის სისტემის სტაბილურობას წარუმატებლობებისა და პიკის დატვირთვების მიმართ.