- PgDog, proxy ekstensi PostgreSQL, mengadopsi binding langsung Rust alih-alih serialisasi Protobuf untuk meningkatkan performa parsing SQL
- Struktur lama berbasis Protobuf diganti dengan konversi langsung C–Rust (bindgen + wrapper buatan Claude) sehingga kecepatan parsing meningkat 5,45x dan deparsing 9,64x
- Bottleneck performa ditemukan pada fungsi pg_query_parse_protobuf, dan setelah percobaan caching, perubahan struktur dilakukan untuk perbaikan yang lebih mendasar
- Dengan memanfaatkan Claude LLM, tim secara otomatis menghasilkan 6.000 baris kode konversi Rust–C dan menerapkannya pada fungsi utama seperti
parse, deparse, fingerprint, dan scan
- Optimisasi ini menurunkan penggunaan CPU dan latensi PgDog, sehingga efisiensinya sebagai proxy skalabilitas horizontal PostgreSQL meningkat signifikan
PgDog dan keterbatasan Protobuf
- PgDog adalah proxy untuk menskalakan PostgreSQL, dan secara internal menggunakan libpg_query untuk mem-parsing kueri SQL
- Ditulis dalam Rust, dan sebelumnya berkomunikasi dengan pustaka C melalui serialisasi/deserialisasi Protobuf
- Protobuf memang cepat, tetapi pendekatan binding langsung lebih cepat
- Tim PgDog mem-fork
pg_query.rs, menghapus Protobuf, lalu mengimplementasikan binding langsung C–Rust
- Hasilnya, parsing kueri menjadi 5,45x lebih cepat dan deparsing 9,64x lebih cepat
Hasil benchmark
- Benchmark dapat direproduksi dari repositori fork milik PgDog
pg_query::parse (Protobuf): 613 QPS
pg_query::parse_raw (langsung C–Rust): 3357 QPS
pg_query::deparse (Protobuf): 759 QPS
pg_query::deparse_raw (langsung Rust–C): 7319 QPS
Analisis bottleneck performa dan percobaan caching
- Dari analisis waktu penggunaan CPU dengan profiler samply, fungsi pg_query_parse_protobuf teridentifikasi sebagai bottleneck
- Beberapa perbaikan dicoba lewat caching
- Menggunakan cache hashmap berbasis algoritme LRU, menyimpan AST dengan teks kueri sebagai kunci
- Dalam kasus penggunaan prepared statement, hasilnya bisa dipakai ulang
- Namun, beberapa ORM menghasilkan ribuan kueri unik, atau driver PostgreSQL lama tidak mendukung prepared statement, sehingga efisiensi cache rendah
Menghapus Protobuf dengan bantuan LLM
- Tim PgDog memanfaatkan Claude LLM untuk menghasilkan binding Rust tanpa Protobuf
- AI bekerja efektif dalam ruang lingkup tugas yang jelas dan dapat diverifikasi
- Claude memetakan struct C ke struct Rust berdasarkan spesifikasi Protobuf milik
libpg_query
- Setelah iterasi selama 2 hari, tersusun 6.000 baris kode Rust rekursif
- Diterapkan pada fungsi
parse, deparse, fingerprint, dan scan, lalu terkonfirmasi peningkatan performa 25% menurut pgbench
Detail implementasi
- Konversi antara Rust dan C dilakukan dengan fungsi unsafe untuk memetakan struct secara langsung
- Struct C diteruskan ke API Postgres untuk membuat AST, lalu dikonversi secara rekursif ke Rust
- Setiap node AST diproses oleh fungsi convert_node, yang memetakan ratusan token sintaks SQL
- Untuk tiap tipe node seperti SELECT dan INSERT, ada fungsi konversi terpisah
- Hasil konversi memakai ulang struct Protobuf yang sudah ada (
protobuf::ParseResult) sehingga verifikasi lewat perbandingan byte-level saat pengujian dimungkinkan
- Algoritme rekursif ini mengurangi alokasi memori dan meningkatkan efisiensi cache CPU, sehingga lebih cepat daripada implementasi berbasis iterasi
- Implementasi berbasis iterasi justru lebih lambat karena alokasi memori yang tidak perlu dan lookup hashmap
Kesimpulan
- Dengan mengurangi overhead parser Postgres, PgDog menekan latensi, penggunaan memori, dan penggunaan CPU sekaligus
- Melalui optimisasi ini, PgDog berkembang menjadi proxy skalabilitas PostgreSQL yang lebih cepat dan lebih hemat biaya untuk dioperasikan
- PgDog sedang merekrut engineer untuk bersama-sama membangun skalabilitas horizontal (next iteration) PostgreSQL
3 komentar
Mungkin saya salah memahami tulisan aslinya, tetapi khususnya tulisan-tulisan terkait Rust terasa seperti ditulis seolah-olah jadi lebih cepat "karena Rust" sambil mengabaikan inti persoalannya.
Poin utama tulisan kali ini adalah performa meningkat karena mengurangi overhead serialisasi yang tidak perlu.
Sekarang setelah saya lihat lagi, sepertinya ini juga bukan tulisan yang memuja Rust seperti itu, tetapi mungkin saya sudah telanjur punya persepsi negatif karena tulisan-tulisan lain?
Saya juga merasa judul asli tulisan ini, tidak seperti isi sebenarnya, terlalu berfokus pada Rust sehingga terlihat seolah menitikberatkan pada peningkatan performa, jadi saya sedikit mengubahnya.
Sepertinya tulisan-tulisan tentang Rust cukup sering menunjukkan kecenderungan seperti itu, jadi rasanya perlu disaring sedikit saat membacanya.
Komentar Hacker News
Judulnya terkesan seolah Rust memberikan peningkatan performa 5x, tetapi ironisnya justru sebenarnya sempat menjadi lebih lambat
Masalahnya adalah perangkat lunak yang ditulis dengan Rust harus memakai libpg_query berbasis C, namun karena tidak bisa terhubung langsung, mereka menggunakan binding Rust–C berbasis Protobuf
Pendekatan ini lambat, sehingga pada akhirnya, dengan bantuan LLM, mereka menulis ulang binding yang baru, tidak portabel tetapi jauh lebih optimal
Jika sejak awal ditulis dalam C, proses konversi itu tidak akan diperlukan. Jadi judul yang lebih akurat adalah “mengurangi kehilangan performa akibat penggunaan Rust”
Lapisan konversi memang memberi portabilitas dan keamanan, tetapi pada akhirnya penyalinan, konversi, dan serialisasi terus berulang dan menjadi salah satu penyebab aplikasi melambat
Memanggil pustaka C dari Rust sangat mudah, dan wrapper yang aman juga sudah banyak tersedia
Struktur dengan Protobuf di tengah hampir tidak pernah terlihat, dan itulah yang menjadi bottleneck
Judulnya terasa seperti meme “ditulis ulang dengan Rust” untuk memancing klik
Pustaka aslinya memang punya desain buruk karena berulang kali melakukan serialisasi/deserialisasi, dan inti perbaikannya adalah menghapus hal itu
Judul yang lebih akurat adalah “mengganti Protobuf dengan API biasa membuatnya 5x lebih cepat”
Binding C di Rust termasuk yang paling mudah, dan kalau API-nya tidak besar, cukup sederhana
Protobuf menurut saya adalah alat yang tidak cocok untuk pertukaran data di dalam memori
Ke depan, berkat LLM, sepertinya ledakan porting ke berbagai bahasa akan makin sering terjadi
Judulnya agak menyesatkan
Pada dasarnya isinya adalah “setelah menghapus tahap serialisasi Protobuf, semuanya jadi lebih cepat”
Ia memungkinkan klien dan server diperbarui secara independen namun tetap berjalan, serta mempermudah komunikasi antarbahasa
Dalam sistem skala besar, fleksibilitas seperti ini sangat penting
memcpyataummapmemang jauh lebih cepat, tetapi di ekosistem Rust, pendekatan tidak aman seperti itu cenderung dihindariPenyebab lambatnya mungkin bukan Rust, melainkan penggunaan Protobuf sebagai format penyimpanan serba umum
Pada akhirnya, yang penting adalah penyederhanaan sesuai tujuan khusus
Memasukkan Rust ke judul tampaknya pilihan untuk memancing klik
Penulis asli pg_query menjelaskan latar belakangnya
Awalnya ini dipakai di pganalyze untuk mem-parsing kueri Postgres, mencari referensi tabel, serta menulis ulang dan memformat kueri
Pada awalnya menggunakan JSON, tetapi kemudian beralih ke Protobuf agar lebih mudah menyediakan binding yang type-safe ke berbagai bahasa (Ruby, Go, Rust, Python, dan lainnya)
Untuk bahasa seperti Rust, FFI memang lebih baik, tetapi untuk bahasa lain beban pemeliharaannya besar
Mereka mendukung upaya Lev, dan ke depan berencana menambahkan fungsi untuk mengakses libpg_query langsung lewat FFI
Namun jika performa bukan faktor penting, Protobuf tetap merupakan pilihan yang lebih praktis
Ungkapan “5x lebih cepat” mengingatkan pada lelucon Cap’n Proto yang “tak terhingga lebih cepat”
Judulnya memang berlebihan, tetapi pekerjaan yang dilakukan tetap mengesankan
Mereka bukan benar-benar menghapus Protobuf sepenuhnya, melainkan mengoptimalkan cara pemakaiannya
Kalimat seperti “setelah diganti ke X jadi 5x lebih cepat” biasanya berarti “implementasi yang berantakan akhirnya diperbaiki”
Pelajaran utamanya adalah
FFI Rust juga punya overhead, jadi pencapaian sebenarnya bukan karena bahasanya, melainkan berkat desain ulang aliran data dan upaya optimasi
FlatBuffers lebih cepat, tetapi alasan orang memakai Protobuf adalah karena dipelihara oleh perusahaan besar
Pada akhirnya, anggapan “kalau buatan Google pasti aman” tidak punya dasar kuat
Menurut saya, kalau hanya butuh struktur zero-copy dengan berbagi memori dan field versi, tidak ada alasan kuat untuk repot-repot memakai Protobuf
Menurut saya performa Protobuf itu sudah seperti lelucon
Yang seharusnya dipakai adalah format zero-copy di mana serialisasi nyaris gratis
Misalnya, Lite³ buatan saya 242x lebih cepat daripada FlatBuffers
Orang tetap memakai Protobuf karena banyak alasan praktis seperti ekosistem, skema, dan tooling lintas bahasa
Sebenarnya ini bukan masalah Rust atau Protobuf, melainkan implementasi serialisasi yang tidak efisien pada lapisan abstraksi PostgreSQL
pgdog menghapus lapisan itu dan mengirim data langsung ke API C
Kalau fitur yang tidak diperlukan dihapus, tentu hasilnya jadi lebih cepat
Tetapi bagi sebagian orang, serialisasi tetap dibutuhkan
Untuk mereka, judul yang menyuruh “ganti ke Rust” justru memberi pesan yang salah
Pada akhirnya, dalam kebanyakan kasus JSON sudah cukup, dan kalau benar-benar butuh yang lebih cepat, serialisasi itu sendiri sebaiknya dihindari
Ini adalah perbandingan yang tidak adil
Memakai protokol serialisasi untuk komunikasi IPC tentu membawa overhead
Ini pas sekali dengan ungkapan: “kalau 20% lebih cepat itu peningkatan, kalau 10x lebih cepat berarti dari awal desainnya memang salah”