Architekt systemówArchitektura Systemów

Zaprojektuj globalnie rozproszony, niskolatencyjny silnik podejmowania decyzji o autoryzacji, który ocenia polityki kontroli dostępu oparte na relacjach (ReBAC) dla miliardów obiektów i podmiotów, utrzymując latencję oceny poniżej 10 ms w 99. percentylu dzięki rozproszonemu cache'owi, zapewniając spójną przyczynową przy kaskadowych cofnięciach uprawnień i zapobiegając zjawisku huraganowego ataku podczas burz unieważnienia cache'a bez wprowadzania wąskich gardeł centralnej koordynacji?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź na pytanie

Architektura koncentruje się na rozproszonym serwisie autoryzacyjnym inspirowanym Zanzibar, składającym się z trzech warstw: bezstanowych węzłów oceniających silnika Check Engine, globalnie rozproszonej bazy danych grafu Snapshot Database oraz reaktywnej linii Watch Pipeline do unieważnienia cache'a. Ten projekt oddziela autoryzacje, które są wymagające w odczycie, od aktualizacji uprawnień, które są wymagające w zapisie, umożliwiając niezależne skalowanie zdolności oceny, jednocześnie utrzymując silne gwarancje spójności dla mutacji relacji. System korzysta z wzorca zookie firmy Google, aby ograniczyć przestarzałość bez utraty korzyści wydajnościowych związanych z cache'em brzegowym.

Węzły Check Engine wdrażane w lokalizacjach brzegowych oceniają zapytania o autoryzację przy użyciu lokalnych pamięci podręcznych w pamięci i skompaktowanych bitmap uprawnień. Te węzły ładują konfiguracje przestrzeni nazw z replikowanego klastra etcd i rozwiązują krotek relacji z geo-podzielonej instancji CockroachDB lub Spanner, która zapewnia zewnętrzną spójność dzięki TrueTime lub hybrydowym zegarom logicznym. Każdy węzeł utrzymuje Bloom filtry, aby zapobiegać nieudanym próbom cache'a uderzającym w bazę danych, gdy relacje zdecydowanie nie istnieją.

Aby poradzić sobie z „problemem nowego wroga”, gdzie niedawne cofnięcia mogą być niewidoczne dla cache'y brzegowych, system wdraża tokeny zookie — nieprzezroczyste znaczniki czasowe kodujące spójność zrzutu — które wymuszają nieudane próby cache'a dla wrażliwych operacji w konfigurowalnym oknie niepewności. Klienci otrzymują te tokeny z początkowymi sprawdzeniami i muszą je odtworzyć przy dostępie do zasobów o wysokiej wartości, zapewniając, że niedawno cofnięte uprawnienia są natychmiast widoczne bez konieczności globalnego unieważnienia cache'a. Ten mechanizm równoważy potrzebę niskiej latencji z wymaganiem bezpieczeństwa polegającym na natychmiastowej widoczności cofnięcia.

Unieważnienie cache'a wykorzystuje linię Watch Pipeline wspieraną przez Apache Kafka, która propaguje zmiany krotek do wszystkich węzłów Check Engine na brzegu, wykorzystując spójną haszowanie, zapewniając, że burze cofnięć uruchamiają stopniowe odświeżenia cache'a, a nie synchronizowaną bombardowanie bazy danych. Linii korzysta z opóźnienia z jitterem, aby zapobiegać huraganowym atakom, gdy szeroko udostępnione obiekty doświadczają zmian uprawnień. Ta architektura zapewnia, że system utrzymuje latencję poniżej 10 ms dla trafień w cache'u, zapewniając jednocześnie spójną przyczynową dla aktualizacji uprawnień na geograficznie rozproszonych węzłach.

Sytuacja z życia wzięta

Globalna platforma współpracy dokumentów, obsługująca 50 milionów użytkowników przedsiębiorstw, doświadczyła katastrofalnych skoków latencji w godzinach szczytu podczas oceniania skomplikowanych hierarchii udostępniania. Każdy dostęp do dokumentu wymagał przechodzenia przez zagnieżdżone członkostwa grup i dziedziczone uprawnienia przechowywane w monolitycznym klastrze PostgreSQL, co skutkowało czasem zapytania powyżej 500 ms i częstymi przekroczeniami czasowymi podczas masowych aktualizacji uprawnień, gdy pracownicy zmieniali działy lub projekty. Zespół inżynieryjny potrzebował rozwiązania, które mogłoby zapewnić latencję poniżej 10 ms, jednocześnie utrzymując ścisłe gwarancje bezpieczeństwa podczas kaskadowych cofnięć w zagnieżdżonych strukturach folderów.

Pierwsze podejście oceniało utrzymanie scentralizowanego klastra PostgreSQL z agresywnym szczegółowym podręcznym cache'em dla ścieżek uprawnień. Zalety obejmowały silne gwarancje ACID, zapewniające natychmiastową widoczność cofnięć oraz zrozumiałą semantykę transakcji dla skomplikowanych aktualizacji wielotabelowych. Wady obejmowały poważne wąskie gardła zapisu podczas masowych zmian uprawnień, nieuniknione ryzyka lotów cache'u, gdy popularne dokumenty były aktualizowane, oraz fundamentalną niezdolność do skalowania przepustowości odczytu geograficznie bez skomplikowanych replik odczytu, które wprowadzały nieakceptowalne opóźnienia replikacji dla decyzji krytycznych z punktu widzenia bezpieczeństwa.

Drugie podejście sugerowało całkowicie denormalizowaną implementację Apache Cassandra z rozwiązywaniem uprawnień po stronie aplikacji i wygasaniem cache'a opartym na TTL. Zalety obejmowały doskonałą przepustowość zapisu dla mutacji relacji i wbudowaną dostępność wieloregionową bez pojedynczych punktów awarii. Wady ujawniały nieakceptowalne kompromisy dotyczące ostatecznej spójności, w których cofnięte uprawnienia pozostawały widoczne przez minuty z powodu opóźnień protokołu plotek, a brak atomowych usunięć kaskadowych tworzył luki w bezpieczeństwie, gdzie użytkownicy utrzymywali dostęp do zasobów po usunięciu z grup nadrzędnych, naruszając zasadę najmniejszego przywileju.

Zespół ostatecznie wybrał architekturę w stylu Zanzibar, wykorzystując CockroachDB do przechowywania krotek relacji, boczne procesory Envoy jako punkty egzekucji polityki i poziomą skalę węzłów Check Engine z cache'ami Least Recently Used wspieranymi przez Bloom filtry. Ten wybór równoważył potrzebę silnej spójności w zapisie uprawnień za pomocą domyślnej izolacji serializowalnej z wymaganiami wydajnościowymi na brzegu poprzez lokalne cache'e oceny i strumienie unieważnienia napędzane przez Apache Kafka. Efekt zmniejszył latencję autoryzacji p99 z 500 ms do 4 ms, obsługując 15 milionów sprawdzeń na sekundę globalnie i zapewniając, że cofnięcia uprawnień propagowały się do wszystkich węzłów brzegowych w ciągu 150 milisekund, jednocześnie utrzymując dostępność na poziomie 99,99%.

Czego często brakuje kandydatom

Jak zapobiegać zwracaniu przestarzałych decyzji „zezwól” bezpośrednio po cofnięciu uprawnienia bez rezygnacji z korzyści wydajnościowych rozproszonego cache'a?

Kandydaci często pomijają wzorzec zookie lub wektory wersji, zamiast tego proponując globalne unieważnienie cache'a lub odczyty z bazy danych dla każdego sprawdzenia. Rozwiązanie wymaga, aby usługa autoryzacyjna zwracała token spójności z każdą decyzją, który koduje znacznik czasowy zrzutu używanych danych. Dla wrażliwych operacji lub po niedawnych zdarzeniach cofnięcia, klient musi przedstawić ten token, wymuszając Węzeł Check Engine na weryfikację w centralnym magazynie, jeśli jego lokalny cache jest wcześniejszy niż znacznik czasowy tokena. Gwarantuje to spójną przyczynową bez potrzeby globalnego unieważnienia cache'a lub rezygnacji z wydajności odczytu dla większości żądań.

Jak zaprojektowałbyś mechanizm unieważnienia cache'a, aby uniknąć efektów huraganowego ataku, gdy udostępniony obiekt ma zmienione uprawnienia, co potencjalnie wywołuje miliony równoczesnych odświeżeń cache'a?

Kluczowa technika polega na spójnym haszowaniu kluczy cache'a w połączeniu z opóźnieniem jitterowanym i koalescencją żądań w węzłach brzegowych. Kiedy linia Watch Pipeline nadaje zmianę krotki, węzły brzegowe nie unieważniają od razu, lecz zamiast tego planują unieważnienie przy użyciu hasza identyfikatora obiektu, aby rozłożyć odświeżenia na okno czasowe. Dodatkowo każdy Węzeł Check Engine utrzymuje grupę lotów dla bieżących sprawdzeń, zapewniając, że równoczesne żądania dla tego samego obiektu dzielą jeden wynik zapytania zaplecza, zapobiegając przeciążeniu bazy danych podczas aktualizacji popularnych obiektów.

Dlaczego użycie prostej traversji grafu jest niewystarczające do modelowania polityk ReBAC, a jak radzisz sobie z ograniczeniami przecięcia i wykluczenia w rozproszonej środowisku oceny?

Prosta traversja grafu nie uchwyca operacji zbiorowych potrzebnych dla zaawansowanych polityk, takich jak „zezwól tylko wtedy, gdy użytkownik jest w grupie A I NIE w grupie B”. Rozwiązanie implementuje system przepisywania, w którym konfiguracje przestrzeni nazw kompilują się do drzew decyzyjnych ocenianych za pomocą odwróconych indeksów, przechowując zarówno dodatnie, jak i ujemne krotki relacji w sposób jawny. Dla ograniczeń przecięcia system zapytuje oba zestawy i oblicza przecięcie w Węźle Check Engine, podczas gdy wyłączenia korzystają z oceny przerywanej z wcześniejszym zakończeniem. To podejście zapewnia, że złożona logika boole'a efektywnie ocenia się bez potrzeby wielokrotnego krążenia do bazy danych.