Rewriting PostgreSQL in Rust: Challenges and Lessons Learned
Porting PostgreSQL to Rust surfaces deep challenges in memory management, concurrency, and extension compatibility, with measurable performance trade-offs.
Porting a decades-old C codebase like PostgreSQL to Rust involves serious low-level engineering trade-offs across memory management, concurrency, and compatibility. Replacing manual malloc/free and MemoryContexts with Rust's ownership model via Rc/Arc wrappers introduced measurable overhead—12-15% in benchmarks—while Rust's default stack size clashed with PostgreSQL's deep recursive query planning.
The concurrency model also required a rework: pthread_mutex was replaced with Mutex/RwLock, and spinlocks gave way to external crates like crossbeam, trading verbosity for compile-time safety. Migrating to async-std introduced latency spikes under high concurrency. Preserving compatibility for PostgreSQL's 300+ official extensions demanded a hybrid C/Rust execution model and an SPI compatibility layer, while byte-level parity had to be maintained for on-disk data formats.
Early benchmarks showed 8-12% performance degradation on TPC-C workloads, prompting optimizations like #[inline(always)], const generics, and thread-local allocator caching. Memory-intensive operations such as sorting and hash joins required reimplementing PostgreSQL's JIT interface using cranelift. The key takeaway is that incremental migration—starting with the storage layer before touching query execution—and disciplined, narrowly scoped use of unsafe code are essential; a full MVCC model and a Rust-native query planner remain future milestones.