Replacing Protobuf with Rust to go 5 times faster
Recorded: Jan. 23, 2026, noon
| Original | Summarized |
Replacing Protobuf with Rust to go 5 times faster | PgDog PgDog Blog Documentation Community Book a demo Replacing Protobuf with Rust to go 5 times faster Function pg_query::parse (Protobuf) pg_query::parse_raw (Direct C to Rust) pg_query::deparse (Protobuf) pg_query::deparse_raw (Direct Rust to C) The process This is the entrypoint to the libpg_query C library, used by all pg_query bindings. The function that wraps the actual Postgres parser, pg_query_raw_parse, barely registered on the flame graph. Parsing queries isn’t free, but the Postgres parser itself is very quick and has been optimized for a long time. With the hot spot identified, our first instinct was to do nothing and just add a cache. While the id parameter can change between invocations, the prepared statement does not, so we could cache its static AST in memory. Some ORMs can have bugs that generate thousands of unique statements, e.g., value IN ($1, $2, $3) instead of value = ANY($1), which causes a lot of cache misses The clock on Protobuf was ticking and we needed to act. So, like a lot of engineers these days, we asked an LLM to just do it for us. libpg_query is a library that wraps the PostgreSQL parser in an API. pg_query.rs is a Rust wrapper around libpg_query which uses Protobuf for (de)serialization. Replace Protobuf with bindgen-generated Rust structs that map directly to the Postgres AST. And after two days of back and forth between us and the machine, it worked. We ended up with 6,000 lines of recursive Rust that manually mapped C types and structs to Rust structs, and vice versa. We made the switch for parse, deparse (used in our new query rewrite engine, which we’ll talk about in another post), fingerprint and scan. These four methods are heavily used in PgDog to make sharding work, and we immediately saw a 25% improvement in pgbench benchmarks2. For each node in the list, the implementation calls convert_node, which then handles each one of the 100s of tokens available in the SQL grammar: match (*node_ptr).type_ { For nodes that contain other nodes, we recurse on convert_node again until the algorithm reaches the leaves (nodes with no children) and terminates. For nodes that contain scalars, like a number (e.g., 5) or text (e.g., 'hello world'), the data type is copied into a Rust analog, e.g., i32 or String. If you’re curious, Rust hashmap’s implementation uses SipHash, which is fast and DDoS-resistant, but that’s a story for another day. ↩ #699 ↩ PgDog The horizontal scaling layer for PostgreSQL. Resources Documentation Contact Book a demo © 2026 PgDog, Inc. All rights reserved. |
Replacing Protobuf with Rust to go 5x faster | PgDog PgDog, a proxy for scaling PostgreSQL, has significantly improved its performance by replacing its Protobuf serialization with Rust code, resulting in a 5x acceleration in query parsing and a 10x boost in deparsing (converting the Abstract Syntax Tree, or AST, back to SQL). This optimization was spearheaded by Lev Kokotov at PgDog. The core of PgDog’s operation utilizes libpg_query, a C library that parses and interprets SQL queries. Due to this reliance on C, the team recognized the need for efficient data exchange between the Rust implementation and the core library, leading them to employ Protobuf for serialization. However, the team found that direct C-to-Rust bindings, paired with bindgen and Claude-generated wrappers, provided a substantially faster alternative. The initial optimization process centered around profiling. Utilizing Samply, a sampling profiler that measures CPU execution within functions, the PgDog team identified the `pg_query.parse` function, the entry point to the libpg_query C library, as a critical performance bottleneck. The team then considered caching solutions, leveraging an LRU (Least Recently Used) cache backed by a hashmap. This caching strategy, designed for prepared statements – commonly used in SQL, where placeholders are substituted for actual values – aimed to store query structures for later reuse. Despite the success of caching, the team encountered challenges when dealing with ORMs (Object-Relational Mappers) that generated numerous unique statements, such as `value IN ($1, $2, $3)`. These variations caused cache misses, hindering the effectiveness of the caching mechanism. Furthermore, the usage of older PostgreSQL client drivers, like Python’s psycopg2, that didn’t support prepared statements, also posed an issue. Recognizing the need for a rapid solution, the team employed an LLM (Large Language Model), Claude, to automate the conversion process. This approach was deliberately scoped to a well-defined and machine-verifiable task – essentially, translating the Protobuf structure to Rust code that would interact directly with the Postgres parser. The resulting Rust implementation employs recursive algorithms to effectively traverse the AST and convert it into a Rust representation of the SQL query. The translation utilizes unsafe Rust functions to bridge the gap between Rust and C, facilitating interaction with the Postgres/libpg_query C API. The team’s diligence, including utilizing Prost, the Protobuf Rust implementation, and comparing parse and parse_raw output using a generated PartialEq trait, ensured the accuracy and stability of the new approach. The team's innovation delivered notable improvements, realizing a 25% enhancement in pgbench benchmarks. The success was driven by several converging factors: a pre-existing Protobuf spec for generating bindings, the existing use of bindgen, and access to a working parse and deparse implementation – all of which expedited the translation process. The crucial element was the team's ability to rigorously test and verify the AI-generated code, emphasizing the importance of machine-verifiable tasks. The chosen recursive algorithm provides efficient traversal of the AST, leveraging CPU cache locality and avoiding unnecessary memory allocations that hampered iterative approaches. PgDog's goal is to deliver a scalable PostgreSQL proxy with reduced latency and improved resource utilization, highlighting the potential of combining human expertise with AI assistance in optimizing critical system components. The team’s focus now extends to seeking a Founding Software Engineer, showcasing an ambition to build on their achievements and grow their innovative platform. |