cnx/laravel-pgbouncer-reconnect

Resets pgBouncer server connections after migrations that change the database schema

Maintainers

Package info

github.com/cnxapp/laravel-pgbouncer-reconnect

pkg:composer/cnx/laravel-pgbouncer-reconnect

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-08 08:56 UTC

This package is auto-updated.

Last update: 2026-08-09 15:35:28 UTC


README

Resets pgBouncer server connections after migrations that change the database schema.

The problem

PostgreSQL caches query plans together with the shape of their result — how many columns and of which types. ALTER TABLE ... ADD COLUMN changes that shape, which makes every cached plan for the table invalid:

SQLSTATE[0A000] ERROR: cached plan must not change result type

Normally this resolves itself: the connection is re-established and the plan is rebuilt. Behind pgBouncer it does not. Plans live on the pooled server connections, which outlive application restarts, and server_lifetime only recycles a connection once it goes idle — under load it never does.

The result is an application failing every query against the changed tables long after the migration succeeded, until the pool is reset by hand.

This package resets it automatically: once migrations have run, it issues RECONNECT on the pgBouncer admin console.

Installation

composer require cnx/laravel-pgbouncer-reconnect

The service provider is registered through package discovery — no application code changes are needed.

Publishing the config is optional:

php artisan vendor:publish --tag=pgbouncer-reconnect-config

How it works

  • Listens for MigrationsEnded and acts only when migrations were actually applied. A deploy without migrations leaves live connections untouched.
  • Connects to the pgbouncer admin database on the same host and port as the application, reusing the same credentials. No extra secret is required as long as the application role is listed in admin_users.
  • RECONNECT does not drop client connections: pgBouncer closes each server connection as it becomes idle, i.e. after the current transaction finishes.

Environments without pgBouncer

Detected automatically — nothing to configure. A plain PostgreSQL server has no pgbouncer database, so the connection fails immediately, a debug message is recorded and execution continues.

This is the normal path for environments that talk to PostgreSQL directly, for local development and for tests.

Configuration

Variable Default Purpose
PGBOUNCER_RECONNECT_ENABLED true Disable the behaviour explicitly
PGBOUNCER_RECONNECT_CONNECTION default connection Name of a connection from database.connections

pgBouncer requirements

The role your application connects with must be listed in admin_users:

admin_users = my_app_role

Otherwise RECONNECT is rejected for lack of privileges — the package logs a warning and carries on without failing the deploy.

Design notes

Never throws. The reset runs after migrations have already been applied. A failed reset is a warning, not a reason to turn a successful migration into a failed deploy.

PDO::exec(), never prepare(). The pgBouncer admin console does not speak the extended query protocol:

extended query protocol not supported by admin console

That rules out DB::statement(), which prepares its queries. A dedicated PDO connection using exec() is required. Verified against pgBouncer 1.23 and 1.25.

The core is framework-agnostic. PoolReset takes a connection config array and a PSR-3 logger, so it is testable without booting a framework. Laravel integration lives entirely in the service provider.

Compatibility

  • PHP 8.1+
  • Laravel 10, 11, 12
  • PostgreSQL behind pgBouncer in any pool mode

Tests

composer install
vendor/bin/phpunit

License

Proprietary.