- Der PostgreSQL-Erweiterungs-Proxy PgDog hat zur Verbesserung der SQL-Parsing-Performance direkte Rust-Bindings anstelle der Protobuf-Serialisierung eingeführt
- Die bisherige Protobuf-basierte Struktur wurde durch direkte C–Rust-Konvertierung (bindgen + von Claude erzeugte Wrapper) ersetzt, was 5,45-fach schnelleres Parsing und 9,64-fach schnelleres Deparsing brachte
- Der Performance-Engpass wurde in der Funktion pg_query_parse_protobuf gefunden; nach Caching-Versuchen wurde für eine grundlegende Verbesserung die Struktur geändert
- Mithilfe von Claude LLM wurden 6.000 Zeilen Rust–C-Konvertierungscode automatisch erzeugt und auf zentrale Funktionen wie
parse, deparse, fingerprint und scan angewendet
- Durch diese Optimierung wurden CPU-Auslastung und Latenz von PgDog reduziert, wodurch die Effizienz als PostgreSQL-Proxy für horizontale Skalierung deutlich verbessert wurde
PgDog und die Grenzen von Protobuf
- PgDog ist ein Proxy zur Skalierung von PostgreSQL und verwendet intern libpg_query, um SQL-Abfragen zu parsen
- Es ist in Rust geschrieben und kommunizierte bisher über Protobuf-Serialisierung/Deserialisierung mit der C-Bibliothek
- Protobuf ist schnell, aber direkte Bindings sind noch schneller
- Das PgDog-Team hat
pg_query.rs geforkt, Protobuf entfernt und direkte C–Rust-Bindings implementiert
- Dadurch wurde das Query-Parsing 5,45-mal und das Deparsing 9,64-mal schneller
Benchmark-Ergebnisse
- Die Benchmarks lassen sich im Fork-Repository von PgDog reproduzieren
pg_query::parse (Protobuf): 613 QPS
pg_query::parse_raw (direkt C–Rust): 3357 QPS
pg_query::deparse (Protobuf): 759 QPS
pg_query::deparse_raw (direkt Rust–C): 7319 QPS
Analyse des Performance-Engpasses und Caching-Versuche
- Die Analyse der CPU-Zeit mit dem Profiler samply zeigte, dass die Funktion pg_query_parse_protobuf der Engpass ist
- Über Caching wurde eine teilweise Verbesserung versucht
- Verwendet wurde ein Hashmap-Cache auf Basis eines LRU-Algorithmus, der den AST mit dem Query-Text als Schlüssel speichert
- Bei der Verwendung vorbereiteter Statements ist eine Wiederverwendung möglich
- Einige ORMs erzeugten jedoch Tausende eindeutige Queries, und ältere PostgreSQL-Treiber unterstützten vorbereitete Statements nicht, wodurch die Cache-Effizienz gering blieb
Entfernung von Protobuf mithilfe eines LLM
- Das PgDog-Team nutzte Claude LLM, um Rust-Bindings ohne Protobuf zu erzeugen
- Innerhalb eines klaren und überprüfbaren Aufgabenbereichs arbeitete die KI effektiv
- Claude mappt auf Basis der Protobuf-Spezifikation von
libpg_query C-Strukturen auf Rust-Strukturen
- Nach zwei Tagen iterativer Arbeit wurden 6.000 Zeilen rekursiven Rust-Codes fertiggestellt
- Die Lösung wurde auf die Funktionen
parse, deparse, fingerprint und scan angewendet; dabei wurde laut pgbench eine Leistungssteigerung von 25 % bestätigt
Details zur Implementierung
- Die Konvertierung zwischen Rust und C verwendet unsafe-Funktionen, um Strukturen direkt zu mappen
- C-Strukturen werden an die Postgres-API übergeben, um den AST zu erzeugen, und anschließend rekursiv nach Rust konvertiert
- Jeder AST-Knoten wird von der Funktion convert_node verarbeitet, die Hunderte von Tokens der SQL-Syntax mappt
- Für Knotentypen wie SELECT, INSERT usw. gibt es jeweils eigene Konvertierungsfunktionen
- Für das Konvertierungsergebnis wird die bestehende Protobuf-Struktur (
protobuf::ParseResult) wiederverwendet, wodurch sich bei Tests eine Verifikation per Byte-für-Byte-Vergleich durchführen lässt
- Der rekursive Algorithmus benötigt wenig Speicherzuweisungen und nutzt den CPU-Cache effizient, wodurch er schneller ist als eine iterative Implementierung
- Eine iterative Implementierung war wegen unnötiger Speicherzuweisungen und Hashmap-Lookups sogar langsamer
Fazit
- Durch die Verringerung des Overheads des Postgres-Parsers wurden bei PgDog Latenz, Speicherbedarf und CPU-Auslastung gesenkt
- Mit dieser Optimierung entwickelt sich PgDog zu einem schnelleren und kostengünstiger betreibbaren PostgreSQL-Proxy für Skalierung
- PgDog sucht derzeit Ingenieure, die gemeinsam an der horizontalen Skalierung (next iteration) von PostgreSQL arbeiten
Noch keine Kommentare.