Zusammenfassung:
- OpenAI verarbeitet mit einer einzelnen Primary und rund 50 Read Replicas (Azure Flexible Server) mehrere Millionen QPS und 800 Millionen Nutzer.
- Um die Schreiblast zu verteilen, wurden shardbare Workloads nach Azure Cosmos DB verlagert; zusätzlich wurden auf Anwendungsebene unter anderem „Lazy Write“-Verfahren eingesetzt, um Schreibvorgänge zu optimieren.
- Durch die Einführung von PgBouncer wurde die Verbindungs-Latenz von 50 ms auf 5 ms reduziert; außerdem wurde ein Cache-Lock-Mechanismus implementiert, um Cache-Miss-Stürme zu verhindern.
- Die Stabilität wurde durch das Entfernen komplexer Join-Abfragen, ein striktes Timeout von unter 5 Sekunden für Schemaänderungen und die Isolation von Workloads nach Traffic-Priorität sichergestellt.
Ausführliche Zusammenfassung:
1. Hintergrund und aktueller Architekturstand
Der PostgreSQL-Traffic von OpenAI ist im vergangenen Jahr um mehr als das Zehnfache gestiegen und verarbeitet inzwischen 800 Millionen Nutzer sowie mehrere Millionen QPS (Queries per Second). Bemerkenswert ist, dass diese Größenordnung mit einer Architektur aus einer einzelnen Primary-Instanz und rund 50 weltweit verteilten Read Replicas betrieben wird. Um Risse im ursprünglichen Design zu verhindern, hat OpenAI umfassende Optimierungen sowohl auf Infrastruktur- als auch auf Anwendungsebene vorgenommen.
2. Wichtige Engpässe und Optimierungsstrategien
-
Verteilung der Schreiblast:
- Die Single-Writer-Struktur von PostgreSQL hat Skalierungsgrenzen und bringt durch MVCC (Multiversion Concurrency Control) Probleme durch Write Amplification mit sich.
- Als Lösung wurden schreibintensive Workloads, die sich horizontal partitionieren (sharden) lassen, in Systeme wie Azure Cosmos DB verlagert.
- Im bestehenden PostgreSQL wurden neue Tabellen untersagt; zusätzlich wurden Anwendungsfehler behoben und Lazy Write eingeführt, also verzögerte Schreibvorgänge zur Glättung von Traffic-Spitzen, um die Last auf der Primary zu minimieren.
-
Abfrageoptimierung und ORM-Management:
- In der Vergangenheit hat eine Abfrage mit Joins über 12 Tabellen einen Ausfall (SEV) verursacht; deshalb werden komplexe Multi-Join-Abfragen vermieden und stattdessen in Anwendungslogik aufgeteilt.
- Ineffiziente, vom ORM erzeugte Abfragen werden fortlaufend überprüft; außerdem wird mit der Einstellung
idle_in_transaction_session_timeoutverhindert, dass inaktive Abfragen Autovacuum blockieren.
-
Connection Pooling (PgBouncer):
- Um das Verbindungslimit von Azure PostgreSQL (5.000) und Verbindungsstürme zu vermeiden, wurde PgBouncer als Proxy-Schicht bereitgestellt.
- Durch den Einsatz von Transaction-/Statement-Pooling wurde die Wiederverwendbarkeit von Verbindungen erhöht und die durchschnittliche Verbindungszeit von 50 ms auf 5 ms verkürzt.
-
Verhinderung von Cache Misses (Cache Locking):
- Um das „Thundering Herd“-Problem zu verhindern, bei dem nach Ablauf eines Cache-Eintrags zahlreiche Anfragen gleichzeitig auf die DB treffen, wurde ein Cache-Lock-(Leasing-)Mechanismus eingeführt.
- Wenn für einen bestimmten Schlüssel ein Cache Miss auftritt, erhält nur eine einzige Anfrage das Recht (Lock), auf die DB zuzugreifen und die Daten zu aktualisieren; die übrigen Anfragen warten, wodurch die DB vor Lastspitzen geschützt wird.
3. Stabilität und Betriebsrichtlinien
- Abmilderung eines Single Point of Failure (SPOF): Selbst bei einem Ausfall der Primary werden Leseanfragen weiterhin über Replicas verarbeitet, wodurch die Schwere eines Ausfalls sinkt; die Primary läuft zudem im Hochverfügbarkeitsmodus (HA) mit Hot Standby, um einen schnellen Failover sicherzustellen.
- Workload-Isolation: Um das „Noisy Neighbor“-Problem zu verhindern, wird Traffic je nach Wichtigkeit auf getrennte Instanzen für Low/High Priority geroutet.
- Striktes Schema-Management: Änderungen, die ein vollständiges Umschreiben der Tabelle (Full Table Rewrite) auslösen, sind verboten; bei Schemaänderungen gilt ein striktes Timeout von 5 Sekunden, um Service-Verzögerungen zu vermeiden.
4. Ausblick (The Road Ahead)
Obwohl die aktuelle Struktur bereits ausreichend skalierbar ist, testet OpenAI für eine weitere Ausweitung der Read Replicas künftig Cascading Replication, bei der nicht mehr die Primary das WAL an alle Replicas sendet, sondern eine zwischengeschaltete Replica das WAL an nachgelagerte Replicas weiterleitet. Langfristig wird auch Sharding innerhalb von PostgreSQL selbst in Betracht gezogen.
2 Kommentare
Zusammenfassung der Hacker-News-Diskussion: https://news.ycombinator.com/item?id=46725300
Die Stärke einer einzelnen Instanz: Viele reagierten darauf, dass Traffic im Maßstab von 800 Millionen Nutzern ohne Sharding mit einem einzelnen Postgres-(Write-)System verarbeitet wird, mit dem Tenor: „Big DB servers are your friend“ — eine erneute Bestätigung dafür, dass Vertical Scaling weiterhin wirksam ist.
Die Ironie von Sharding vs. Refactoring: Zur Passage im Artikel, dass man sich gegen Sharding entschieden habe, weil das Refactoring der bestehenden App zu komplex sei, gab es bissige Witze und Kritik wie: „Es ist ironisch, dass ein Unternehmen, das Coding-AI verkauft, wegen schwierigem Refactoring kapituliert.“ Gleichzeitig wurde die Entscheidung auch verteidigt, da die betriebliche Komplexität und die Migrationskosten von Sharding eine rationale Abwägung rechtfertigen.
Enttäuschung über die technische Tiefe: Da sich der Text eher auf allgemeine Themen wie Caching und Connection Pooling konzentriert, bemängelten einige, dass konkrete Engineering-Details fehlten, und sahen darin eher einen „PR-Artikel“.
Rust-bezogene Diskussion: In den Kommentaren entwickelte sich unabhängig vom Haupttext zudem eine tiefere technische Debatte, in der Methoden geteilt wurden, mit denen sich mithilfe von Compile-Time-Checks in Rust das Problem von „Idle Transaction“ grundsätzlich verhindern lässt.
Ich persönlich fand es spannend, wie man Strukturen wie Cascading Replication angewendet oder im Betrieb technische Grenzen überwunden hat. Dazu habe ich meine Gedanken etwas ausführlicher auf Facebook festgehalten. https://www.facebook.com/share/p/1Kp8V917bL/