8 Punkte von GN⁺ 2026-01-30 | 3 Kommentare | Auf WhatsApp teilen
  • 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

3 Kommentare

 
a1eng0 2026-01-31

Vielleicht missverstehe ich den Originaltext, aber gerade bei Artikeln über Rust wirkt es oft so, als würden sie am eigentlichen Punkt vorbeigehen und so schreiben, als sei es „wegen Rust“ schneller geworden.

Der Hauptpunkt dieses Artikels ist doch, dass sich die Leistung verbessert hat, weil unnötiger Serialisierungs-Overhead reduziert wurde.

Wenn ich jetzt noch einmal draufsehe, ist es wohl doch kein Artikel, der Rust übermäßig lobpreist — oder habe ich durch andere Artikel einfach schon eine negative Wahrnehmung entwickelt?

 
xguru 2026-01-31

Ich fand auch, dass der Originaltitel im Vergleich zum tatsächlichen Inhalt zu stark nach Rust klang und dadurch wirkte, als läge der Fokus auf der Leistungsverbesserung, deshalb habe ich ihn leicht angepasst.
Bei Rust-Artikeln sieht man diese Tendenz recht häufig, daher scheint es sinnvoll, sie mit einem kleinen Filter zu lesen.

 
GN⁺ 2026-01-30
Hacker-News-Kommentare
  • Der Titel wirkt so, als hätte Rust für eine 5-fache Leistungssteigerung gesorgt, ironischerweise wurde es in Wirklichkeit aber langsamer
    Das Problem war, dass die in Rust geschriebene Software libpg_query in C nutzen musste, aber nicht direkt anbinden konnte und deshalb Rust–C-Bindings auf Protobuf-Basis verwendete
    Dieser Ansatz war langsam, also schrieb man schließlich mit Hilfe eines LLM neue Bindings, die zwar weniger portabel, aber deutlich stärker optimiert waren
    Hätte man von Anfang an in C geschrieben, wäre dieser Umwandlungsprozess nicht nötig gewesen. Genauer wäre also ein Titel wie „Den durch den Einsatz von Rust verursachten Performanceverlust reduziert“ gewesen
    Solche Konvertierungsschichten bringen Portabilität und Sicherheit, führen aber letztlich zu wiederholtem Kopieren, Umwandeln und Serialisieren und sind damit einer der Gründe, warum Apps langsamer werden

    • Nicht Rust war langsam, sondern das ineffiziente Design der externen Bibliothek war das Problem
      Aus Rust heraus C-Bibliotheken aufzurufen ist sehr einfach, und es gibt bereits viele sichere Wrapper
      Eine Architektur mit Protobuf als Zwischenschicht sieht man fast nie, und genau das war der Flaschenhals
      Der Titel wirkt eher wie ein einfacher „in Rust neu geschrieben“-Meme, um Klicks zu erzeugen
    • Zu sagen, in C wäre es schneller gewesen, ist nicht fair
      Die ursprüngliche Bibliothek hatte schlicht ein schlechtes Design mit wiederholter Serialisierung/Deserialisierung, und entscheidend war, genau das zu entfernen
      Treffender wäre der Titel „Protobuf durch eine normale API ersetzt und dadurch 5-mal schneller geworden“
    • Ich frage mich, warum nicht direkt FFI verwendet wurde
      C-Bindings in Rust gehören zum Einfachsten, und solange die API nicht riesig ist, bleibt es überschaubar
      Protobuf halte ich für ein ungeeignetes Werkzeug für den Datenaustausch im Speicher
    • Wenn man schon mit einem LLM optimiert hat, hätte man die C-Bibliothek vielleicht gleich vollständig nach Rust portieren können
      Dank LLMs dürfte die Zahl von Portierungen in viele verschiedene Sprachen künftig explosionsartig steigen
    • Protobuf zwischen Rust und Postgres zu setzen, ist auf Performance-Ebene ein Albtraum. Erstaunlich, dass so eine Bibliothek populär werden konnte
  • Der Titel ist etwas irreführend
    Im Grunde heißt es nur: „Nachdem man den Protobuf-Serialisierungsschritt entfernt hat, wurde es schneller“

    • Protobuf bietet Versionskompatibilität, die man mit einfachem Kopieren nicht bekommt
      Dadurch können Client und Server unabhängig voneinander aktualisiert werden und trotzdem weiter funktionieren, außerdem wird die Kommunikation zwischen vielen Sprachen erleichtert
      In großen Systemen ist solche Flexibilität sehr wichtig
    • Dass Protobuf-Serialisierung nur 5-mal langsamer als eine reine Speicherkopie ist, wirkt eher schneller als erwartet
    • memcpy oder mmap sind deutlich schneller, aber im Rust-Umfeld meidet man solche unsicheren Verfahren eher
    • Für so einen Fall wäre vielleicht ein standardisiertes Zero-Copy-Format wie Arrow sinnvoll. Das würde Probleme mit sprachspezifischem Padding und Sicherheitsprüfungen automatisch behandeln
  • Nicht Rust, sondern der Einsatz von Protobuf als generalisierter Speicher-/Ablageformat könnte die Ursache für die Langsamkeit gewesen sein
    Letztlich war entscheidend, die Lösung auf den konkreten Zweck zuzuschneiden und zu vereinfachen

    • Ein Titel wie „Protobuf durch eine nativ optimierte Implementierung ersetzt“ hätte wohl weniger Aufmerksamkeit bekommen
      Dass Rust im Titel auftaucht, wirkt wie eine bewusste Entscheidung zur Klickerzeugung
    • Der Artikeltitel sorgt für Streit, aber der eigentliche Text ist sich dessen bewusst
    • Tatsächlich hat das fast nichts mit Rust zu tun, aber ohne Rust wäre es wohl nicht auf der Startseite gelandet
  • Der ursprüngliche Autor von pg_query erklärt den Hintergrund
    Ursprünglich wurde es bei pganalyze verwendet, um Postgres-Abfragen zu parsen, Tabellenreferenzen zu finden und Queries umzuschreiben oder zu formatieren
    Anfangs nutzte man JSON, wechselte später aber zu Protobuf, um in mehreren Sprachen (Ruby, Go, Rust, Python usw.) leichter typsichere Bindings anbieten zu können
    Für Sprachen wie Rust ist FFI zwar besser, bei anderen Sprachen ist der Wartungsaufwand aber höher
    Er unterstützt Levs Ansatz, und künftig sollen Funktionen für direkten FFI-Zugriff auf libpg_query ergänzt werden
    Wenn Performance allerdings nicht entscheidend ist, bleibt Protobuf weiterhin die bequemere Wahl

  • Die Formulierung „5-mal schneller“ erinnert an den Witz von Cap’n Proto mit „unendlich schnell

    • Cap’n Proto stammt vom Entwickler von Protobuf und wurde so beschrieben, weil die Struktur kein Parsen benötigt
    • In der Praxis hat Cap’n Proto aber eine schlechte Usability
  • Der Titel ist übertrieben, aber die eigentliche Arbeit ist beeindruckend
    Protobuf wurde nicht komplett entfernt, sondern die Art der Nutzung optimiert
    Die Formulierung „Auf X umgestellt und dadurch 5-mal schneller geworden“ bedeutet meist nur: „Eine vorher chaotische Implementierung wurde repariert“
    Die wichtigste Lehre ist:

    1. Serialisierung/Deserialisierung wird leicht zu einem versteckten Flaschenhals
    2. Standardimplementierungen sind meist nicht für jeden Spezialfall optimiert
    3. Nur durch Profiling findet man den tatsächlichen Engpass zuverlässig
      Auch Rust-FFI hat Overhead, daher lag der eigentliche Gewinn nicht an der Sprache, sondern an der Neugestaltung des Datenflusses und der Optimierungsarbeit
  • FlatBuffers ist zwar schneller, aber Protobuf wird genutzt, weil es von großen Unternehmen gepflegt wird

    • Allerdings wird auch FlatBuffers von Google gepflegt
      Am Ende ist die Vorstellung „von Google gemacht, also sicher“ unbegründet
    • Ich habe früher selbst Code auf Googles Plattform (code.google.com) veröffentlicht und erlebt, wie das gescheitert ist
      Eine einfache Zero-Copy-Struktur mit gemeinsamem Speicher und Versionsfeldern würde genügen; einen zwingenden Grund für Protobuf sehe ich nicht
    • Google hat die Zero-Copy-Optimierung für String-Felder bis heute nicht veröffentlicht
  • Ich halte die Performance von Protobuf für ein Witzniveau
    Man sollte ein Zero-Copy-Format verwenden, bei dem Serialisierung praktisch kostenlos ist
    Zum Beispiel ist mein eigenes Lite³ 242-mal schneller als FlatBuffers

    • Diese Bibliothek ist allerdings erst nach November 2025 erschienen
      Für Protobuf sprechen viele praktische Gründe wie Ökosystem, Schema und sprachspezifisches Tooling
  • Das eigentliche Problem war weder Rust noch Protobuf, sondern die ineffiziente Serialisierungsimplementierung der PostgreSQL-Abstraktionsschicht
    pgdog hat diese Schicht entfernt und die Daten direkt über die C-API übergeben
    Wenn man unnötige Funktionen entfernt, wird es natürlich schneller
    Für manche Menschen gibt es aber weiterhin Situationen, in denen Serialisierung nötig ist
    Für sie sendet ein Titel wie „Wechselt zu Rust“ die falsche Botschaft
    In den meisten Fällen reicht am Ende JSON, und wenn man wirklich mehr Geschwindigkeit braucht, sollte man Serialisierung ganz vermeiden

  • Das ist ein unfairer Vergleich
    Wenn man für IPC-Kommunikation ein Serialisierungsprotokoll verwendet, entsteht natürlich Overhead
    Das passt perfekt zu dem Spruch: „20 % schneller ist eine Verbesserung, 10-mal schneller bedeutet, dass es von Anfang an falsch gebaut war“