Created
April 16, 2026 12:37
-
-
Save jirevwe/1738c90126405e145be7aacb19947eaa to your computer and use it in GitHub Desktop.
PGBouncer + Postgres dashboard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # PGBouncer + Postgres dashboard | |
| # | |
| # Usage: | |
| # ./pgbouncer_stats.sh # uses env vars or defaults | |
| # ./pgbouncer_stats.sh --watch # refresh every 5s | |
| # ./pgbouncer_stats.sh --watch 2 # refresh every 2s | |
| # | |
| # Environment variables (all optional): | |
| # PGB_HOST PGBouncer host (default: localhost) | |
| # PGB_PORT PGBouncer port (default: 6432) | |
| # PGB_USER PGBouncer admin user (default: postgres) | |
| # PGB_PASSWORD PGBouncer admin password (default: pg_password) | |
| # PG_HOST Postgres host (default: localhost) | |
| # PG_PORT Postgres port (default: 5432) | |
| # PG_USER Postgres user (default: convoy) | |
| # PG_PASSWORD Postgres password (default: pg_password) | |
| # PG_DATABASE Postgres database (default: convoy) | |
| set -euo pipefail | |
| # Config with defaults | |
| PGB_HOST="${PGB_HOST:-localhost}" | |
| PGB_PORT="${PGB_PORT:-6432}" | |
| PGB_USER="${PGB_USER:-postgres}" | |
| PGB_PASSWORD="${PGB_PASSWORD:-pg_password}" | |
| PG_HOST="${PG_HOST:-localhost}" | |
| PG_PORT="${PG_PORT:-6432}" | |
| PG_USER="${PG_USER:-convoy}" | |
| PG_PASSWORD="${PG_PASSWORD:-pg_password}" | |
| PG_DATABASE="${PG_DATABASE:-convoy}" | |
| # Auto-detect: use local psql if available, otherwise try docker | |
| PGB_DOCKER="${PGB_DOCKER:-local-pgbouncer-1}" | |
| PG_DOCKER="${PG_DOCKER:-local-postgres-1}" | |
| pgb_query() { | |
| if command -v psql &>/dev/null; then | |
| PGPASSWORD="$PGB_PASSWORD" psql -h "$PGB_HOST" -p "$PGB_PORT" -U "$PGB_USER" pgbouncer -t -A -F"|" -c "$1" 2>/dev/null | |
| else | |
| docker exec "$PGB_DOCKER" sh -c "PGPASSWORD=$PGB_PASSWORD psql -h 127.0.0.1 -p 6432 -U $PGB_USER pgbouncer -t -A -F\"|\" -c \"$1\"" 2>/dev/null | |
| fi | |
| } | |
| pg_query() { | |
| if command -v psql &>/dev/null; then | |
| PGPASSWORD="$PG_PASSWORD" psql -h "$PG_HOST" -p "$PG_PORT" -U "$PG_USER" -d "$PG_DATABASE" -t -A -c "$1" 2>/dev/null | |
| else | |
| docker exec "$PG_DOCKER" psql -U "$PG_USER" -d "$PG_DATABASE" -t -A -c "$1" 2>/dev/null | |
| fi | |
| } | |
| fmt() { printf "%'d" "$1" 2>/dev/null || echo "$1"; } | |
| fmt_us() { | |
| local us=$1 | |
| if [ "$us" -ge 1000000 ] 2>/dev/null; then | |
| echo "$((us / 1000000)).$((us % 1000000 / 1000))s" | |
| elif [ "$us" -ge 1000 ] 2>/dev/null; then | |
| echo "$((us / 1000))ms" | |
| else | |
| echo "${us}us" | |
| fi | |
| } | |
| run_once() { | |
| # Fetch stats | |
| POOLS=$(pgb_query "SHOW POOLS;" | grep -v "^pgbouncer" | head -1) | |
| STATS=$(pgb_query "SHOW STATS;" | grep -v "^pgbouncer" | head -1) | |
| PG_BACKENDS=$(pg_query "SELECT count(*), count(*) FILTER (WHERE state='active'), count(*) FILTER (WHERE state='idle') FROM pg_stat_activity WHERE backend_type='client backend';") | |
| PG_MAX=$(pg_query "SHOW max_connections;") | |
| if [ -z "$POOLS" ]; then | |
| echo "ERROR: Could not connect to PGBouncer at $PGB_HOST:$PGB_PORT" | |
| echo "Set PGB_HOST, PGB_PORT, PGB_USER, PGB_PASSWORD env vars." | |
| exit 1 | |
| fi | |
| if [ -z "$PG_BACKENDS" ]; then | |
| echo "ERROR: Could not connect to Postgres at $PG_HOST:$PG_PORT" | |
| echo "Set PG_HOST, PG_PORT, PG_USER, PG_PASSWORD, PG_DATABASE env vars." | |
| exit 1 | |
| fi | |
| # Parse PGBouncer pools | |
| CL_ACTIVE=$(echo "$POOLS" | cut -d'|' -f3) | |
| CL_WAITING=$(echo "$POOLS" | cut -d'|' -f4) | |
| SV_ACTIVE=$(echo "$POOLS" | cut -d'|' -f7) | |
| SV_IDLE=$(echo "$POOLS" | cut -d'|' -f10) | |
| MAXWAIT=$(echo "$POOLS" | cut -d'|' -f14) | |
| POOL_MODE=$(echo "$POOLS" | cut -d'|' -f16 | tr -d ' ') | |
| # Parse PGBouncer stats | |
| TOTAL_XCT=$(echo "$STATS" | cut -d'|' -f3) | |
| TOTAL_QUERY=$(echo "$STATS" | cut -d'|' -f4) | |
| AVG_QUERY_US=$(echo "$STATS" | cut -d'|' -f14) | |
| TOTAL_WAIT_US=$(echo "$STATS" | cut -d'|' -f9) | |
| # Parse Postgres | |
| PG_TOTAL=$(echo "$PG_BACKENDS" | cut -d'|' -f1) | |
| PG_ACTIVE=$(echo "$PG_BACKENDS" | cut -d'|' -f2) | |
| PG_IDLE=$(echo "$PG_BACKENDS" | cut -d'|' -f3) | |
| # Multiplexing ratio | |
| SV_TOTAL=$((SV_ACTIVE + SV_IDLE)) | |
| if [ "$SV_TOTAL" -gt 0 ] 2>/dev/null; then | |
| RATIO="$CL_ACTIVE clients -> $SV_TOTAL PG backends ($PG_MAX max)" | |
| else | |
| RATIO="$CL_ACTIVE clients -> $PG_TOTAL PG backends ($PG_MAX max)" | |
| fi | |
| echo "" | |
| echo "=== PGBouncer + Postgres Dashboard === ($(date '+%H:%M:%S'))" | |
| echo "" | |
| printf "%-38s %s\n" "Pool mode:" "$POOL_MODE" | |
| printf "%-38s %s\n" "Client connections (active):" "$CL_ACTIVE" | |
| printf "%-38s %s\n" "Client connections (waiting):" "$CL_WAITING" | |
| printf "%-38s %s\n" "Max wait (seconds):" "$MAXWAIT" | |
| printf "%-38s %s\n" "Server connections (active):" "$SV_ACTIVE" | |
| printf "%-38s %s\n" "Server connections (idle):" "$SV_IDLE" | |
| echo "" | |
| printf "%-38s %s\n" "Total transactions:" "$(fmt $TOTAL_XCT)" | |
| printf "%-38s %s\n" "Total queries:" "$(fmt $TOTAL_QUERY)" | |
| printf "%-38s %s\n" "Avg query time:" "$(fmt_us $AVG_QUERY_US)" | |
| printf "%-38s %s\n" "Total wait time:" "$(fmt_us $TOTAL_WAIT_US)" | |
| echo "" | |
| printf "%-38s %s\n" "Postgres backend connections:" "$PG_TOTAL" | |
| printf "%-38s %s\n" "Postgres active backends:" "$PG_ACTIVE" | |
| printf "%-38s %s\n" "Postgres idle backends:" "$PG_IDLE" | |
| printf "%-38s %s\n" "Postgres max_connections:" "$PG_MAX" | |
| echo "" | |
| printf "%-38s %s\n" "Connection multiplexing:" "$RATIO" | |
| echo "" | |
| } | |
| # Handle --watch mode | |
| if [ "${1:-}" = "--watch" ]; then | |
| INTERVAL="${2:-5}" | |
| while true; do | |
| clear | |
| run_once | |
| sleep "$INTERVAL" | |
| done | |
| else | |
| run_once | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment