ℹ️ A massively inmproved version of prepared statement support is available in PgBouncer 1.21+
Unfortunately PgBouncer does not support prepared statements in transaction pooling mode, as stated in the FAQ:
To make prepared statements work in this mode would need PgBouncer to keep track of them internally, which it does not do. So the only way to keep using PgBouncer in this mode is to disable prepared statements in the client.
This patch introduces prepared statement tracking, allowing to run PgBouncer in transaction pooling mode with server side prepared statements. It only supports the extended query protocol parse, bind, execute and close message formats. E.g. the flow used by the PostgreSQL JDBC driver to create server prepared statements.
Download and install pgbouncer-ps by running the following commands (RHEL/CentOS):
# install required packages - see https://github.com/pgbouncer/pgbouncer#building
sudo yum install libevent-devel openssl-devel python-devel libtool git patch make -y
# download the latest tested pgbouncer distribution - 1.19
git clone https://github.com/pgbouncer/pgbouncer.git --branch "pgbouncer_1_19_0"
# download pgbouncer-ps extensions - tag v1.19.0+1
git clone https://github.com/topicusonderwijs/pgbouncer-ps-patch.git
# merge pgbouncer-ps extensions into pgbouncer code
cd pgbouncer-ps-patch
./install-pgbouncer-ps-patch.sh ../pgbouncer
# build and install
cd ../pgbouncer
git submodule init
git submodule update
./autogen.sh
./configure
make
sudo make install
MacOS instructions:
# Run configure on MaxOS with homebrew
./configure --prefix=/usr/local --with-openssl=/opt/homebrew/Cellar/[email protected]/1.1.1t/
Create a configuration file, using ./etc/pgbouncer.ini
as a starting point.
By default, prepared statement support is not enabled. To enable it just add the following to your pgbouncer-ps configuration:
disable_prepared_statement_support = 0
The number of prepared statements kept active on a single backend connection is defined by the following configuration:
prepared_statement_cache_queries = 100
Note: keep in mind that this will increase the memory footprint of each client connection on your PostgreSQL server.
Tracking prepared statements does not only come with a memory cost, but also with increased cpu usage. Multiple PgBouncer instances can be run to use more than one core for processing, see so_reuseport
socket option.
Configure your client application as though you were connecting directly to a PostgreSQL database.
Example - JDBC driver
jdbc:postgresql://pgbouncer-ps:6432/postgres?prepareThreshold=10&preparedStatementCacheQueries=512&preparedStatementCacheSizeMiB=10
Note: Cached prepared statements by the JDBC driver will increase the memory footprint of each JDBC connection in your application and each frontend connection in PgBouncer.
./gradlew cleanTest test -info
PgBouncer has client connections (application
Whenever a client connection receives a command involving a prepared statement, it does a lookup to get the prepared statement metadata (sql sent by the parse command and the sql hash). The server connection maps this sql hash to the prepared statement name unique to that server connection and modifies the prepared statement name before sending the command to PostgreSQL. If the sql hash cannot be found for the server connection, this means we are on a different server connection than the one we originally issued the parse command on. In this case, a parse command is generated and sent first.
An effort is being made to improve Postgres connection scalability:
- Improving Postgres Connection Scalability: Snapshots by Microsoft
- Postgres mailing list thread
- Postgres 14 release notes
Improve the speed of computing MVCC visibility snapshots on systems with many CPUs and high session counts (Andres Freund)
This also improves performance when there are many idle sessions.