DIEGO CECATO / NOTE DAL CAMPO

Habitat: isolare tutto non scala, condividere tutto nemmeno

diego cecato//
Habitat: isolare tutto non scala, condividere tutto nemmeno — cover geometrica Diego Cecato

C’è un modo piuttosto comodo di raccontare le architetture software: si prende la decisione finale, la si presenta come inevitabile e si finge che il percorso precedente fosse una lunga preparazione alla soluzione giusta. Poi arriva un sistema reale, cresce oltre le previsioni e rovina la narrazione. Habitat, la piattaforma di storage applicativo di OpenAI, ha fatto esattamente questo: è partita da un container condiviso su Azure Cosmos DB, ha spostato molti carichi su container dedicati per proteggersi dagli incidenti e, dopo aver pagato il conto operativo di quella scelta, è tornata a usare la condivisione come impostazione predefinita.

Nella seconda parte del resoconto tecnico pubblicato il 23 ottobre 2026, OpenAI descrive una piattaforma che serve prodotti utilizzati da oltre un miliardo di persone, con più di 500 petabyte di dati distribuiti su decine di regioni. Sono numeri dichiarati dall’azienda, non misurazioni indipendenti. Ma la parte interessante non è il numero di zeri: è il motivo per cui due scelte apparentemente opposte si sono rivelate, in momenti diversi, entrambe ragionevoli.

Il pendolo tra condividere e isolare

Un sistema multi-tenant concentra carichi diversi su risorse comuni. È efficiente perché assorbe meglio le oscillazioni: il servizio che cresce oggi può usare capacità non consumata da un altro. È anche pericoloso quando un tenant particolarmente aggressivo degrada le prestazioni degli altri. Il classico noisy neighbor non è un difetto estetico del cloud; è un problema di responsabilità. Chi decide quanto un carico può consumare? Chi interviene quando il traffico cambia improvvisamente? Chi paga il costo della protezione?

Habitat aveva inizialmente un container condiviso. Le collisioni fra chiavi e i limiti delle partizioni fisiche rendevano possibile che un uso intensivo penalizzasse funzionalità diverse. Separare i container ha ridotto il raggio degli incidenti: un problema locale poteva compromettere una funzione invece dell’intero prodotto. Questa è una vittoria operativa reale, non una regressione architetturale. Ma a quel punto ogni gruppo di prodotto doveva prevedere in anticipo il proprio fabbisogno, dimensionare le risorse e ricordarsi di aumentare la capacità prima di un lancio.

OpenAI racconta che alcune release arrivavano in produzione senza un adeguato pre-scaling, costringendo i team a intervenire durante gli incidenti. Il carico era stato isolato; la complessità, invece, era stata distribuita a decine di squadre. Se ogni team deve conoscere il funzionamento del database per pubblicare una nuova funzione, la piattaforma centrale ha smesso di nascondere la complessità e ha iniziato a esportarla.

La partizione logica non conosce le tue promesse

Per capire perché l’isolamento non si risolve con un semplice interruttore, bisogna scendere sotto l’astrazione. In Cosmos DB i dati sono organizzati tramite chiavi di partizione logica, ma il throughput disponibile dipende anche da come queste chiavi finiscono sulle partizioni fisiche. La documentazione Microsoft sulla partizione descrive il limite di 10.000 RU/s per partizione fisica e spiega come una distribuzione sbilanciata possa produrre partizioni calde e throttling anche quando il container, considerato nel suo complesso, sembra avere capacità.

Il punto non è che le chiavi logiche siano inutili. È che non costituiscono automaticamente un confine di prestazioni. Più tenant possono finire a contendersi le stesse risorse sottostanti; aumentare un numero in configurazione non significa garantire che la capacità aggiuntiva sia disponibile dove serve. Ecco perché monitorare solo il throughput aggregato è un modo elegante di non vedere il problema. Servono segnali per partizione, distribuzione delle richieste, percentuali di throttling e comportamento dei carichi peggiori, non soltanto la media della giornata.

In un’architettura più piccola la lezione è identica. Se un database relazionale, una coda o un pool di connessioni sono condivisi, il limite effettivo è spesso un dettaglio fisico o di scheduling che l’API nasconde. Lo stesso errore compare nelle migrazioni cloud quando si presume che il servizio gestito elimini ogni vincolo: ne ho discusso anche nell’articolo sugli assunti infrastrutturali che il cloud non cancella.

Il ritorno al multi-tenant non è un ritorno al passato

Il nuovo equilibrio descritto da OpenAI non consiste nel rimettere tutti nello stesso container e sperare. Il team racconta di aver lavorato con Azure su limitazione delle chiavi calde, classi di priorità per i carichi e una modalità serverless migliorata capace di arrivare a 10.000 RU/s per partizione fisica, con split guidati anche dall’uso di RU e non soltanto dallo spazio occupato. Sono caratteristiche e interventi descritti nel caso Habitat: non vanno scambiati automaticamente per funzionalità identiche, disponibili e garantite in qualunque account Cosmos DB.

La distinzione è importante anche per un’altra ragione. La prioritizzazione documentata da Microsoft è best effort, senza uno SLA dedicato per la precedenza, e presenta limiti di compatibilità. Non si può trasformare una descrizione architetturale di un cliente molto grande in una promessa commerciale valida per qualsiasi applicazione. Quello che conta è il principio: se voglio condividere risorse senza far pagare ai carichi critici gli eccessi di quelli secondari, devo avere strumenti reali per misurare, limitare e attribuire il consumo.

Condividere una risorsa non significa condividere alla cieca il suo limite. L’isolamento utile è quello che sai osservare, applicare e verificare quando il sistema è sotto pressione.

OpenAI dichiara di non aver più osservato, nei mesi successivi a questi interventi, incidenti di provisioning o noisy neighbor. È un risultato operativo riportato dalla fonte, non la dimostrazione che la multi-tenancy sia universalmente superiore. Il vantaggio più interessante è che i team di prodotto possono tornare a concentrarsi sulle funzioni, mentre la piattaforma si assume il lavoro difficile: governare il comportamento collettivo dei carichi.

Una cache veloce può restituire il dato sbagliato

La stessa tensione fra prestazioni e controllo emerge nella lettura dei dati. OpenAI spiega che oltre il 90% delle richieste a Habitat sono letture. Replicare tutto ovunque sarebbe la soluzione intuitiva, ma la capacità fisica delle regioni non lo consente: spazio, energia e raffreddamento sono risorse finite. Il team replica ampiamente soltanto dataset piccoli e particolarmente sensibili alla latenza, come alcuni metadati di autenticazione, e per gli altri utilizza una cache distribuita vicina ai punti in cui vengono servite le richieste.

La cache è basata su Valkey. Il problema, però, non è soltanto avere un buon hit rate. Una scadenza TTL breve limita il tempo in cui si può leggere un valore vecchio, ma aumenta i miss; una scadenza lunga migliora le prestazioni e peggiora la freschezza. Invalidare la cache direttamente quando l’applicazione scrive sembra migliore, finché una scrittura riesce e l’invalidazione fallisce, oppure una regione riceve gli eventi prima che la replica del dato sia visibile. In quel momento il sistema può riempire di nuovo la cache con un valore già superato.

Habitat affronta il problema usando il change feed di Cosmos DB come sorgente per propagare invalidazioni alle cache locali, secondo il modello change data capture (CDC). OpenAI riferisce invalidazioni globali nell’ordine di secondi: quando un oggetto cambia, i consumer ricevono l’evento e rimuovono la copia, così la lettura successiva torna allo storage autorevole. La documentazione Microsoft sul change feed chiarisce che le modalità di cattura hanno semantiche diverse, in particolare per versioni intermedie e cancellazioni. Non basta pronunciare “CDC” per ottenere automaticamente una garanzia di coerenza forte.

Questo è un passaggio che interessa anche chi non gestisce centinaia di petabyte. Una cache è parte del modello dei dati: bisogna decidere cosa può diventare obsoleto, per quanto tempo, come recuperare dopo un consumer fermo e come impedire che un evento ritardato ripristini uno stato vecchio. Trattarla come un semplice plugin per guadagnare millisecondi è il modo più rapido di nascondere un bug di correttezza dietro un grafico di performance.

Dodici volte più veloce, ma non perché basta riscrivere in Rust

OpenAI riporta una riduzione della latenza mediana globale delle letture da circa 120 a circa 10 millisecondi, cioè un miglioramento di 12 volte. Nello stesso percorso descrive replica selettiva, caching integrato e riduzione dell’overhead del servizio grazie a una riscrittura in Rust della precedente implementazione Python. È allettante prendere il numero più vistoso e attribuirlo al linguaggio. Sarebbe anche una lettura scorretta della fonte: il dato è presentato come risultato dell’insieme degli interventi, non come benchmark isolato di Python contro Rust.

La mediana, inoltre, non racconta da sola la coda delle latenze. Un sistema può migliorare drasticamente il p50 e continuare a soffrire nei casi peggiori: cache miss interregionali, failover, partizioni calde, ritardi nella propagazione degli eventi. Per valutare un intervento analogo servono distribuzioni per tipologia di richiesta, percentili alti, cache hit/miss, costo per operazione e comportamento sotto carico degradato. Non è un attacco a Rust: è il rifiuto di usare un linguaggio come spiegazione universale di un risultato architetturale.

È lo stesso motivo per cui una riscrittura va giudicata sulla compatibilità e sul comportamento del sistema, non sull’entusiasmo per la tecnologia scelta. Nell’articolo su Mold 3.0 e la riscrittura in Rust il problema centrale era proprio non rompere il contratto mentre cambiava l’implementazione. Habitat aggiunge una dimensione: la riscrittura del servizio è solo uno dei livelli su cui intervenire.

Quando il servizio gestito diventa una responsabilità condivisa

C’è un dettaglio della storia di Habitat che dovrebbe interessare chiunque firmi un contratto cloud pensando di aver trasferito interamente il problema al fornitore. OpenAI racconta che un’interruzione regionale a fine 2024 rivelò tempi di failover di diverse ore per i suoi account più grandi, una scala alla quale né il cliente né il team Cosmos DB avevano eseguito prove sufficienti. Un successivo failback causò un altro incidente grave. L’affidabilità promessa dall’astrazione si è scontrata con una configurazione reale fuori dalla distribuzione usuale dei carichi.

Da qui la necessità di dashboard condivise, test congiunti, canali diretti fra ingegneri e responsabilità esplicite sui failure mode. Il servizio rimane gestito, ma non è più ragionevole trattarlo come una scatola nera di cui ci si occupa solo aprendo un ticket. Questo non significa che ogni azienda debba negoziare un rapporto privilegiato con il cloud provider. Significa che il piano di continuità deve essere verificato alla scala del proprio carico, con dati, esercitazioni e procedure di ritorno alla normalità.

La lezione è sul sistema, non sulla dimensione

Habitat è un caso estremo, e sarebbe ridicolo proporre a un normale progetto web un’infrastruttura con centinaia di topic Kafka, migliaia di worker CDC e replica globale. Ma i principi non richiedono la stessa scala per essere utili. Prima di scegliere tra un database condiviso e uno per cliente, domandiamoci quale guasto deve restare locale, chi controlla i limiti, quali segnali permettono di distinguere un tenant rumoroso da un problema generale e quanta capacità viene sprecata per ottenere l’isolamento desiderato.

Prima di aggiungere una cache, decidiamo come invalidarla e come verificare la freschezza. Prima di dichiarare una migrazione riuscita, proviamo il failover e il failback. Prima di celebrare un miglioramento di 12 volte, chiediamo quali componenti hanno contribuito, quali percentili sono stati misurati e quanto costa mantenere quel risultato. Sono domande meno spettacolari di un nuovo framework, ma molto più vicine al lavoro di progettare software che deve continuare a funzionare.

OpenAI sta inoltre sviluppando una piattaforma di storage su scala exabyte che combina RocksDB Cloud e replica basata su Raft per alcune tipologie di dati voluminose. È un progetto dichiarato in sviluppo, non una sostituzione già completata di Cosmos DB. Anche qui il compromesso è esplicito: meno dipendenza dalla capacità flash replicata, in cambio di un throughput di picco inferiore per certi carichi. La soluzione definitiva non esiste: esiste il problema che, a un certo punto, cambia forma.

Il vero insegnamento di Habitat non è “scegli il multi-tenant”, né “scegli il single-tenant”. È smettere di considerare l’architettura una collezione di tecnologie e iniziare a trattarla come un insieme di responsabilità misurabili. Quando i limiti sono invisibili, ogni ottimizzazione è una scommessa. Quando sono espliciti, puoi perfino permetterti di tornare sui tuoi passi senza chiamarlo fallimento.

Domande frequenti

Perché OpenAI è tornata a un container condiviso dopo aver separato i carichi?

I container dedicati riducevano l’impatto degli incidenti tra servizi, ma trasferivano ai team di prodotto il dimensionamento e la gestione dei picchi. Con nuovi meccanismi di controllo del carico e isolamento, OpenAI ha potuto recuperare i vantaggi operativi della condivisione. È una scelta descritta per Habitat, non una regola universale.

Che differenza c'è tra partizione logica e partizione fisica in Azure Cosmos DB?

La chiave logica raggruppa i dati secondo un criterio applicativo; una partizione fisica ospita una o più partizioni logiche e dispone di risorse e limiti di throughput. Un carico sbilanciato può saturare una partizione fisica anche quando la capacità complessiva sembra sufficiente.

Come funziona l'invalidazione della cache Habitat tramite CDC?

Habitat consuma il change feed di Cosmos DB e propaga eventi di invalidazione alle cache Valkey distribuite. Dopo l’invalidazione, la lettura successiva recupera il valore dallo storage autorevole. Il sistema riduce la staleness ma non equivale automaticamente a una garanzia di consistenza forte.

Il miglioramento di 12 volte della latenza dipende soltanto da Rust?

No. OpenAI attribuisce il passaggio della latenza mediana globale delle letture da circa 120 a circa 10 millisecondi a un insieme di interventi: caching, replica selettiva e riduzione dell’overhead del servizio, inclusa la riscrittura in Rust. Non è un benchmark isolato del linguaggio.