Alle OpenAI-Produkte benötigen schnellen, zuverlässigen Zugriff auf Daten, der von unserer Speicherplattform Habitat bereitgestellt wird. Sie muss innerhalb von Millisekunden auf über 1 Milliarde Nutzer in 40 geografischen Regionen reagieren. Unsere Infrastruktur sah sich einer außerordentlichen Herausforderung gegenüber: explosives Wachstum und neue Anwendungsfälle zu unterstützen und gleichzeitig Zuverlässigkeit, Leistung und die Produktivität der Produktentwickler weiter zu verbessern. Die Lösung dieser Herausforderung erforderte ein Überdenken der Konzeption und des Betriebs unserer Online-Speicherplattform. Wir bewerteten ständig die Trade-offs und folgten dabei dem Muster: zuerst zuverlässig genug für mission-kritischen Produktverkehr werden, dann schnell genug für globale Nutzer und schließlich gewandt im Betrieb bei massivem Maßstab.
Diese zweiteilige Reihe erklärt, wie wir Habitat von einer einfachen Client-Bibliothek und Datenbank zu einer global verteilten Speicherplattform entwickelt haben, die über 500 Petabyte an Daten verwaltet. Teil I konzentrierte sich auf die Entwicklung von Habitat von einem Python-Client zu einem Dienst. Es behandelte auch die Techniken, die es uns ermöglichten, einen Python-basierten Dienst weit über das hinaus zu bringen, was viele als praktikabel ansehen würden.
In diesem Beitrag, Teil II, führen wir durch die Skalierung der zugrunde liegenden Speicherkomponente: Azure Cosmos DB. Durch eine enge Arbeitspartnerschaft mit dem Azure-Team gelangten wir zu einem tragfähigen Multi-Tenancy-Ansatz mit gemeinsam verwalteten Containern und ausgezeichneter Leseleistung bei massivem, globalem Maßstab.
Abbildung 01 · Was ist Habitat?
Online-Speicherplattform
Habitat ist die Online-Speicherplattform, die wir gebaut haben, damit OpenAI-Produkte schnell und zuverlässig auf benötigte Informationen zugreifen können.
- Anfrage
- Antwort
- Änderungen (CDC)
Clients
Online-Speicherplattform
Speicherressourcen
- ChatGPT
- API
- Codex
- Interne Dienste
- Und mehr
Habitat
- CachingCaches
- ACL policiesAutorisierung
- Placement & data residencyData residency
- EncryptionDatensicherheit
- IsolationMulti-Tenancy
- Rate limitingRequest shaping
- RoutingSchema lookup · Data residency
- Azure Cosmos DBOnline-Speicher
- NanobaseOnline-Speicher
- ValkeyCaches
- Blob storageSpeicherressourcen
- Databricks
- Rockset
- Kafka
- Und mehr
Zuverlässiges Multi-Tenancy im großen Maßstab
Wenn Unternehmen wachsen, entwickeln sich Infrastrukturteams oft von Einzel-Mandanten-Systemen zu Multi-Tenant-Systemen weiter. Single-Tenancy, bei dem jedes Team einer Produktfläche sein eigenes System betreibt, verringert den Auswirkungsradius und bietet klare Eigentumsgrenzen und Workload-Isolation. Mit wachsender Zahl von Teams und Produktflächen wird dies teuer im Betrieb. Single-Tenancy macht jedes Produktteam für die Kapazitätsplanung verantwortlich und führt oft zu erheblicher Überprovisionierung.
Multi-Tenancy hingegen überträgt die Kontrolle auf ein zentrales Datenbankteam mit besserem Wissen und mehr Erfahrung bei der Bereitstellung von Ressourcen. Aber Multi-Tenancy schafft eigene Probleme, wie etwa Noisy-Neighbor-Probleme, bei denen ein Mandant einen ungewöhnlich großen Anteil der Ressourcen verbraucht und dadurch den anderen Mandanten schadet.
In Habitat mussten wir all diese Kompromisse abwägen, um ein zuverlässiges, effizientes Speichersystem im großen Maßstab zu entwerfen. Heute ist Habitat eine Multi-Tenant-Datenbankschicht für viele OpenAI-Produkte, aber wir haben eine kreisförmige Reise durchlaufen: von einem einzigen Multi-Tenant-Azure-Cosmos-DB-Container über die Bereitstellung von Single-Tenant-Backing-Containern zurück zur Standardnutzung eines Multi-Tenant-Containers. Wir stehen bei unserer Multi-Tenancy-Story in Habitat noch ganz am Anfang: Wir unterstützen eine große Anzahl von Anwendungsfällen, aber wir konnten Investitionen in diesem Bereich aufschieben, indem wir die integrierten Primitive von Azure Cosmos DB so weit wie möglich nutzen.
Ein vereinfachter Überblick über Azure Cosmos DB Container
Bevor wir unsere Azure Cosmos DB-Reise durchgehen, ein paar grundlegende Kontextinformationen:
Ein Azure Cosmos DB-Konto definiert eine Reihe von Containern und eine Replikationsrichtlinie
Für jeden Container ist eine bestimmte Menge an Durchsatz in Request Units pro Sekunde (RU/s) bereitgestellt
Daten innerhalb jedes Containers werden auf typische Weise nach einem angegebenen logischen Partitionsschlüssel geshardet, mit dokumentierten Grenzwerten für Speicher und Durchsatz pro logischem Partitionsschlüssel
Azure Cosmos DB verwaltet das Sharding logischer Partitionsschlüssel auf weniger physische Backing-Partitionen und teilt sie in mehrere physische Partitionen auf, wenn der Speicher eine Schwelle überschreitet oder wenn der Container mehr physische Partitionen benötigt, um den bereitgestellten Durchsatz aufrechtzuerhalten
Phase I: Verlassen auf Azure Cosmos DB Primitive
Wir ließen anfangs jede Client-Last durch und verließen uns auf den angegebenen Grenzwert von 10K RU/s pro logischem Partitionsschlüssel, um Rückdruck (Back Pressure) zu erzeugen. Zu Beginn verwendeten wir außerdem nur einen einzigen Standard-Azure-Cosmos-DB-Container pro Konto. Das funktionierte ausreichend gut, erwies sich aber aus einigen Gründen als problematisch:
Grenzwerte für logische Partitionsschlüssel
Aufgrund früherer API-Entscheidungen haben wir die Auswahl des logischen Partitionsschlüssels direkt den Clients überlassen. Dadurch kam es häufig zu Kollisionen bei häufig verwendeten Partitionsschlüsseln, wie user_id oder workspace_id, über verschiedene Produktanwendungsfälle hinweg. Da sich diese Anwendungsfälle die Azure Cosmos DB-Grenzwerte auf denselben logischen Partitionsschlüsseln teilten, beschränkte das Design den Gesamtdurchsatz für einen bestimmten Nutzer.
Ein häufiges Problem, dem wir vor dem Umzug zu Habitat und Azure Cosmos DB beim Skalieren von Postgres begegneten, war die Möglichkeit, dass viele Anfragen von einer sehr stark ausgelasteten Organisation oder einem Hot Key—ein Mandant, der eine unverhältnismäßig hohe Last erzeugt—die gesamte Datenbank lahmlegen konnten. Es wurde erheblicher gemeinsamer Aufwand betrieben, um diese Schlüssel zu cachen und Leases in Cache-Misses einzubauen, sodass die entstehende Cache-Miss-Last nicht auf die zugrunde liegende Datenbank niederfällt.
Abbildung 02a · Grenzwerte für logische Partitionsschlüssel
Verschiedene Clients, derselbe Partitionsschlüssel
Noisy Neighbors
Während die dokumentierten Grenzwerte für Datengröße und Durchsatz für logische Partitionsschlüssel gelten, werden die Grenzwerte tatsächlich auf der physischen Partition durchgesetzt. In der Praxis bedeutet dies, dass logische Partitionen 10,000 RU/s nicht erreichen können. Stattdessen teilen sie sich die Grenzwerte der physischen Partition mit jedem anderen logischen Partitionsschlüssel, der auf dieser physischen Partition untergebracht ist.
Dies ermöglichte es Noisy Neighbors, viele unabhängige logische Partitionsschlüssel zu beeinträchtigen, wobei Workloads, die auf Daten von Nischen-Produktfeatures operierten, die zentralen Login- und Signup-Abläufe störten.
Abbildung 02b · Noisy Neighbors
Verschiedene Nutzer, dieselbe physische Partition
Schwache Partitionen
Der Durchsatzgrenzwert einer bestimmten physischen Partition ist außerdem variabel und hängt vom gesamten Durchsatz ab, der dem Azure Cosmos DB-Container zugewiesen ist. Der bereitgestellte Durchsatz des Containers wird ungefähr gleichmäßig auf die physischen Partitionen dieses Containers verteilt.
Mit wachsendem Datenvolumen teilen sich physische Partitionen auf, wodurch die Gesamtzahl der Partitionen steigt, während der gesamte bereitgestellte Durchsatz unverändert bleibt. Das Ergebnis ist, dass der effektive Durchsatzgrenzwert pro Partition mit wachsender Datengröße schrumpft.
Für unsere speicherintensivsten, blob-artigen Anwendungsfälle entsteht dadurch ein Fehlermodus, bei dem bereits das Storage-Wachstum allein den Durchsatz für aktive Workloads reduziert, selbst wenn Anfrage-raten und Nutzung konstant bleiben.
Abbildung 02C · Schwache Partitionen
Das Limit fällt unter die Nachfrage
Eine Partition wird aufgeteilt, wenn sie ihr Speicherlimit erreicht. Jedes untergeordnete Element erhält die Hälfte der Durchsatzkapazität.
Vor der Aufteilung: 10K RU/s Kapazität
Nach der Aufteilung: 5K RU/s Kapazität × 2 Partitionen
Phase II: Workload-Isolation und Single-Tenancy versuchen
Separate Container helfen, diese Grenzen der Partitionsschlüssel und störende Nachbarn teilweise zu lösen. Durch die Verwendung separater Container kann jedem seine eigenen unabhängigen physischen Partitionen zugewiesen werden, und nur die variierenden Workloads auf diesem Container selbst könnten störende Nachbarn sein oder sich im Partitionsschlüsselraum überlappen. Der Nachteil ist, dass der Aufwand für die Bereitstellung neuer Container nicht null ist – jeder Container erfordert eine individuelle Kapazitätsplanung –, daher möchten wir Container nur an einer sinnvollen Service- oder Use-Case-Grenze erstellen.
Wir begannen, separate Azure Cosmos DB Container für die kritischsten neuen Anwendungsfälle zu verwenden, die eine starke Isolation erfordern. In Kombination mit einem Skript zur Verwaltung schwacher Partitionen, das den insgesamt für jeden Container bereitgestellten Durchsatz mit seiner Anzahl physischer Partitionen synchronisiert, vermieden wir die meisten der Probleme, mit denen wir konfrontiert waren.
Da wir jedoch mit einem Standardcontainer begonnen hatten, blieben viele ältere kritische Anwendungsfälle leider ohne Datenmigration daran hängen. Um kritische Dienste zu schützen, die weiterhin auf Daten im gemeinsamen Standardcontainer angewiesen waren, empfahlen wir Teams häufig, separate Container für neue Dienste oder große Produktfunktionen zu erstellen. Als wir skalierten, wuchs der Druck auf den Standardcontainer, und auch Produktionsvorfälle nahmen zu. Wir begannen, Container für jeden neuen Dienst oder jede große Produktfunktion bereitzustellen, nur um den Standardcontainer vor weiterer Last zu schützen.
Das bewahrte uns vor vielen standortweiten Ausfällen und begrenzte den Auswirkungsradius auf Funktions- oder Dienstverschlechterungen. Doch der Aufwand für die Bereitstellung neuer Container holte uns schließlich ein:
Fehlendes stabiles aggregiertes Wachstum
Im alten gemeinsamen Standardcontainer war es unwahrscheinlich, dass ein bestimmter neuer Anwendungsfall, der von 0 QPS auf 1 Million QPS anstieg, die gesamte aggregierte Nutzung signifikant verschieben würde. Auf einem isolierten Container hingegen könnte ein neuer Anwendungsfall, der von 0 auf 1 Million QPS ansteigt, 1,000-mal mehr Last darstellen als die Baseline, die nur dem internen Dogfooding zugeschrieben wird.
Mit dedizierten Containers mussten sich Teams nun merken, Container vorab auf das erwartete Durchsatzniveau für ihre Launches bereitzustellen. Produktteams versandten ihre Funktionen häufig in die Produktion und vergaßen das vorzeitige Skalieren, und wir saßen am Launch-Tag in SEV-Räumen und versuchten, Container in letzter Minute hochzuskalieren.
Dies ist ein zentraler Vorteil der Workload-Durchmischung von Multi-Tenant-Systemen, den wir zugunsten einer besseren Isolation mit dieser Containerstrategie verlieren.
Abbildung 03 · Fehlendes stabiles aggregiertes Wachstum
Dieselbe Workload, unterschiedlicher Spielraum
1K RU/s während des Lasttests → 1M RU/s beim Launch
Gemeinsamer Container
100M RU/s Kapazität · 80% ausgelastet
Lasttest
1K20M=0.005%
Spielraum
Launch
1M20M=5%
Spielraum
Neuer dedizierter Container
10K RU/s Kapazität · keine anderen Workloads
Lasttest
1K10K=10%
Spielraum
Launch
1M10K=10,000%
Spielraum
Kapazitätsplanung
Im Laufe der Zeit erforderte die Überwachung der Nutzung dedizierter Container auch tiefgreifendes Produktwissen darüber, wann sich der Verkehr verschieben oder vergrößern würde.
Wir können ein einfaches Control-Plane-Skript bereitstellen, um den bereitgestellten Durchsatz für jeden Container auf Basis von Wachstumstrends hochzuskalieren, aber ohne das stabile aggregierte Wachstum machte das korrekte Skalieren im Wesentlichen jedes Produktteam bei OpenAI an der Kapazitätsplanung beteiligt.
Dies ließ sich organisatorisch nicht skalieren, da die Sorgfalt, mit der das jeweils zuständige Team sein Azure Cosmos DB Provisioning behandelte, stark variierte.
Phase III: Multi-Tenancy als Standard, mit Datenverschiebungsfähigkeit
Während die Organisation wuchs, wurden diese operativen Probleme schwer zu skalieren. Kein einzelnes, zentrales Team, das die Planung übernahm, führte dazu, dass viele Teams schlecht planten. Infolgedessen verlagerten wir uns wieder zurück in Richtung einer Multi-Tenant-Lösung und arbeiteten eng mit dem Azure-Team zusammen, um unsere ursprünglichen Multi-Tenancy-Probleme mit einem Shared-Tenancy-Design zu beheben:
Hot-Key-Drosselung
Die Drosselung der heißesten Keys zuerst verhindert, dass laute Nachbarn den gesamten verfügbaren Durchsatz einer bestimmten physischen Partition verbrauchen. Dies verringert den Noisy-Neighbor-Effekt, bei dem ein einzelner heißer Key eine explosionsartige Reichweite über die gesamte Website haben kann.
Quality of Service (QoS)
Ein auf Anfragetaxierung basierendes QoS verhindert gleichermaßen, dass Offline-Workloads wie Masseneinfügungen oder Datenextraktionen den gesamten Durchsatz einer bestimmten physischen Partition belegen. Da diesen Workloads eine niedrigere Priorität zugewiesen wird, werden sie zuerst gedrosselt, wodurch eine Drosselung von Anfragen mit höherer Priorität verhindert wird – interaktive Anfragen, die Endnutzern dienen.
Serverless-Modus
Azure Cosmos DB hatte bereits einen Serverless-Modus, der unser Problem schwacher Partitionen im Provisioned-Modus gelöst hätte. Es gab jedoch einen großen Nachteil: Er skalierte ausschließlich basierend auf der Speichernutzung und begrenzte die RU-Nutzung auf 5K RU/s pro physischer Partition. Dies hätte unser Problem schwacher Partitionen gelöst, hätte uns aber daran gehindert, RU-intensive Anwendungsfälle hochzuskalieren.
Ein neuer, verbesserter Serverless-Modus, den das Azure Cosmos DB Team für uns baute, ermöglichte es jeder physischen Partition, auf bis zu 10K RU/s hochzuskalieren, und führte zusätzlich zu Speichernutzung-basierten Splits RU-Nutzung-basierte Splits ein, wodurch wir aufhören konnten, Container manuell zu überwachen und hochzuskalieren.
Mit diesen Änderungen konnten wir zur Verwendung eines einzigen gemeinsamen Containers zurückkehren, was die Notwendigkeit einer anfänglichen oder fortlaufenden Kapazitätsplanung für Produktteams erheblich vereinfacht hat und gleichzeitig die Isolation zwischen Workloads bewahrt. Seitdem gab es in den vergangenen Monaten trotz beschleunigter Produktentwicklung keine Vorfälle mehr im Zusammenhang mit Provisioning oder lauten Nachbarn.
Bessere Zuverlässigkeit durch gemeinsam verwaltete Dienste
Wenn Unternehmen auf OpenAIs Größe anwachsen, werden verwaltete Dienste weniger „verwaltet“. Viele Workloads werden zu Ausreißern in der Infrastruktur des Cloud-Anbieters. Als OpenAI zu einem der größten Nutzer von Azure Cosmos DB wurde, erforderte der erfolgreiche Betrieb ein anderes Modell: gemeinsame Tests, gemeinsame Dashboards, direkte Engineering-Kanäle und gemeinsame Verantwortung für Ausfallmodi, die nur in unserer Größenordnung auftreten. Heute speichert unser größtes Azure Cosmos DB Konto 10er-Petabytes an Daten und bedient mehr als 20M QPS. Nach unserem Wissen sind wir das einzige Unternehmen, das Azure Cosmos DB in diesem Umfang eingesetzt hat.
Ende 2024 machte ein regionaler Ausfall deutlich, warum diese Zusammenarbeit notwendig war. Damals traf ein Stromausfall die primäre Azure-Region für eines unserer größten Azure Cosmos DB Konten. Bei der daraus resultierenden Failover-Übung stellten wir fest, dass unsere größten Konten mehrere Stunden benötigen würden, um in eine sekundäre Region umzuschalten – etwas, das weder unser Team noch das Azure Cosmos DB Team in unserer Größenordnung getestet hatte.
Etwa einen Monat später, während wir diese Konten in die ursprüngliche Region zurückverlagerten, lösten wir einen weiteren schwerwiegenden Ausfall aus. Ein Verbindungsansturm auf die ursprüngliche Region überlastete das Azure Cosmos DB Gateway der ursprünglichen Region. Eine Eigenheit darin, wie der Gateway-Modus-Client von Azure Cosmos DB Failovers behandelte, veranlasste alle Clients, sofort Verbindungen aufzubauen und Anfragen an die ursprüngliche Region zu senden, obwohl nur eine Teilmenge der physischen Partitionen in diese Region zurückverlagert worden war.
Bei jedem Ausfall durchliefen sowohl OpenAI als auch Azure ihre Incident-Management-Prozesse, wobei eine Reihe von Ursachen identifiziert und Korrekturen bereitgestellt wurden. Doch diese Ausfälle waren Symptome eines größeren systemischen Problems zwischen der Art und Weise, wie unser Team und das Azure Cosmos DB Team arbeiteten und testeten. Anfänglich ermöglichte uns die Nutzung verwalteter Dienste, uns schnell zu bewegen und uns auf Geschäftsziele zu konzentrieren. In großem Maßstab werden verwaltete Dienste weniger verwaltet und erfordern tiefe Integrationen mit dem Dienstanbieter auf allen Ebenen, vom Design bis zum Betrieb.
Heute überwachen die Habitat- und Azure-Engineering-Teams gemeinsam jede Ebene von Habitat, von unseren Serviceebenen hinunter bis in die physischen Speicherebenen von Azure Cosmos DB. Wir betreiben und testen Änderungen gegen ein produktionsgroßes, aber nicht-produktives Azure Cosmos DB Konto. Wir testen und iterieren gemeinsam an SDKs und legen Best Practices fest, wie wir unsere Azure Cosmos DB Konten skalieren und dimensionieren. Wir haben direkten Engineering-Zugriff aufeinander über Teams und haben sogar Wege geschaffen, einander direkt zu rufen, ohne Support-Stufen durchlaufen zu müssen.
Ein erheblicher Anteil gemeinsamer Arbeit ist im letzten Jahr erfolgt, um die Skalierbarkeit der Azure Cosmos DB Control Plane und des Gateways zu verbessern, darunter das Hinzufügen von Lastschutz und Caching verschiedener Azure Cosmos DB Metadaten in ihrem Gateway, um die Azure Cosmos DB Metadatenpartition zu schützen, die Reduzierung des Drucks durch unsere Flotte von Python habitat-service-Prozessen durch Connection Pooling und höhere Verbindungswiederverwendung, die Kolokation von Workloads, wo möglich, und die Isolation kritischer Workloads von Offline-Workloads über verschiedene VPCs.
Dieses Ausmaß an Co-Engineering war entscheidend für die Stabilität von Azure Cosmos DB bei uns im letzten Jahr. Jedes System, das groß oder kritisch genug ist, erfordert diese Art von Teamarbeit: das Aufbrechen von Eigentumsgrenzen und das Arbeiten über Organisationsgrenzen hinweg.
Verbesserung der Leistung in globalem Maßstab
Wie in Teil I erwähnt, lautet unsere Kernphilosophie: Make it work, make it fast, make it cheap. Zuerst Zuverlässigkeit, dann Leistung, dann Effizienz. Habitat war zu kritischer Infrastruktur für OpenAI geworden, daher hatte die Zuverlässigkeit höchste Priorität. Als sich das System 2025 stabilisierte und OpenAI global expandierte, wurde die Latenz zur nächsten Einschränkung für Nutzer und Produktteams.
Rechen- und Speicherkapazitäten befanden sich oft in unterschiedlichen Regionen, sodass regionenübergreifende Anfragen unvermeidlich waren.
Abbildung 04 · Verbesserung der Leistung auf globaler Ebene
Globaler Datenverkehr
Rechen- und Speicherkapazitäten befanden sich oft in unterschiedlichen Regionen, sodass regionenübergreifende Anfragen unvermeidlich waren.
Die geografischen Standorte von Compute und Storage dienen nur zur Veranschaulichung.
Die Latenz variierte stark je nachdem, wo die Daten gespeichert waren und wo die Anfrage bearbeitet wurde. Um Habitat-Anfragen leistungsfähig zu machen, griffen Produktentwickler auf zusätzliche Client-seitiges Caching, Request-Batching, die Minimierung von Datenbank-Lese- und Schreibvorgängen sowie die Verlagerung von Arbeit abseits des kritischen Pfads zurück, wo immer möglich. Dies führte zwar zu einigen interessanten Produktentwürfen, die widerstandsfähiger gegenüber Datenbankproblemen waren, machte die Produkte aber auch komplexer und verlangsamte die Produktiteration – ein Versagen gegenüber Habitats Ziel, eine schnelle Produktentwicklung zu fördern und das Verständnis der Datenbank nicht erforderlich zu machen.
Dies war 2026 ein großer Schwerpunkt für uns, und wir haben eine mehrstufige Strategie zur Optimierung der Leseleistung entwickelt, die über 90% der Habitat-Anfragen ausmacht:
Daten nahe bei den Nutzern speichern, wenn die Kapazität es erlaubt
Wir verfügen über mehrere Azure Cosmos DB-Konten in verschiedenen Azure-Regionen weltweit für Nutzer mit Anforderungen an die Datenspeicherung (Data Residency). Selbst für Nutzer ohne strenge Data-Residency-Anforderungen verbessert die Platzierung der Daten in der Nähe der Nutzer deren Erfahrung durch geringere Latenz.
In der Praxis können wir dies außerhalb von Data-Residency-Anwendungsfällen nicht breitflächig erreichen, da die Platzierung durch die physische Cloud-Kapazität eingeschränkt ist. Aufgrund der Größe unserer Datensätze können viele Regionen den massiven Speicherbedarf, der für ihren proportionalen Anteil am Nutzerverkehr erforderlich ist, nicht aufnehmen.
In Regionen mit begrenzter Rechenzentrumskapazität, Stromversorgung oder Kühlung stehen wir manchmal vor einem direkten Kompromiss: Kapazität für gemeinsam platzierten Speicher und geringere Habitat-Latenz reservieren, oder diese Kapazität für zusätzlichen Serving-Compute nutzen und Speicheranfragen weiter weg leiten.
Infolgedessen sind Platzierungsentscheidungen häufig ein produktbezogener Kompromiss zwischen: A) der Optimierung der Latenz für verschiedene Produkte und Nutzergruppen und B) der reinen Speicher- und Rechenkapazität.
Nur die kleinsten und latenzempfindlichsten Datensätze breit replizieren
Für die latenzkritischsten Workloads, wie etwa Authentifizierungsmetadaten, die bei jeder Anfrage benötigt werden, reduzieren wir die RTT zwischen Compute und Habitat, indem wir stark replizierte Azure Cosmos DB-Konten mit vielen sekundären Regionen verwenden. Einige Konten replizieren in alle 40 Azure-Regionen, in denen wir Traffic bedienen; andere replizieren in mindestens eine Region pro Kontinent.
Wir mussten eine transparente Datenbewegungs- und Routing-Schicht in Habitat einbauen, früher als gewünscht, damit wir Datensätze zwischen Konten mit unterschiedlichen Replikationstopologien verschieben können.
Dieser Ansatz ist letztlich ebenfalls kapazitätsbeschränkt und daher auf wenige handverlesene Datensätze beschränkt.
Für alles andere auf integriertes Caching setzen
Habitat-Workloads sind überwiegend leseintensiv, wobei Anwendungen ständig Einstellungen, Zustände und andere Eigenschaften abrufen; ein unverhältnismäßig großer Anteil des Traffics ist auf eine relativ kleine Gruppe von häufig abgerufenen Typen zurückzuführen. Dies macht das System besonders gut für verteiltes Caching geeignet. Während Caching die Leistung in Single-Region-Infrastruktur verbessert, verstärken sich die Verbesserungen bei regionenübergreifender Arbeit, wo es genutzt werden kann, um die regionenübergreifende Latenz zu „verstecken“.
Wir haben eine verteilte Look-Aside-Caching-Schicht in Habitat integriert. Sie speichert eine Kopie kürzlich abgerufener Objekte und Kanten auf Valkey-Servern, die in unserer Infrastruktur nahe dem Ort platziert sind, an dem Nutzeranfragen verarbeitet werden. Typischerweise verwenden solche Caches eine TTL, die einen Kompromiss zwischen Trefferquote und Aktualität darstellt. Kurze TTLs verringern die Veraltung, schmälern aber den positiven Effekt des Cachings, da Einträge schnell aus dem Cache ablaufen und häufiger neu abgerufen werden müssen. Längere TTLs verbessern die Trefferquoten, liefern aber über längere Zeiträume veraltete Daten, was zu schlechten Nutzererlebnissen und unvorhersehbarem Produktverhalten führt.
Ein anderer Ansatz besteht darin, explizite Invalidierungsvorgänge auszuführen, wenn die Anwendung Schreibvorgänge auf denselben zugrunde liegenden Daten durchführt. Dies verbessert die Frische der zwischengespeicherten Daten erheblich, kann aber schwer korrekt umzusetzen sein. Es bleibt zudem einer Reihe von Grenzfällen unterworfen, wie etwa Teilfehlern, die den Cache veraltet lassen. Multi-Region-Bereitstellungen verschärfen diese Probleme, mit weniger zuverlässigen regionenübergreifenden Netzwerken und dem Wettrennen zwischen Cache und Replikation des zugrunde liegenden Speichers. Eine Ergänzung durch TTLs begrenzt die maximale Veraltung, leidet aber weiterhin unter demselben zugrunde liegenden TTL-Kompromiss, der oben besprochen wurde.
Wir haben einen fortgeschritteneren Ansatz zur Invalidierung verfolgt, in den Fußstapfen von Systemen wie Metas TAO. Habitats integrierter Cache nutzt Change Data Capture (CDC) über den Change Feed von Azure Cosmos DB, um Invalidierungen global innerhalb von Sekunden zu verbreiten und so hohe Trefferquoten bei geringer Veraltung zu erreichen. Ein Dienst in unserer Infrastruktur konsumiert diesen Change Feed und führt Invalidierungen an den lokalen Caches durch, wenn die zugrunde liegenden Daten aktualisiert werden. Dies verursacht beim nächsten Lesen der Daten einen Miss, und der Cache wird mit der neuesten Version der Daten gefüllt.
Proxy-Overhead mit Rust reduzieren
Wir haben die Latenz auch erheblich reduziert, indem wir Overhead im Habitat-Dienst selbst beseitigt haben, indem wir den Code in Rust neu geschrieben haben, das performanter als das ursprüngliche Python ist.
Zusammen haben diese Änderungen unser globales Read-p50 von rund 120 ms auf rund 10 ms reduziert, eine 12-fache Verbesserung, seit wir Anfang dieses Jahres den Fokus auf Performance gelegt haben.
Abbildung 05 · Verbesserung der Performance in globalem Maßstab
p50-Latenz für Reads
Drei große Designänderungen verbesserten die Read-Latenz
rund 120 ms → rund 10 ms
12-fache Verbesserung
- kritische Daten in nahegelegene Geos repliziert
- In-Region-Caching eingeführt
- Rust-Rewrite eingeführt
Während Caching die mediane Latenz dramatisch reduziert hat, erfordern die langsamsten Anfragen weiterhin das Abrufen nicht zwischengespeicherter Daten aus dem zugrunde liegenden Speicher, oft über weit entfernte Regionen hinweg. Da mehr Kapazität verfügbar wird und wir die Speichereffizienz verbessern, planen wir, eine grundlegendere Herausforderung anzugehen: Daten von Anfang an näher an dem Ort zu platzieren, von dem aus auf sie zugegriffen wird.
Skalierung von Change Data Capture
Zuverlässiges und skalierbares CDC – die Weitergabe von Datenbankänderungen an nachgelagerte Systeme – ist eine kritische Komponente in weit mehr als nur Habits Caching-Strategie. Millionen erfasster Änderungen pro Sekunde, verteilt über tausende Container und Millionen von Containerpartitionen, fließen durch mehr als hundert Kafka-Topics und treiben zusätzlich eventuell konsistente Sekundärindizes, Datenverschiebungs-Pipelines, Offline-Analysen und andere Echtzeit-Produktfeatures an. Da unsere zugrunde liegende Infrastruktur global gewachsen ist, ist es zunehmend komplexer geworden, auf Zeilenänderungen zu lauschen. Wenn ein Client einen Callback registrieren möchte, der immer ausgeführt wird, wenn Zeilen seines Typs geändert werden, muss er unter anderem zunächst bestimmen, welche Cosmos-Accounts und -Container seine Daten enthalten, welche CDC-Service-Bereitstellung in den jeweiligen Regionen läuft und auf diese Container lauscht, und die CDC-Service-Worker-Flotte, Kafka und seinen Consumer richtig skalieren. Unsere CDC-Plattform zielt darauf ab, all dies von Produktentwicklern fernzuhalten und stattdessen eine einfache Oberfläche bereitzustellen, auf der Clients eine Funktion registrieren, die bei ihren geänderten Zeilen aufgerufen werden soll, während wir die gesamte Verdrahtung und Skalierung übernehmen.
Eine interne Steuerungsebene stellt kontinuierlich sicher, dass alle erforderlichen Container erfasst werden, wählt aus, aus welchen Cosmos-Regionen gelesen wird, und verteilt die Arbeit effizient auf tausende Worker über mehrere geografische Regionen. Die Verwendung von Replikatregionen und Feed-Reads mit niedrigerer Priorität schützt latenzsensiblen Habitat-Traffic vor Hintergrund-Erfassungsworkloads. Worker-Abstürze, Containerprobleme, Lease-Store-Fehler, ungültige Konfigurationen und Ziel-Fehler treten bei einer so großen Flotte ständig auf. Wir haben den CDC-Worker-Lebenszyklus und das Lease-Handling gehärtet, Fehler zwischen Streams isoliert und Schutzmaßnahmen für Konfigurationsänderungen und Rollouts gebaut. Wir nutzen Dead-Letter-Queues, um Head-of-Line-Blocking durch dauerhafte Zustellfehler zu verhindern. Regionale Ausfälle tolerieren wir, indem wir Leser auf alternative Cosmos-Regionen umschalten. Bei unserem Maßstab mussten wir sogar Änderungen am CDC-SDK von Azure vornehmen, um dessen Checkpointing-Implementierung effizienter zu machen, indem wir Checkpoints im Speicher bündeln und sie nur periodisch flushen.
Während Habits interne Nutzung von Azure Cosmos DB komplex ist, besteht ihr Nutzenversprechen stets darin, Produktentwicklern Infrastrukturessen zu abstrahieren, um die Produktentwicklungsgeschwindigkeit zu verbessern – und unsere CDC-Plattform ist da nicht anders.
Ausblick
Ein kleines Team konnte eine schnell wachsende Speicherplattform unterstützen, indem es die Storage-API bewusst schmal hielt, den Python-Service weiter skalierte und die dringendsten technischen Herausforderungen priorisierte. Wir konzentrierten uns zuerst auf Zuverlässigkeit für mission-kritischen Produktverkehr, dann auf Read-Performance für globale Nutzer und schließlich auf operative Effizienz im großen Maßstab. Dieser Ansatz ermöglichte schnelles Wachstum und neue Anwendungsfälle bei gleichzeitiger Verbesserung von Zuverlässigkeit, Performance und Produktentwicklungsgeschwindigkeit.
Um Habitat und die Konversationsspeicherung für die nächste Milliarde Nutzer zu skalieren, müssen wir die Schwierigkeit angehen, unsere Daten in noch größerem Maßstab über unsere Infrastruktur zu replizieren. Um dem zuvorzukommen, bauen wir ein eigenes, maßgeschneidertes Online-Speichersystem im Exabyte-Maßstab, optimiert für voluminöse Habitat-Datentypen. Dieses sich in Entwicklung befindliche System kombiniert den RocksDB-Cloud-Kern von Rockset mit Raft-basierter Replikation. Es implementiert dasselbe Dokumentenmodell wie Azure Cosmos DB, nutzt jedoch entkoppelten Speicher und physische Replikation, um den benötigten Flash-Kapazitätsbedarf zu reduzieren – im Austausch für einen niedrigeren Peak-qps.
Wenn Sie daran interessiert sind, an diesen Arten von Dateninfrastruktur-Projekten in Frontier-Maßstab zu arbeiten, schauen Sie sich unsere offenen Stellen an.