L'architettura si basa su un servizio di autorizzazione ispirato a Zanzibar distribuito in tre livelli: nodi di valutazione del Check Engine senza stato, un database Snapshot distribuito a livello globale e una pipeline Watch basata su eventi per l'invalidazione della cache. Questo design separa i controlli di autorizzazione ad alta lettura dagli aggiornamenti di permessi ad alta scrittura, consentendo una scalabilità indipendente della capacità di valutazione mantenendo forti garanzie di coerenza per le mutazioni delle relazioni. Il sistema impiega il pattern zookie di Google per limitare la staleness senza compromettere i benefici delle prestazioni della cache edge.
I nodi del Check Engine distribuiti in posizioni edge valutano le query di autorizzazione utilizzando cache locali in memoria e bitmap di permessi compatti. Questi nodi caricano le configurazioni di namespace da un cluster replicato di etcd e risolvono le tuple delle relazioni da un'istanza geo-partizionata di CockroachDB o Spanner, che fornisce coerenza esterna tramite TrueTime o orologi logici ibridi. Ogni nodo mantiene Bloom filters per prevenire i cache misses che colpiscono il database quando le relazioni non esistono sicuramente.
Per gestire il "problema del nuovo nemico" in cui le revoche recenti potrebbero essere invisibili alle cache edge, il sistema implementa token zookie: timestamp opachi che codificano la coerenza degli snapshot e che forzano i cache misses per operazioni sensibili all'interno di una finestra di incertezza configurabile. I client ricevono questi token con i controlli iniziali e devono riprodurli quando accedono a risorse di alto valore, garantendo che i permessi recentemente revocati siano immediatamente visibili senza richiedere un'invalidazione globale della cache. Questo meccanismo bilancia la necessità di bassa latenza con il requisito di sicurezza dell'immediata visibilità della revoca.
L'invalidazione della cache sfrutta una pipeline Watch supportata da Apache Kafka che propaga le modifiche delle tuple a tutti i Check Engine edge utilizzando il hashing coerente, garantendo che le tempeste di revoca attivino aggiornamenti della cache sfalsati piuttosto che bombardamenti sincronizzati del database. La pipeline impiega un backoff esponenziale jitterizzato per prevenire le cariche di mandrie quando gli oggetti ampiamente condivisi subiscono modifiche ai permessi. Questa architettura garantisce che il sistema mantenga una latenza inferiore a 10 ms per i cache hits garantendo coerenza causale per gli aggiornamenti dei permessi attraverso nodi distribuiti geograficamente.
Una piattaforma globale di collaborazione documentale che serve 50 milioni di utenti aziendali ha subito picchi di latenza catastrofici durante le ore di punta quando valutava gerarchie di condivisione complesse. Ogni accesso ai documenti richiedeva l'attraversamento di appartenenze a gruppi annidati e permessi ereditati memorizzati in un cluster monolitico di PostgreSQL, risultando in tempi di query superiori a 500 ms e frequenti timeout durante gli aggiornamenti in blocco dei permessi quando i dipendenti cambiavano dipartimenti o progetti. Il team di ingegneria richiedeva una soluzione capace di una latenza inferiore a 10 ms mantenendo garanzie di sicurezza rigorose durante le revoche a cascata attraverso strutture di cartelle annidate.
Il primo approccio ha previsto il mantenimento di un cluster centralizzato di PostgreSQL con viste materializzate aggressive di Redis che memorizzano in cache i percorsi di permesso. I pro includevano solide garanzie di ACID che assicurano visibilità immediata delle revoche e una semantica di transazione semplice per aggiornamenti complessi multi-tabella. I contro hanno comportato gravi colli di bottiglia in scrittura durante modifiche di permesso in blocco, inevitabili rischi di tempesta della cache quando documenti popolari venivano aggiornati e l'incapacità fondamentale di scalare il throughput di lettura geograficamente senza complesse repliche di lettura che introducevano un ritardo di replicazione inaccettabile per decisioni critiche per la sicurezza.
Il secondo approccio ha suggerito un'implementazione completamente denormalizzata di Apache Cassandra con risoluzione dei permessi lato applicazione e scadenza della cache basata su TTL. I pro hanno incluso un ottimo throughput di scrittura per le mutazioni delle relazioni e disponibilità multi-regione intrinseca senza punti singoli di guasto. I contro hanno rivelato inaccettabili compromessi di coerenza eventuale in cui i permessi revocati rimanevano visibili per minuti a causa dei ritardi del protocollo gossip, e l'assenza di eliminazioni a cascata atomiche creava falle di sicurezza in cui gli utenti mantenevano accesso a risorse dopo essere stati rimossi da gruppi genitori, violando il principio della minima privazione.
Il team ha infine selezionato un'architettura in stile Zanzibar utilizzando CockroachDB per la memorizzazione delle tuple di relazione, i sidecar Envoy come punti di enforcement della policy e nodi del Check Engine scalati orizzontalmente con cache Least Recently Used supportate da Bloom filters. Questa scelta ha bilanciato la necessità di una forte coerenza nelle scritture di permessi tramite isolamento di default serializzabile con i requisiti di prestazione edge attraverso cache di valutazione locali e flussi di invalidazione basati su Apache Kafka. Il risultato ha ridotto la latenza di autorizzazione p99 da 500 ms a 4 ms, supportato 15 milioni di controlli al secondo a livello globale e garantito che le revoche dei permessi si propagassero a tutti i nodi edge entro 150 millisecondi mantenendo il 99,99% di disponibilità.
Come prevenite che i controlli di autorizzazione restituiscano decisioni "consenti" stantii immediatamente dopo una revoca di permesso senza sacrificare i vantaggi delle prestazioni della cache distribuita?
I candidati trascurano spesso il pattern zookie o i vettori di versione, proponendo invece l'invalidazione globale della cache o letture del database per ogni controllo. La soluzione richiede che il servizio di autorizzazione restituisca un token di coerenza con ogni decisione che codifica il timestamp dello snapshot dei dati utilizzati. Per operazioni sensibili o dopo eventi di revoca recenti, il client deve presentare questo token, costringendo il Check Engine a verificare contro il negozio centrale se la sua cache locale precede il timestamp del token. Questo garantisce coerenza causale senza richiedere invalidazione globale della cache o sacrificare le prestazioni di lettura per la maggior parte delle richieste.
Come architettereste il meccanismo di invalidazione della cache per evitare effetti di mandria rovente quando un oggetto ampiamente condiviso ha i suoi permessi modificati, potenzialmente attivando milioni di aggiornamenti della cache simultanei?
La tecnica chiave coinvolge il hashing coerente delle chiavi della cache combinato con backoff esponenziale jitterizzato e coalescenza delle richieste nei nodi edge. Quando la pipeline Watch trasmette un cambiamento della tupla, i nodi edge non invalidano immediatamente ma programmando invece l'invalidazione utilizzando un hash dell'ID dell'oggetto per diffondere gli aggiornamenti nel tempo. Inoltre, ogni Check Engine mantiene un gruppo di volo per le verifiche in corso, assicurandosi che le richieste concorrenti per lo stesso oggetto condividano un unico risultato della query backend, prevenendo il sovraccarico del database durante gli aggiornamenti degli oggetti popolari.
Perché utilizzare una semplice traversata di grafi diretti è insufficiente per modellare le politiche ReBAC e come gestite i vincoli di intersezione ed esclusione in un ambiente di valutazione distribuito?
La semplice traversata del grafo non riesce a catturare le operazioni insieme necessarie per politiche sofisticate come "consenti solo se l'utente è nel gruppo A E NON nel gruppo B". La soluzione implementa un sistema di riscrittura in cui le configurazioni del namespace si compilano in alberi decisionali valutati tramite ricerche di indice inverso, memorizzando esplicitamente sia le tuple di relazione positive che negative. Per i vincoli di intersezione, il sistema interroga entrambi i set e calcola l'intersezione nel Check Engine, mentre le esclusioni impiegano una valutazione a circuito corto con terminazione anticipata. Questo approccio garantisce che la logica booleana complessa venga valutata in modo efficiente senza richiedere più viaggi al database.