Dokładnie raz semantyka
Co dokładnie-raz naprawdę jest
„Dokładnie raz” jest często rozumiane jako dwie różne rzeczy:- Dostawa: Wiadomość zostanie dostarczona konsumentowi dokładnie raz.
- Przetwarzanie: ostateczny efekt uboczny (pisanie do bazy danych, zmiana salda, wydanie innego zdarzenia) nastąpi dokładnie raz, nawet jeśli będzie więcej dostaw lub prób.
W systemach rozproszonych bardziej niezawodne jest mówienie o semantyce przetwarzania. Dostawa dokładnie raz jest trudna (duplikaty i replikaty są nieuniknione), ale powstały stan może być równoważny pojedynczej obróbce.
Kiedy EOS jest potrzebny, a kiedy nie
EOS wymagany, jeżeli:- Transakcje gotówkowe i salda: Podwójne odpisy nie są dozwolone.
- Licencja/rozliczanie kwot, liczniki rozliczeniowe.
- Nieodwracalne połączenia zewnętrzne (na przykład jednorazowa aktywacja klucza).
- Efekty są odwracalne lub kompensowalne (sagi, zwroty).
- Tymczasowe duplikaty są dozwolone w sklepach/dziennikach.
- Tańsze jest zapewnienie idempotentnego zlewozmywaka niż przeciąganie transakcji przez całą ścieżkę.
Model: end-to-end vs. hop-by-hop
Hop-by-hop EOS: każda sekcja (źródło → procesor → odbiornik) gwarantuje, że będzie stosować swoje działanie dokładnie raz.
End-to-end EOS: Cały łańcuch zapewnia, że od „faktu” do „efektu ubocznego” wynik jest równoważny pojedynczej obróbce.
W praktyce koniec-koniec osiąga się poprzez połączenie transakcji i/lub idempotencji na każdym chmurze.
Podstawowe elementy konstrukcyjne
1. Operacje idempotentne
Powtarzanie tego samego zapytania na kluczu operacyjnym daje ten sam wynik.
Клика: 'idempotency _ key '/' event _ id'/' operation _ id'.
Implementacja: tabela operacji „widzianych” z TTL ≥ prezentacja dziennika wejściowego.
2. Transakcje odczytu i zapisu
W jednej atomowej jednostce pracy rejestruje się zarówno efekt uboczny, jak i postęp odczytu (przesunięcia/położenie). Eliminuje to „duchy” podczas spadania między etapami.
3. Wersioning/SEKWENCJA
Dla agregatu przechowywana jest wersja/licznik; zmiany mają zastosowanie tylko w przypadku dopasowania 'expected _ version'. Powtórzenia tego samego zdarzenia nie zwiększają wersji → efekt raz.
4. Deduplikowanie
Indeks na '(consumer_id, event_id)' lub na naturalnym 'business _ id' transakcji.
Schematy realizacji
1) Dziennik transakcyjny + zlewozmywak transakcyjny z zamocowaniem offsetowym
Idealny do przetwarzania strumienia.
Czytamy z dziennika (tylko potwierdzone wpisy).
Przeprowadzamy przetwarzanie.
- a) zapisać efekt w zlewie (baza danych/tabela),
- b) naprawić "read to offset N' (w tej samej bazie danych).
- Popełnić. Przy ponownym uruchomieniu, albo wszystko jest zdyskontowane (a przesunięcie jest przesunięte), albo nic.
Właściwości: powtarzanie wykonania nie jest szkodliwe; „dokładnie raz” w rzeczywistości, nawet jeśli wiadomość była czytana dwa razy.
2) Outbox + consumer idempotent
Dla usług producentów transakcyjnych.
W jednej transakcji bazodanowej: zmień rekord domeny i zapisz zdarzenie do skrzynki odbiorczej.
Repeater dostarcza zdarzenie do autobusu z tym samym 'event _ id'.
Konsumenci stosują zdarzenia idempotently (dedup by 'event _ id').
Właściwości: Producent zapewnia, że żaden fakt nie zostanie utracony; konsumenci gwarantują dokładnie jeden efekt.
3) EOS w systemach Kafka/Flink-like (koncepcyjnie)
Idempotent producent: chroni przed braniem na wysyłanie rekolekcje.
Transakcje producentów: grupa wpisów w tematach + zmiana konsumpcji są dokonywane atomowo; czytniki używają izolacji 'read _ committed'.
Strona po stronie przetwarzania przechowuje sklep stanowy i zobowiązuje go wraz z transakcją.
Właściwości: Ponowne uruchomienie sklepu/przeciągnij nie prowadzi do podwójnego efektu; duplikaty „niewidoczne”.
4) Idempotentne „zlewy” poprzez upsert/fuzja
Sink bierze 'operation _ id'/' event _ id' i wykonuje' UPSERT... TAM, GDZIE NIE ISTNIEJE ".
Efekt uboczny (na przykład memoriałowy) jest wykonywany atomowo z kontrolą „nie został jeszcze zastosowany”.
Właściwości: tania metoda EOS na krawędzi z pamięcią masową, bez transakcji rozproszonych.
Kluczowe szczegóły realizacji
Identyfikatory transakcji
Musi być deterministyczny dla powtórzeń (nie generować nowego UUID podczas cofania).
Mieć stabilny zakres (na konsument/jednostka/system).
Tabela deduplikacji
Колонка: 'consumer _ id',' operation _ id', 'applied _ at', 'ttl _ expires _ at'.
Indeksy na „(consumer_id, operation_id)”.
TTL ≥ maksymalne okno powtarzania (zatrzymanie dziennika + potencjalne opóźnienia).
Optymistyczna konkurencja
W modelu zapisu zapisz wersję urządzenia.
Przy stosowaniu zdarzenia/polecenia należy użyć 'WHERE version =: expected'; duplikat nie zwiększy wersji.
Zamówienie/Zamówienie
EOS nie jest "dokładnie tą samą kolejnością. "Zapewnij spójność przez klucz partii (wszystkie zdarzenia zagregowane → jedna partia) i/lub porównanie" równorzędności ".
Idempotentne połączenia zewnętrzne
W przypadku niepewnych metod (na przykład haki internetowe HTTP do usługi strony trzeciej) dodaj 'Idempotence-Key' i wymagaj od partnera wsparcia.
Częste pułapki
EOS w jednym miejscu: jeśli zlew jest idempotentny, ale emitujesz zdarzenia wtórne bez idempotencji, dostaniesz „dokładnie wiele razy” w dół strumienia.
Dwa commits: najpierw w bazie danych, potem w maklerze commit offset - upadek pomiędzy nimi tworzy duplikaty efektów.
Surowe płyty CDC: Zmiana systemu DB łamie dempotencję konsumentów.
Klucze niestabilne: 'operation _ id' zależy od czasu/losowości i zmian podczas przekaźnika.
Koszty i kompromisy
Opóźnienie: transakcje/odizolowane odczytuje → wzrost p95/p99.
Przechowywanie napowietrzne: tabele deduplikacji, sklepy stanowe, dzienniki transakcji.
Złożoność operacyjna: timeouts transakcji, rebalance nici, utknęły sesje.
Diagnostyka: więcej stanów („in kamit”, „widoczne jako read_committed,” „zwinięte z powrotem”).
Wybierz punkt EOS: dla krytycznych agregatów i skutków; resztę pokryć idempotencją i rekompensatą.
Badanie dokładnie raz
1. Zastrzyk błędu: spadek procesu pomiędzy etapami „zarejestrował efekt” i „zarejestrował przesunięcie”.
2. Duplikaty: pobierz tę samą wiadomość 2-5 razy, upewnij się o jednym efekcie.
3. Ponowne uruchomienie i przywrócenie równowagi: zatrzymanie/ponowne uruchomienie pracowników, sprawdzenie braku podwójnego przetwarzania.
4. Klapki sieciowe: średnie czasy transakcji, powtórne próbowanie.
5. Testy obciążenia: wzrost kolejki → czy nie ma degradacji do „na zawsze w transakcji”.
Mini szablony (pseudo)
Idempotent zlewozmywak z zamocowaniem offsetowym
pseudo begin tx if not exists(select 1 from dedup where consumer_id=:c and op_id=:id)
then apply_effect(...) -- upsert / merge / add_one_time_action insert into dedup(c, id, applied_at) values(:c,:id, now)
end if update offsets set pos=:pos where consumer_id=:c commit
Polecenie z wersją jednostkową
pseudo begin tx update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
if row_count=0 then error CONCURRENT_MODIFICATION commit
Bezpieczeństwo i zgodność
PII/PCI w tabelach deduplikacji: przechowywać minimum, używać żetonów zamiast danych surowych.
Audyt: log 'operation _ id',' trace _ id', wynik (APPLIED/ALREADY_APPLIED).
Zasady przechowywania: TTL na stołach dedup, archiwizacja przesunięć/dzienników.
Anty-wzory
„Rzeczywista dostawa dokładnie raz”: próba wykluczenia duplikatów na poziomie protokołu transportowego bez efektu iempotencji.
Globalne transakcje rozproszone dla wszystkiego: XA/2PC za pośrednictwem wszystkich usług jest kruche i powolne.
Mieszanie nie-idempotentnych skutków ubocznych (na przykład, e-mail wysłany przed popełnieniem offsetu).
Brak kluczy operacyjnych: poleganie na „wyjątkowości” ładunku.
Lista kontrolna produkcji
- Każdy wpływ krytyczny ma klucz idempotentny.
- Pozycja offset/read jest ustalona w jednej transakcji ze skutkiem.
- Indeksowane tabele dedublacji; TTL ≥ zatrzymanie kłód.
- Optymistyczna konkurencja (wersja/sekwencja) jest włączona dla agregatów.
- Wątki/Tematy są odczytywane w trybie „Comp Only” (jeśli jest dostępny).
- Duplikat i drop testów są obecne w CI/CD.
- Deski rozdzielcze: udział powtórzeń, nieudane transakcje, czas blokowania, opóźnienia.
- Dokumentacja integratora dla 'Idempotency-Key '/retries/timeouts.
NAJCZĘŚCIEJ ZADAWANE PYTANIA
Czy EOS można zapewnić bez transakcji?
Często tak - poprzez idempotencję zlewu (upsert/fuzja) i wersioning agregatów. Transakcje upraszczają gwarancje, ale zwiększają koszty.
Czy każdy potrzebuje dokładnie raz?
Nie, nie jest. To jest drogie. Zastosuj punkt widzenia, w którym rekompensata nie jest możliwa/kosztowna.
Jak skojarzyć listy/haki internetowe z EOS?
Bufor powiadomienia przed zatwierdzeniem, wyślij po ustaleniu efektu; przechowywać 'notification _ id' i czynić wysyłanie idempotentem.
Co jest ważniejsze - dostawa lub przetwarzanie?
Przetwarzanie. Dostawy mogą być powtarzane; stan końcowy musi być poprawny i jedyny.
Wynik
Dokładnie raz chodzi o poprawność efektu, a nie o brak duplikatów w okablowaniu. Osiąga się go poprzez połączenie idempotencji, atomowego utrwalenia efektu i postępu czytania, rozsądnego podziału i dyscypliny wersioning. Zastosuj system EOS, w którym koszt błędu jest niedopuszczalny, i sprawdź jego rzeczywistość z upadkami i podejmuje testy - brak wiary w transport.