Internal Pool Scaling vs. External Pooler Deployment: Choosing the Right Approach for ArgoCD Connection Exhaustion
0 reputation · 07 Jul 2020, 20:46 UTC
0 reputation · 07 Jul 2020, 20:46 UTC
In a highly‑available ArgoCD cluster, sync waves and frequent repository polling can trigger “pool exhausted” or “dial tcp” errors. The root cause is the mismatch between the per‑pod connection pools ArgoCD maintains for Redis and PostgreSQL and the hard limits those backends enforce.
ArgoCD exposes two documented mitigation paths: 1) tune the internal pool sizes via argocd-cm keys such as redis.connectionPoolSize and db.pool.maxOpenConnections; 2) add an external pooler (Redis Cluster or PgBouncer) to multiplex connections. Each path has trade‑offs: internal scaling is straightforward but risks hitting backend maxclients or max_connections; an external pooler adds operational overhead but can provide better observability and failover.
Currently ArgoCD does not expose pool utilization metrics, so operators infer exhaustion from logs or backend counters. The decision point is which approach delivers reliable performance without exceeding backend limits.
What metrics or instrumentation can be added to expose per‑pod pool usage? How can the optimal internal pool size be calculated given backend limits and expected traffic? Does enabling a PgBouncer transaction pool interfere with ArgoCD’s use of prepared statements, and what configuration is required to mitigate that?
35275 reputation · 08 Jul 2020, 04:50 UTC
Start by exposing per‑pod pool usage, calculate a safe internal pool size, tune ArgoCD via argocd-cm, and only add an external pooler (Redis Cluster or PgBouncer) if the tuned internal size still approaches backend limits.
ArgoCD creates a separate Redis and PostgreSQL connection pool inside each pod. During sync waves many pods open connections simultaneously, which can exceed the backend’s maxclients (Redis) or max_connections (PostgreSQL) even though each pod’s pool looks healthy.
redis.connectionPoolSize and db.pool.maxOpenConnections.kube-state-metrics with a custom query) that reads:PoolStats via redis.Client.PoolStats() → expose argocd_redis_pool_connections_used and argocd_redis_pool_capacity.pg_stat_activity via a simple SQL query (SELECT count(*) FROM pg_stat_activity WHERE application_name = 'argocd') → expose argocd_pg_connections_used.:8080/metrics and scrape with Prometheus.sum by (pod) (argocd_redis_pool_connections_used)sum by (pod) (argocd_pg_connections_used)Let:
W = expected concurrent sync waves (peak number of ArgoCD repo‑syncs running at once).C = average connections each wave opens to Redis/PostgreSQL (measure from a load test or from pg_stat_activity during a sync).P = number of ArgoCD pods (replicas).M = backend‑wide limit (maxclients for Redis, max_connections for PostgreSQL).S = safety margin (typically 0.2‑0.3).Formula:
optimal_per_pod_pool = ceil( (W * C) / P ) * (1 + S)
Then verify:
optimal_per_pod_pool * P < M
If the inequality fails, reduce W (throttle sync concurrency) or increase pod count P before raising the pool size.
If after tuning the internal pool you still observe:
M, oroptimal_per_pod_pool being far below the configured size.Deploy an external pooler:
redis-proxy (e.g., twemproxy) to shard connections.pool_mode = session in pgbouncer.ini.server_reset_query = DISCARD ALL (default in session mode) so that temporary state is cleared without discarding prepared statements.db.connectionParams = ?prepareThreshold=0 (or equivalent) and rely on PgBouncer to reset.PREPARE statements after each connection is taken from the pool.To confirm whether internal tuning suffices, I need the current backend limits: maxclients for Redis and max_connections for PostgreSQL. Please provide those values so I can verify the inequality optimal_per_pod_pool * P < M and decide if an external pooler is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.