8 puntos por GN⁺ 2026-01-30 | 3 comentarios | Compartir por WhatsApp
  • PgDog, un proxy de extensiones para PostgreSQL, adoptó enlaces directos de Rust en lugar de serialización con Protobuf para mejorar el rendimiento del análisis de SQL
  • Reemplazó la estructura previa basada en Protobuf por conversión directa C–Rust (bindgen + wrappers generados con Claude), logrando una mejora de 5.45 veces en parsing y 9.64 veces en deparsing
  • El cuello de botella de rendimiento se encontró en la función pg_query_parse_protobuf, y tras intentar con caché se hizo un cambio estructural para lograr una mejora de fondo
  • Con Claude LLM se generaron automáticamente 6,000 líneas de código de conversión Rust–C, aplicadas a funciones clave como parse, deparse, fingerprint y scan
  • Con esta optimización, bajaron el uso de CPU y la latencia de PgDog, mejorando mucho su eficiencia como proxy de escalado horizontal para PostgreSQL

PgDog y los límites de Protobuf

  • PgDog es un proxy para escalar PostgreSQL y usa internamente libpg_query para analizar consultas SQL
    • Está escrito en Rust y antes se comunicaba con la librería en C mediante serialización/deserialización con Protobuf
  • Protobuf es rápido, pero el enfoque de enlaces directos es todavía más rápido
    • El equipo de PgDog hizo un fork de pg_query.rs, eliminó Protobuf e implementó enlaces directos C–Rust
    • Como resultado, el parsing de consultas fue 5.45 veces más rápido y el deparsing 9.64 veces más rápido

Resultados del benchmark

  • El benchmark se puede reproducir en el repositorio fork de PgDog
    • pg_query::parse (Protobuf): 613 QPS
    • pg_query::parse_raw (directo C–Rust): 3357 QPS
    • pg_query::deparse (Protobuf): 759 QPS
    • pg_query::deparse_raw (directo Rust–C): 7319 QPS

Análisis del cuello de botella e intento con caché

  • Tras analizar el tiempo de uso de CPU con el profiler samply, se confirmó que la función pg_query_parse_protobuf era el cuello de botella
  • Se intentó una mejora parcial mediante caché
    • Se usó una caché hash map basada en algoritmo LRU, guardando el AST con el texto de la consulta como clave
    • En los casos con prepared statements, era posible reutilizarlo
  • Sin embargo, algunos ORM generaban miles de consultas únicas, o bien drivers antiguos de PostgreSQL no soportaban prepared statements, por lo que la eficiencia de la caché era baja

Eliminación de Protobuf con ayuda de un LLM

  • El equipo de PgDog usó Claude LLM para generar bindings de Rust sin Protobuf
    • La IA funcionó bien dentro de un alcance de trabajo claro y verificable
  • Claude mapeó estructuras C a estructuras Rust basándose en la especificación Protobuf de libpg_query
    • Tras dos días de iteración, completaron 6,000 líneas de código recursivo en Rust
  • Se aplicó a las funciones parse, deparse, fingerprint y scan, confirmando una mejora de rendimiento del 25% según pgbench

Detalles de implementación

  • La conversión entre Rust y C se hace con funciones unsafe que mapean directamente las estructuras
    • Las estructuras C se pasan a la API de Postgres para generar el AST, y luego se convierten recursivamente a Rust
  • Cada nodo del AST se procesa con la función convert_node, que mapea cientos de tokens de la sintaxis SQL
    • Existen funciones de conversión separadas para cada tipo de nodo, como SELECT o INSERT
  • El resultado de la conversión reutiliza la estructura Protobuf existente (protobuf::ParseResult), lo que permite validación en pruebas mediante comparación a nivel de bytes
  • El algoritmo recursivo requiere menos asignaciones de memoria y aprovecha mejor la caché de CPU, por lo que fue más rápido que una implementación basada en iteraciones
    • Una implementación iterativa resultó más lenta por asignaciones de memoria innecesarias y búsquedas en hash maps

Conclusión

  • Al reducir la sobrecarga del parser de Postgres, PgDog logró bajar latencia, memoria y uso de CPU
  • Con esta optimización, PgDog evoluciona como un proxy de extensiones para PostgreSQL más rápido y más barato de operar
  • PgDog está contratando ingenieros para construir juntos la siguiente iteración del escalado horizontal de PostgreSQL

3 comentarios

 
a1eng0 2026-01-31

Puede que yo esté malinterpretando el texto original, pero especialmente los artículos relacionados con Rust parecen escritos como si, dejando de lado lo esencial, se hubiera vuelto más rápido simplemente "por ser Rust".

El punto principal de este artículo es que el rendimiento mejoró al reducir la sobrecarga innecesaria de serialización.

Ahora que lo vuelvo a ver, tampoco parece ser un artículo que glorifique tanto a Rust, pero ¿será que otros artículos ya me hicieron formar una percepción negativa?

 
xguru 2026-01-31

Yo también sentí que el título original, a diferencia del contenido real, estaba demasiado cargado hacia Rust y hacía ver que el foco era la mejora de rendimiento, así que lo ajusté un poco.
Como los artículos sobre Rust suelen mostrar esa tendencia con frecuencia, creo que conviene leerlos filtrándolos un poco.

 
GN⁺ 2026-01-30
Opiniones en Hacker News
  • El título hace parecer irónicamente que Rust dio una mejora de rendimiento de 5x, cuando en realidad se había vuelto más lento
    El problema era que el software escrito en Rust tenía que usar libpg_query, que está en C, pero como no podían conectarlo directamente, usaron unos bindings Rust–C basados en Protobuf
    Ese enfoque era lento, así que al final, con ayuda de un LLM, reescribieron bindings nuevos, menos portables pero mucho más optimizados
    Si lo hubieran escrito en C desde el principio, no habría hecho falta el proceso de conversión. O sea, el título más exacto habría sido “redujimos la pérdida de rendimiento causada por usar Rust”
    Las capas de conversión dan portabilidad y seguridad, pero al final repiten copias, conversiones y serialización, y creo que son una de las causas que terminan haciendo más lenta a una app

    • No es que Rust sea lento, el problema era el diseño ineficiente de una librería externa
      Llamar librerías de C desde Rust es muy fácil, y ya existen muchos wrappers seguros
      Casi nunca he visto una arquitectura con Protobuf en medio, y eso era el cuello de botella
      El título se siente más como un meme de “lo reescribimos en Rust” para atraer clics
    • Decir que en C habría sido más rápido no es del todo justo
      La librería original tenía un mal diseño que repetía serialización/deserialización, y la clave fue eliminar eso
      Un título más preciso sería “reemplazamos Protobuf por una API común y se volvió 5 veces más rápido”
    • Me pregunto por qué no usaron FFI directamente desde el inicio
      En Rust, los bindings con C son de lo más fácil y, si la API no es grande, bastante simples
      Protobuf me parece una herramienta inadecuada para intercambiar datos en memoria
    • Si ya usaron un LLM para optimizar, me pregunto si no habría sido mejor usarlo para portar por completo la librería de C a Rust
      Da la impresión de que, gracias a los LLM, van a explotar las migraciones entre lenguajes
    • Poner Protobuf entre Rust y Postgres suena a pesadilla de rendimiento. Sorprende que una librería así se haya vuelto popular
  • El título se presta un poco a confusión
    En la práctica, la historia es “quitamos el paso de serialización con Protobuf y se volvió más rápido”

    • Protobuf ofrece compatibilidad de versiones que no se logra con una copia simple de memoria
      Permite que cliente y servidor se actualicen por separado y sigan funcionando, además de facilitar la comunicación entre varios lenguajes
      En sistemas grandes, esa flexibilidad es muy importante
    • Que serializar con Protobuf sea solo 5 veces más lento que una copia directa de memoria hasta da la impresión de ser más rápido de lo esperado
    • memcpy o mmap son mucho más rápidos, pero en el mundo Rust se evita ese tipo de métodos inseguros
    • En casos así, quizá convendría usar un formato zero-copy estandarizado como Arrow. Te resuelve automáticamente temas de padding entre lenguajes y validaciones de seguridad
  • La lentitud quizá no venía de Rust, sino de usar Protobuf como formato de almacenamiento generalizado
    Al final, la clave fue simplificarlo para ajustarlo a un propósito específico

    • Un título como “reemplazamos Protobuf por una implementación nativa optimizada” habría llamado menos la atención
      Meter Rust en el título parece más una decisión para generar clics
    • El título del artículo provoca debate, pero el texto sí es consciente de eso
    • En realidad casi no tiene que ver con Rust, pero sin Rust probablemente no habría llegado a la portada
  • El autor original de pg_query explicó el contexto
    En un principio, en pganalyze lo usaban para parsear consultas de Postgres, encontrar referencias a tablas, y reescribir o formatear queries
    Al inicio usaban JSON, pero luego cambiaron a Protobuf para poder ofrecer más fácilmente bindings con seguridad de tipos en varios lenguajes (Ruby, Go, Rust, Python, etc.)
    Para lenguajes como Rust, FFI sí es mejor, pero en otros lenguajes la carga de mantenimiento es mayor
    Apoya el intento de Lev, y planea agregar en el futuro funciones para acceder directamente a libpg_query vía FFI
    Aun así, cuando el rendimiento no es crítico, Protobuf sigue siendo una opción más conveniente

  • Lo de “5 veces más rápido” hace pensar en el chiste de Cap’n Proto de que es “infinitamente rápido

    • Cap’n Proto fue creado por quien hizo Protobuf, y usaban esa expresión porque tiene una estructura que no requiere parsing
    • Pero en la práctica, Cap’n Proto tiene peor usabilidad
  • El título es exagerado, pero el trabajo real sí impresiona
    No quitaron Protobuf por completo, sino que optimizaron la manera de usarlo
    La frase “cambiamos X y se volvió 5 veces más rápido” casi siempre significa “arreglamos una implementación que estaba hecha un desastre”
    La enseñanza principal es:

    1. La serialización/deserialización puede convertirse fácilmente en un cuello de botella oculto
    2. Las implementaciones por defecto casi nunca están optimizadas para todos los casos
    3. Hay que encontrar el cuello de botella exacto mediante profiling
      El FFI de Rust también tiene overhead, así que el logro real no vino del lenguaje sino del rediseño del flujo de datos y del trabajo de optimización
  • FlatBuffers es más rápido, pero se usa Protobuf porque lo mantiene una gran empresa

    • Pero FlatBuffers también lo mantiene Google
      Al final, la idea de “si lo hizo Google, es seguro” no tiene mucho fundamento
    • Yo antes subí código a la plataforma de Google (code.google.com) y ya vi cómo terminó mal
      Si lo único que hace falta es una estructura zero-copy con memoria compartida y campos de versión, no veo por qué usar Protobuf
    • Google todavía no publica una optimización zero-copy para campos de strings
  • Me parece que el rendimiento de Protobuf es casi un chiste
    Habría que usar un formato zero-copy donde serializar sea prácticamente gratis
    Por ejemplo, Lite³, que yo hice, es 242 veces más rápido que FlatBuffers

    • Pero esa librería recién aparecerá después de noviembre de 2025
      La razón por la que se usa Protobuf tiene que ver con el ecosistema, los esquemas, el tooling por lenguaje y muchas otras razones prácticas
  • En realidad no era un problema de Rust ni de Protobuf, sino de una implementación ineficiente de serialización en la capa de abstracción de PostgreSQL
    pgdog quitó esa capa y pasó los datos directamente a través de la API en C
    Si eliminas funciones innecesarias, obviamente va a ser más rápido
    Pero para algunas personas la serialización sigue siendo necesaria
    Para ellas, un título como “cámbiate a Rust” transmite el mensaje equivocado
    Al final, en la mayoría de los casos JSON es suficiente, y si de verdad necesitas más velocidad, lo ideal es evitar la serialización en sí

  • Esta es una comparación injusta
    Usar un protocolo de serialización para comunicación IPC obviamente tiene overhead
    Le queda perfecto esa frase de que “si mejora 20%, es una optimización; si mejora 10x, es porque estaba mal diseñado desde el inicio”