Cara menskalakan throughput PgBouncer hingga 4x
(clickhouse.com)- ClickHouse Managed Postgres menjalankan armada multi-proses sebagai konfigurasi default yang sebanding dengan jumlah core, untuk menghindari batasan PgBouncer yang single-thread dan terikat pada satu core CPU
- Semua proses melakukan bind ke port yang sama dengan
so_reuseport, dan kernel mendistribusikan koneksi baru, sehingga bagi klien terlihat sebagai satu endpoint - Permintaan pembatalan Postgres yang masuk melalui koneksi terpisah bisa tiba di proses yang tidak memegang sesi, sehingga diteruskan ke pemilik sesi sebenarnya melalui peering antarporses
- Pada
c7i.4xlarge16-vCPU, satu proses mencapai puncak sekitar 87.000 TPS, tetapi armada 16 proses naik hingga sekitar 336.000 TPS, mencatat throughput sekitar 4x lebih tinggi - Saat koneksi sedikit, satu proses bisa setara atau sedikit lebih cepat, tetapi pada konkurensi tinggi satu core menjadi bottleneck, sehingga anggaran koneksi perlu dibagi ke seluruh armada untuk menskalakan throughput dan batas koneksi tanpa over-connection ke Postgres
Bottleneck CPU yang dibuat oleh satu proses
- PgBouncer bersifat single-thread, jadi berapa pun jumlah CPU sistem, satu proses hanya menggunakan satu core
- Bahkan pada server 16-vCPU, pekerjaan connection pooling terkonsentrasi pada satu core dan 15 core lainnya tetap idle
- Akibatnya, pooler membatasi throughput keseluruhan sebelum kapasitas pemrosesan Postgres habis
- ClickHouse Managed Postgres mengoperasikan armada proses yang menentukan jumlah proses PgBouncer secara proporsional terhadap jumlah core yang tersedia
Distribusi koneksi dengan so_reuseport
- Semua proses dalam armada mengaktifkan
so_reuseportdan melakukan bind ke port yang sama - Kernel mendistribusikan koneksi masuk ke tiap proses, sehingga klien cukup terhubung ke satu endpoint tanpa perlu tahu bahwa ada beberapa PgBouncer yang berjalan di belakangnya
- Dokumentasi PgBouncer juga merekomendasikan
so_reuseportsebagai cara memanfaatkan beberapa core dengan banyak proses single-thread
Menjamin pembatalan query dengan peering antarporses
- Permintaan cancel Postgres masuk bersama cancel key melalui koneksi baru yang terpisah dari koneksi query yang sedang berjalan
- Saat memakai
so_reuseport, kernel bisa mengarahkan koneksi cancel baru ke proses yang berbeda dari proses yang memegang sesi tersebut- Jika proses yang menerima permintaan tidak mengenali query itu, pembatalan tidak akan dijalankan
- Jika proses-proses PgBouncer melakukan peering, permintaan yang tiba di proses yang salah dapat diteruskan ke proses pemilik sesi yang sebenarnya
- Dengan demikian, pembatalan query tetap berfungsi dengan benar di seluruh armada, siapa pun proses yang menerima permintaan terlebih dahulu
Transaction pooling dan anggaran koneksi
- Pooling berjalan dalam mode transaksi, dan begitu transaksi di-commit, koneksi server segera dikembalikan ke pool
max_client_conndanmax_db_connectionsdibagi menurut jumlah proses lalu dialokasikan ke masing-masing proses- Dengan membagi anggaran koneksi, jumlah koneksi total seluruh armada tetap berada dalam batas aman Postgres sambil menaikkan batas koneksi agregat
- Satu proses akan menolak klien baru yang melampaui
max_client_connmiliknya sendiri dengan error berikutFATAL: no more connections allowed (max_client_conn)
Benchmark pada hardware yang sama
- Kedua konfigurasi diukur pada lingkungan AWS EC2 yang sama
- Server pooler: 16-vCPU
c7i.4xlarge - Postgres: server terpisah
- Pembangkit beban: menggunakan
pgbenchdari server ketiga - Beban kerja: read-only (select-only), mode transaction pooling
- Server pooler: 16-vCPU
- Objek perbandingan adalah satu proses PgBouncer versus armada 16 proses, dengan tipe instance, Postgres, dan beban kerja yang sama
- Koneksi klien ditingkatkan dari 8 hingga 256 sambil mengukur throughput dan penggunaan CPU server 16-core
| Klien | TPS satu proses | CPU server satu proses | TPS armada | CPU server armada |
|---|---|---|---|---|
| 8 | 8,910 | 0.8% | 6,450 | 2.9% |
| 32 | 54,203 | 5.2% | 64,244 | 12.3% |
| 64 | 86,570 | 8.3% | 219,439 | 31.9% |
| 128 | 83,463 | 8.1% | 320,547 | 45.9% |
| 256 | 76,893 | 7.7% | 336,469 | 48.9% |
Hasil throughput dan pemanfaatan CPU
- Satu proses mencapai puncak di sekitar 87.000 TPS, lalu performanya menurun saat beban meningkat, dengan sekitar 77.000 TPS pada 256 klien
- Armada 16 proses meningkat hingga sekitar 336.000 TPS sambil memanfaatkan lebih banyak core, menunjukkan throughput sekitar 4x dibanding satu proses
- Dari pengukuran
pidstat, satu proses PgBouncer memakai sekitar 97% CPU saat dibebani, hampir memenuhi satu core, tetapi penggunaan total server 16-vCPU tetap di bawah 10% - Armada menyebarkan pekerjaan ke seluruh server dan menggunakan sekitar 8 core, serta masih memiliki ruang tambahan bahkan ketika Postgres dan pembangkit beban mencapai batasnya
- Pada pengukuran dengan 256 klien tetap, server satu proses terus menggunakan sekitar 9% CPU, sedangkan server armada terus menggunakan sekitar 52% CPU
- Dari pengukuran eksternal EC2 CloudWatch, rata-rata CPUUtilization instance satu proses sekitar 16%, sedangkan armada sekitar 60%
- Angka CloudWatch sedikit lebih tinggi daripada pengukuran dari dalam guest, tetapi perbedaannya tetap sama: satu PgBouncer tidak mampu memanfaatkan sebagian besar dari 16-vCPU yang tersedia
Memilih konfigurasi berdasarkan tingkat konkurensi
- Saat jumlah koneksi sedikit, tidak ada pekerjaan yang bisa diparalelkan dan koneksi armada tersebar ke beberapa proses, sehingga satu proses bisa cukup atau sedikit lebih cepat
- Pada 8 klien, satu proses mencatat 8,910 TPS, sedangkan armada mencatat 6,450 TPS
- Saat konkurensi meningkat, satu core yang ditempati oleh satu proses menjadi batas throughput, dan selisih performa dengan armada makin besar
- Dalam lingkungan tempat Postgres mencapai batas lebih dulu daripada PgBouncer, satu proses juga merupakan default yang masuk akal
- Begitu pooler mulai membatasi throughput, bottleneck hanya bisa diatasi dengan menggabungkan armada proses yang disesuaikan dengan jumlah core, berbagi port berbasis
so_reuseport, dan peering antarporses - Semua server ClickHouse Managed Postgres menyediakan konfigurasi ini secara default
1 komentar
Komentar Hacker News
Cukup pakai https://github.com/yandex/odyssey. Odyssey adalah PgBouncer yang skalabel
Ada banyak pelanggan yang menskalakan koneksi PostgreSQL hingga lebih dari 10 ribu, dan ke depannya kami juga akan mengevaluasi alternatif seperti Odyssey atau pgdog. Namun secara pribadi saya tidak menyukai pendekatan menaruh lebih dari 10 ribu koneksi di PostgreSQL, dan menurut saya beberapa ratus koneksi saja sudah cukup untuk diskalakan
Menjalankan PgBouncer di Kubernetes memudahkan menjalankan beberapa proses di satu mesin, dan juga sederhana untuk mendistribusikannya ke beberapa mesin.
Azure sering menyebabkan gangguan berurutan di seluruh kelompok server saat pemeliharaan VM, jadi konfigurasi multi-mesin sangat berguna
Saya memahami konsep peering yang dibahas di sini, tetapi belum pernah memakainya di PostgreSQL.
A) Apakah ada mode atau pengaturan di PostgreSQL untuk mengonfigurasi ini dengan mudah? Saya membayangkan cancel request diteruskan secara berurutan ke peer sampai tidak ada error, atau cancel request memuat metadata yang memungkinkan proses yang salah menerima request itu mengidentifikasi proses yang benar.
B) Jika semua proses PostgreSQL menerima request klien dengan
so_reuseport, tampaknya komunikasi antar-peer memerlukan sarana inter-process communication (IPC) terpisah; mekanisme apa yang sebenarnya dipakai?Sepertinya implementasinya cukup sederhana jika ID proses PgBouncer dimasukkan ke dalam cancel key
Bisa dilihat mulai slide 26: https://www.pgevents.ca/events/pgconfdev2024/sessions/sessio...
Ada juga video presentasi dari Jelte, maintainer PgBouncer: https://www.youtube.com/watch?v=X-nCHcZ6vQU
Saya sedang memakai pgdog dan sangat cocok untuk kebutuhan saya
Saya penasaran mengapa PgBouncer sampai perlu peduli pada cancel request. Bukankah bisa diteruskan begitu saja ke PostgreSQL, lalu ketika PostgreSQL mengembalikan error alih-alih respons normal untuk query yang dibatalkan, PgBouncer yang menangani koneksi itu tinggal memprosesnya?
PgBouncer memberi klien PID cancel dan nilai rahasia yang palsu tetapi dapat dilacak; ketika cancel request datang kembali, PgBouncer mencari PID dan nilai rahasia dari server/proses sebenarnya lalu meneruskannya. Selain itu, bisa saja klien percaya query masih berjalan, sementara PgBouncer tahu query sudah selesai dan koneksi sudah dipakai ulang, jadi harus dipastikan apakah koneksi dan PID tersebut masih menjalankan query itu
Apakah ini dipakai untuk mengendalikan akses server PostgreSQL dari microservice melalui connection pool? Untuk backend monolitik, sepertinya tidak diperlukan.
Sebagian besar framework backend yang layak sudah memiliki connection pool bawaan, dan ini menyelesaikan 98% use case yang tidak membutuhkan dan tidak disarankan memakai microservice
PgBouncer adalah perangkat lunak yang hebat. Kami banyak memanfaatkannya, dan ini membuat operasi database jauh lebih sederhana
Saya baru tahu tentang
so_reuseport, menarik. Inti konfigurasinya tampaknya ini dan peering; apakah peering sudah bawaan di PgBouncer dan konfigurasinya juga mudah?Apakah peering juga bisa digunakan di Kubernetes? Di lingkungan ini seharusnya tidak perlu memakai ulang port, dan saya juga penasaran apakah tiap Pod akan memiliki connection pool sendiri dan berjalan secara independen
Sekarang saya berusia 46 tahun, dan masih ingat betapa terkejutnya saya saat umur 23 melihat model koneksi yang berat di PostgreSQL. Apakah sejak itu belum membaik?
Namun tanpa connection pool, PostgreSQL harus melakukan fork proses, jadi pembuatan koneksi baru selalu memakan biaya puluhan milidetik atau lebih. Aplikasi yang ditulis tanpa connection pool juga tidak ideal, tetapi sering ditemui.
Framework aplikasi juga sudah berubah, dan arsitektur serverless bisa membuat puluhan ribu koneksi yang mulai membuat PostgreSQL bermasalah. Secara pribadi saya tidak menyukai lebih dari beberapa ratus koneksi, tetapi sekarang ini situasi yang cukup realistis
Dalam proses memastikan kompatibilitas Java, banyak perusahaan harus mengubah besar-besaran cara mereka menangani konkurensi. PostgreSQL dan SQLite juga dirancang pada periode yang mirip, tetapi keduanya dibuat oleh para veteran industri yang sudah lama menerapkan sistem beban tinggi sebelum diskusi threading semacam ini muncul, sambil tetap mendukung pengguna perangkat keras lama