Przejdź do głównej zawartości

Zdarzenia domenowe

Zdarzenia domenowe to mechanizm rozluźniania zależności, który pozwala funkcjom komunikować się bez bezpośrednich zależności. Zamiast tego, by funkcja A wywoływała funkcję B, funkcja A publikuje zdarzenie, a funkcja B je subskrybuje.

DomainEventBus to wewnątrzprocesowa szyna publikowania i subskrypcji z rozgłaszaniem. Wydawcy wywołują publish(event); subskrybenci konsumują typowany strumień on<T>(). Każde zdarzenie implementuje DomainEvent i niesie occurredAt.

Szyna działa wewnątrz cc_server. Każdy wydawca i każdy żywy subskrybent mieszka tam. Żaden klient nie widzi tej szyny. Klient widzi wyselekcjonowany podzbiór, który serwer ponownie emituje jako ramki JSON-RPC notifications/*.

Bez zdarzeń każda reakcja między funkcjami to import. Ścieżka powiadomień musiałaby importować funkcję agentów, funkcję pull requestów, zgłoszenia i spotkania tylko po to, by wiedzieć, kiedy wyświetlić wyskakujące powiadomienie — a każda z nich musiałaby wtedy wiedzieć, że powiadomienia istnieją. Zgłoszenia importowałyby potoki, aby ukończone zgłoszenie mogło pchnąć uruchomienie do przodu. Graf zależności zamyka się w supeł, a wspólny kernel przestaje być wspólny.

Publikowanie zdarzenia to przerywa. Ścieżka powiadomień subskrybuje dwadzieścia jeden klas zdarzeń i nie importuje żadnej z funkcji, które je wywołują. Dyspozytor wyzwalaczy potoków subskrybuje całą szynę i nie wie nic o pull requestach, zgłoszeniach ani spotkaniach. Agregacje obserwowalności podsumowują wyniki uruchomień, nie sięgając do modułu wysyłania.

Koszt jest ten zwykły: wydawca nie może stwierdzić, czy ktokolwiek słucha, a słuchacz, który nigdy nie zostaje utworzony, pozostaje po cichu nieaktywny. TicketRemoteSyncHandler to stały przykład: jest zadeklarowany, objęty testami i nigdy nie zostaje podłączony na produkcji.

Warto doprecyzować jedną rzecz, której szyna nie obejmuje.

Ścieżka audytu nie jest projekcją strumienia zdarzeń. Każda mutująca operacja RPC jest domyślnie audytowana — RepoOp.audited ma domyślnie wartość true, a operacja rezygnuje z audytu tylko przez zadeklarowanie audited: false, co jest świadomym działaniem widocznym na samej operacji. Dyspozytor dopisuje wiersz „kto co zrobił, skąd” bezpośrednio po udanym wywołaniu. Nie istnieje DomainEventAuditBridge. ActivityLogPersister zapisuje wiersze activity_log ze zdarzeń ActivityLogged; to osobny kanał niż audyt RPC.

Szyna zdarzeń jest transportem dla powiadomień i dla automatyzacji. Nie jest jednak systemem ewidencji będącym źródłem prawdy.

  • Powiadomienia. Serwer zamienia dwadzieścia jeden klas zdarzeń na ramki transmisyjne notifications/*, zapisuje je w trwałym kanale danego obszaru roboczego i rozgłasza do połączonych sesji. Ramka, której workspace_id wskazuje obszar roboczy, którego użytkownik sesji nie jest członkiem, jest odrzucana na serwerze; klient filtruje potem resztę do obszaru roboczego, który przegląda.
  • Automatyzacja sterowana zdarzeniami. Wyzwalacze potoków subskrybują szynę i automatycznie uruchamiają pasujące potoki (patrz niżej).
  • Reakcje cyklu życia. Utworzenie obszaru roboczego zasiewa jego agenta CEO i agentów specjalistycznych, wbudowane szablony potoków oraz startowe zestawy ewaluacji. Ukończone uruchomienie agenta wznawia wstrzymany krok potoku, zamyka zadanie, zasila nadzorcę celu, może obudzić nazwanego kontrolera i może ponownie wyzwolić lidera zespołu. Scalony PR lub usunięta przestrzeń wyzwala odśmiecanie drzew roboczych.
  • Synchronizacja między dostawcami. Lokalne zmiany w zgłoszeniach są wypychane do skonfigurowanych dostawców przez koordynatora nasłuchującego pięciu zdarzeń zgłoszeń.

Zdarzenia obejmują całą powierzchnię produktu. W ogólnym ujęciu:

  • Obszary robocze, agenci i repozytoria — utworzenie obszaru roboczego, ukończenie uruchomienia, rejestracja repozytorium, aktualizacje skilli
  • Pull requesty — publikowanie, zmiany statusu, scalanie, prośby o recenzję, wzmianki, obserwacja autorskich PR-ów, PR-y wykryte z zewnątrz
  • Wiadomości — wiadomości, przestrzenie i postęp provisionowania przestrzeni
  • Zgłoszenia — tworzenie, przypisywanie, ponowne przypisywanie, szczegóły, status i trzy końcowe wyniki
  • Zadania — sekwencjonowane ramki cyklu życia dla jednego wysłanego uruchomienia, dzięki czemu klienci mogą je porządkować i deduplikować
  • Potoki — tylko końcowe zdarzenia uruchomienia (ukończone, anulowane, nieudane)
  • Obserwowalność — wpisy dziennika aktywności i progi budżetu
  • Tożsamość i członkostwo — dodanie członka, usunięcie, zmiana roli
  • Kalendarz i spotkania — wygaśnięcie uwierzytelnienia, rozpoczynanie spotkań i kończenie nagrań
  • Rigi — przejęcie, odzyskanie i nieoczekiwane zamknięcie

Pamięć, orkiestracja, dokumenty planów, artefakty, zatwierdzenia, przyjmowanie synchronizacji zgłoszeń, odświeżanie kalendarza, scalanie drzew roboczych, tworzenie użytkowników, zaproszenia i unieważnianie urządzeń nie są zdarzeniami domenowymi; zobacz dokumentację referencyjną.

Pełny katalog — wszystkie 53 konkretne klasy zdarzeń, ich ładunki i 24 subskrybentów, którzy je konsumują — znajdziesz w dokumentacji referencyjnej zdarzeń domenowych. Tamta strona to inwentarz; ta jest modelem.

Kontrola dostępu na żywo opiera się na dwóch różnych mechanizmach

Dział zatytułowany „Kontrola dostępu na żywo opiera się na dwóch różnych mechanizmach”

Zdarzenia członkostwa i urządzeń wyglądają symetrycznie, ale takie nie są, co ma znaczenie, jeśli rozważasz, jak szybko działa unieważnienie.

Unieważnienie urządzenia kończy jego sesje. Serwer obserwuje tabelę sparowanych urządzeń i w ciągu kilku sekund odrzuca każdą żywą sesję, której urządzenie opuściło aktywny zbiór.

Usunięcie członka nie zamyka gniazda. WorkspaceMemberRemoved jest konsumowane: LocalRpcServer i RemoteRelayHost odrzucają subskrypcje obszarów roboczych przypisane do tego użytkownika, a RemoteEventForwarder unieważnia swój cache członkostwa, dzięki czemu dalsze ramki dla tego obszaru roboczego nie są wypychane. Tym, co odmawia następnego RPC, wciąż jest brama ról, która ponownie ustala członkostwo przy każdym wywołaniu — dlatego ich następne żądanie zostaje odrzucone komunikatem „Not a member of this workspace”. Efekt jest natychmiastowy dla wszystkiego, co próbują zrobić; nie osiąga się go jednak przez rozłączenie ich.

Wyzwalacze potoków subskrybują zdarzenia domenowe i automatycznie uruchamiają pasujące potoki. PipelineTriggerDispatcher nasłuchuje całej szyny, a następnie:

  1. Przerywa działanie, chyba że typ zdarzenia należy do tych, które umie zmapować na ładunek
  2. Wyszukuje włączone wyzwalacze pasujące do tego typu zdarzenia, we wszystkich obszarach roboczych
  3. Filtruje każdy z nich do jego własnego obszaru roboczego i stosuje filtr dopasowania ładunku
  4. Rozpoczyna uruchomienie dla każdego wyzwalacza, który to przetrwa

Najważniejszym ograniczeniem jest krok 1. Szyna niesie 53 typy zdarzeń; selektor dodawania wyzwalacza rozpoczyna uruchomienie dla tych czternastu (gałęzie ładunku EventPayloadMapper): ExternalPrDetected, PullRequestPublished, PullRequestStatusChanged, PrMerged, MessageReceived, TicketAssigned, TicketCompleted, TicketFailed, TicketCancelled, BudgetThresholdCrossed, RepoAdded, MeetingRecordingStopped, SkillUpdated oraz SpaceDeleted. Nic innego nie może uruchomić potoku.

RemoteEventForwarder tłumaczy zdarzenia klasy powiadomień na ramki transmisyjne i wypycha je przez RPC do połączonych sesji, gdzie maper ramek zamienia je na powiadomienia w aplikacji. Cienki klient widzi więc projekcję strumienia zdarzeń serwera, nie odpowiadając za żadne wykonanie — i na tym właśnie polega podział na cienkiego klienta.

Nie każda wypchnięta ramka jest jednak powiadomieniem. Strumień cyklu życia zadań i ponowne przypisywanie zgłoszeń są przekazywane na żywo, by sterować interfejsem, ale nie są zapisywane w kanale powiadomień i nie pojawiają się jako wyskakujące powiadomienia.