AWS RDS PostgreSQL database

AWS RDS limit połączeń przekroczony: too many connections

Naprawa błędu too many connections w RDS PostgreSQL: identyfikacja idle connections, zakończenie wyciekających sesji i wdrożenie connection pooling z RDS Proxy lub PgBouncer.

·
Aplikacja nagle zaczyna odrzucać połączenia z bazą danych: „FATAL: too many connections". Nowe żądania nie przechodzą, istniejące sesje wiszą, a problem eskaluje z każdą sekundą. Ten runbook pokazuje jak odzyskać dostęp i zapobiec powtórzeniu.

Ten runbook opisuje wyczerpanie limitu połączeń RDS PostgreSQL. Aby zapewnić bieżące zarządzanie bazami danych i monitoring, zobacz CloudOps Maintenance. W kwestii architektury connection pooling umów konsultację.

Objaw

Logi aplikacji pokazują błędy połączeń. Użytkownicy doświadczają timeoutów lub błędów 500:

# Application error logs show:
# FATAL: too many connections for role "app_user"
# FATAL: remaining connection slots are reserved for non-replication superuser connections

# Check current connection count from a superuser session
# (if you can still connect via psql as superuser)
psql -h my-rds-instance.abcdefg.eu-west-1.rds.amazonaws.com \
  -U master_user -d mydb -c "SELECT count(*) FROM pg_stat_activity;"

# Check max_connections setting
psql -h my-rds-instance.abcdefg.eu-west-1.rds.amazonaws.com \
  -U master_user -d mydb -c "SHOW max_connections;"

# Check via CloudWatch if you can't connect at all
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name DatabaseConnections \
  --dimensions Name=DBInstanceIdentifier,Value=my-rds-instance \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 60 --statistics Maximum \
  --query 'Datapoints | sort_by(@, &Timestamp) | [-5:].[Timestamp,Maximum]'

Liczba połączeń osiąga lub zbliża się do max_connections. Nowe połączenia nie mogą zostać nawiązane.

Przyczyna

RDS PostgreSQL ma stały limit max_connections zależny od klasy instancji (np. db.t3.micro = 87, db.t3.medium = 150, db.r5.large = 1600). Połączenia wyczerpują się, gdy:

  • Wycieki połączeń: Kod aplikacji otwiera połączenia, ale nie zamyka ich na ścieżkach błędów lub przy timeoutach. Z czasem wyciekające połączenia się kumulują.
  • Brak connection pooling: Każda instancja aplikacji otwiera własną pulę połączeń. Przy autoscalingu nowe instancje przynoszą nowe pule, szybko wyczerpując limit.
  • Idle transactions: Długo trwające zapytania lub porzucone transakcje trzymają połączenia otwarte w nieskończoność.
  • Nagły skok ruchu: Gwałtowny wzrost żądań tworzy więcej połączeń niż baza danych jest w stanie obsłużyć, a po wysyceniu powstaje efekt kaskady.

Domyślna formuła max_connections dla RDS to LEAST({DBInstanceClassMemory/9531392}, 5000), co jest często niższe niż zespoły zakładają dla mniejszych klas instancji.

Rozwiązanie

A) Natychmiastowa ulga: zakończ idle connections:

# Connect as superuser (RDS master user)
psql -h my-rds-instance.abcdefg.eu-west-1.rds.amazonaws.com \
  -U master_user -d mydb

# Check connection breakdown by state and application
SELECT state, application_name, count(*)
FROM pg_stat_activity
GROUP BY state, application_name
ORDER BY count DESC;

# Identify idle connections older than 5 minutes
SELECT pid, usename, application_name, state,
       now() - state_change AS idle_duration
FROM pg_stat_activity
WHERE state = 'idle'
  AND now() - state_change > interval '5 minutes'
ORDER BY idle_duration DESC;

# Terminate idle connections (careful: this kills sessions)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
  AND now() - state_change > interval '5 minutes'
  AND usename != 'rdsadmin';

# Verify connections freed
SELECT count(*) FROM pg_stat_activity;

B) Wdróż connection pooling z RDS Proxy:

# Create RDS Proxy (via AWS CLI)
aws rds create-db-proxy \
  --db-proxy-name my-service-proxy \
  --engine-family POSTGRESQL \
  --auth Description="proxy auth",AuthScheme=SECRETS,SecretArn=arn:aws:secretsmanager:eu-west-1:123456789012:secret:rds-creds,IAMAuth=DISABLED \
  --role-arn arn:aws:iam::123456789012:role/rds-proxy-role \
  --vpc-subnet-ids subnet-abc123 subnet-def456 \
  --vpc-security-group-ids sg-12345678

# Register target (your RDS instance)
aws rds register-db-proxy-targets \
  --db-proxy-name my-service-proxy \
  --db-instance-identifiers my-rds-instance

# Update application connection string to point to the proxy endpoint
# Proxy endpoint: my-service-proxy.proxy-abcdefg.eu-west-1.rds.amazonaws.com

C) Dostosuj parametr max_connections (doraźne rozwiązanie):

# Check current parameter group
aws rds describe-db-instances \
  --db-instance-identifier my-rds-instance \
  --query 'DBInstances[0].DBParameterGroups[0].DBParameterGroupName'

# Modify max_connections (requires custom parameter group, not default)
aws rds modify-db-parameter-group \
  --db-parameter-group-name my-custom-pg \
  --parameters "ParameterName=max_connections,ParameterValue=300,ApplyMethod=pending-reboot"

# Reboot to apply (causes brief downtime)
aws rds reboot-db-instance --db-instance-identifier my-rds-instance

Prewencja: Zawsze używaj connection pooling w produkcji (RDS Proxy dla zarządzanego AWS, PgBouncer dla samodzielnego). Ustaw idle_in_transaction_session_timeout, aby automatycznie ubijać porzucone transakcje. Skonfiguruj alarmy CloudWatch na DatabaseConnections przy 80% wartości max_connections, aby wykrywać problemy zanim staną się awarią.

Walidacja

# 1. Confirm connections are below limit
psql -h my-rds-instance.abcdefg.eu-west-1.rds.amazonaws.com \
  -U master_user -d mydb -c \
  "SELECT count(*) as current, current_setting('max_connections')::int as max FROM pg_stat_activity;"
# Expected: current is well below max

# 2. Verify application can connect
curl -s https://my-app.example.com/health | jq '.database'
# Expected: "connected" or equivalent health check pass

# 3. Check no connection leak pattern in CloudWatch
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name DatabaseConnections \
  --dimensions Name=DBInstanceIdentifier,Value=my-rds-instance \
  --start-time $(date -u -d '30 minutes ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 300 --statistics Average
# Expected: stable connection count, not continuously rising

# 4. If using RDS Proxy, verify it's handling connections
aws rds describe-db-proxy-targets \
  --db-proxy-name my-service-proxy \
  --query 'Targets[0].TargetHealth'
# Expected: State = "AVAILABLE"

Jeśli aplikacja obsługuje żądania bez błędów połączeń i metryka CloudWatch jest stabilna, problem jest rozwiązany. Monitoruj przez 24 godziny, aby potwierdzić, że nie pojawia się powolny wzorzec wycieku.

Wyczerpany limit połączeń bazodanowych oznacza natychmiastowe odrzucanie nowych żądań. Dla produkcyjnych baz danych to awaria, która eskaluje z każdą sekundą: istniejący użytkownicy zaczynają doświadczać timeoutów, zadania w tle piętrzą się, a kolejka połączeń tworzy efekt thundering herd, gdy pojemność zostanie przywrócona. Bez connection pooling i alertów zespoły często odkrywają ten problem dopiero, gdy zgłaszają go klienci.

 

Jerzy Kopaczewski

Połączenia z bazą się wyczerpują?

Umów bezpłatną 30-minutową rozmowę. Przejrzymy konfigurację RDS i wdrożymy connection pooling, aby zapobiec przyszłym awariom.