Skip to content
This repository has been archived by the owner on Apr 11, 2024. It is now read-only.

topicusonderwijs/pgbouncer-ps-patch

Repository files navigation

GitHub license Generic badge

ℹ️ A massively inmproved version of prepared statement support is available in PgBouncer 1.21+

PgBouncer with prepared statement support

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.

Getting Started

Install

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/openssl@1.1/1.1.1t/

Configure

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.

Connect the application

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.

Running tests

./gradlew cleanTest test -info

How it works

PgBouncer has client connections (application ↔️ PgBouncer) and server connections (PgBouncer ↔️ PostgreSQL). Prepared statements received with a 'parse' command are stored on the receiving client connection together with a hash of the sql. Parse commands sent to PostgreSQL are modified to have a unique prepared statement name for the server connection. The server connection stores the sql hash and the prepared statement name unique to that server connection.

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.

Related work

An effort is being made to improve Postgres connection scalability: