A:
There is no "magic number" of daily active users (DAU) because performance depends more on concurrency (active connections) than raw user counts.
However, you should consider PgBouncer when you hit these architectural bottlenecks:
1. The "Connection Per Request" Trap 🚦
If your Go application opens a new database connection for every incoming HTTP request, you will quickly exhaust Postgres’s max_connections (default is usually 100).
- The Threshold: If your app handles ~50–100 concurrent requests that hit the database, you will start seeing "too many clients already" errors.
- The Go nuance: Since Go’s
sql.DB is already a connection pool, you don't need PgBouncer if your app is a single instance. You need it when you scale out to multiple Go instances (e.g., running 5+ containers), as each instance maintains its own pool, collectively overwhelming Postgres.
2. When to deploy PgBouncer 🚀
- Multiple App Instances: If you have 3+ instances of your Go service, each with a
SetMaxOpenConns(20), you are already asking Postgres for 60 connections. If you scale to 10 instances, you hit 200, which is pushing the limits of Postgres's process-based architecture.
- High Latency/Idle Connections: If your Go app holds connections open longer than necessary, PgBouncer’s transaction pooling mode will allow you to multiplex thousands of app-side connections into a tiny set of actual Postgres backend processes.
Summary Checklist 📋
You should add PgBouncer if:
- You have multiple microservices/instances connecting to the same DB.
- You see "connection refused" or "too many clients" errors in your logs.
- Your
max_connections setting is under pressure (check SELECT count(*) FROM pg_stat_activity;).
- You want to perform zero-downtime DB restarts (PgBouncer can pause queries while the DB reboots).
Pro-tip: Start by tuning your Go db.SetMaxOpenConns() and db.SetMaxIdleConns(). If your Go app is a single binary and you aren't hitting the connection limit, you don't need PgBouncer yet. Keep it simple! 🛠️