Amazon RDS Proxy: Connection Pooling for Coding-Agent Database Tools

0 views

Your coding agent’s query_schema tool just opened a new Postgres connection from every Lambda concurrent execution — fifty tenants, three tools each, and RDS max_connections is already rejecting logins while idle sockets pile up in abandoned chat turns. Amazon RDS Proxy is the unfair advantage for agent DB tools: one managed pool in front of Aurora/RDS, IAM auth, and failover that does not force every tool to reinvent pooling. Pair with Secrets Manager rotation for DB secrets and DynamoDB Transactions when the ledger is NoSQL — this post is the relational connection-pooling layer for coding-agent tools.

⚡ TL;DR: Put RDS Proxy between agent runtimes (Lambda, Fargate Spot, CodeBuild) and Aurora/RDS. Prefer IAM DB auth or short-lived Secrets Manager creds; avoid session pinning unless the tool needs temp tables or SET LOCAL. Cap Proxy client connections and RDS max_connections with Budgets watching Proxy CU. Related: Fargate Spot sandboxes, IAM condition keys, SCPs.

Why agent DB tools thrash RDS without a proxy

Coding-agent database tools are bursty, concurrent, and forgetful:

  1. Planner calls describe_table / run_readonly_sql / apply_migration_preview
  2. Runtime opens a connection, runs one statement, returns JSON
  3. Chat turn ends — connection may linger until GC or Lambda freeze
  4. Next turn may land on a different execution environment

Without a proxy you get connection storms on every traffic spike, slow failovers during Aurora patching, and credentials baked into every sandbox image. RDS Proxy multiplexes many client connections onto fewer DB connections and handles failover for you.

Approach Connection storms Failover UX Best agent shape
Direct RDS from Lambda ❌ Easy to melt ❌ Clients reconnect blindly Tiny single-tenant tools
App-level pool in Fargate ✅ If one long process ⚠️ DIY Always-on IDE backends
RDS Proxy ✅ Multiplexed ✅ Transparent Burst tool fleets
DynamoDB / MemoryDB N/A Managed Session/ledger, not SQL schema

Wire Proxy into agent runtimes

Create a proxy targeting Aurora PostgreSQL (or MySQL), put it in private subnets, and point tools at the proxy endpoint — never at the cluster writer DNS from agent code.

typescript
// ✅ CDK sketch: RDS Proxy for coding-agent SQL tools
import * as rds from "aws-cdk-lib/aws-rds";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";

const dbSecret = secretsmanager.Secret.fromSecretNameV2(this, "DbSecret", "agent/db/app");

const proxy = new rds.DatabaseProxy(this, "AgentDbProxy", {
  proxyTarget: rds.ProxyTarget.fromCluster(auroraCluster),
  secrets: [dbSecret],
  vpc,
  requireTLS: true,
  iamAuth: true,
  maxConnectionsPercent: 80,
  maxIdleConnectionsPercent: 25,
  // ✅ borrow timeout beats hanging agent tools
  // (engine-specific knobs via CfnDBProxyTargetGroup)
  securityGroups: [proxySg],
  dbProxyName: "coding-agent-sql",
});

// Lambda tool role: connect via IAM auth to proxy
toolFn.addToRolePolicy(
  new iam.PolicyStatement({
    actions: ["rds-db:connect"],
    resources: [
      `arn:aws:rds-db:${region}:${account}:dbuser:${proxy.dbProxyArn}/*`,
    ],
    conditions: {
      StringEquals: { "aws:PrincipalTag/tenant_id": "${aws:PrincipalTag/tenant_id}" },
    },
  })
);
python
# ✅ tool handler: connect through Proxy with IAM token (psycopg)
import boto3, psycopg

def open_agent_conn(tenant_id: str):
    rds = boto3.client("rds")
    token = rds.generate_db_auth_token(
        DBHostname=PROXY_HOST,
        Port=5432,
        DBUsername="agent_ro",
        Region=REGION,
    )
    # ❌ Never reuse a global connection across tenants
    return psycopg.connect(
        host=PROXY_HOST,
        user="agent_ro",
        password=token,
        dbname="app",
        sslmode="require",
        connect_timeout=5,
        options=f"-c statement_timeout=8000 -c application_name=agent:{tenant_id}",
    )

Rule: one tool invocation → one short-lived client connection through Proxy. Do not hold sockets across WebSocket chat turns (API Gateway WebSockets sessions are not DB sessions).

Session pinning: when it helps and when it hurts

RDS Proxy pins a client to a DB connection when the session holds state the pool cannot safely share (temp tables, SET, prepared statements in some engines, explicit transactions left open).

For coding agents:

  • ✅ Prefer stateless, auto-commit, read-only tools — no pin, max multiplexing
  • ✅ Migrations / DDL previews: run in a dedicated sandbox DB or short pinned transaction, then close
  • ❌ Leaving BEGIN open while the LLM “thinks” for 40 seconds — pin + idle timeout = failures
sql
-- ✅ agent tool: single statement, statement_timeout enforced
SET statement_timeout = '8s';
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = $1;
python
# ❌ pinned forever: agent opens txn, streams tokens, never commits
conn = open_agent_conn(tenant)
cur = conn.cursor()
cur.execute("BEGIN")
cur.execute("CREATE TEMP TABLE preview AS SELECT * FROM orders LIMIT 100")
# ... LLM waits for user confirmation for minutes ...

IAM, secrets, and blast radius

json
{
  "Effect": "Deny",
  "Action": ["rds:ModifyDBProxy", "rds:DeleteDBProxy", "rds:CreateDBInstance"],
  "Resource": "*",
  "Condition": {
    "StringEquals": { "aws:PrincipalTag/workload": "coding-agent" }
  }
}

Observability and capacity

Watch Proxy ClientConnections, DatabaseConnectionsPercent, QueryDatabaseResponseLatency. Spike + pin rate rising usually means a tool left transactions open. Emit EMF with tenant_id (CloudWatch EMF pattern) so one noisy tenant cannot hide behind fleet averages. Cap spend with Budgets + Cost Anomaly on RDS + Proxy CU.

Symptom Likely cause Fix
too many connections on RDS Proxy max % too high or pin storm Lower maxConnectionsPercent; fix open txns
Tool timeouts after failover Clients cached old writer IP Always use Proxy endpoint DNS
IAM auth failures Clock skew / wrong resource ARN Fix rds-db:connect ARN to proxy resource id
Cross-tenant data leak Shared app user + no RLS Per-tenant DB user or Postgres RLS + SET ROLE

Production checklist

  • [ ] Agent tools connect only to Proxy endpoint (TLS required)
  • [ ] IAM DB auth or rotating Secrets Manager creds — no static passwords in images
  • [ ] statement_timeout / idle_in_transaction timeouts set server-side
  • [ ] Read-only tools use a least-privilege DB user; DDL gated behind Cedar + kill switch (AppConfig)
  • [ ] Security groups: Lambda/Fargate → Proxy → RDS only; no public Proxy
  • [ ] Alarms on DatabaseConnectionsPercent and ClientConnections
  • [ ] Failover drill documented; tools retry idempotently (tool schemas)
  • [ ] Ledger/session state stays in DynamoDB/MemoryDB — Proxy is for relational schema tools

FAQ

Q: Do I need Proxy if agents only hit DynamoDB?
A: No — this pattern is for Aurora/RDS SQL tools. Keep transactional ledgers on DynamoDB Transactions.

Q: Can Fargate Spot sandboxes use Proxy?
A: Yes — short-lived clients are exactly what pools love. Just do not pin across Spot SIGTERM (Fargate Spot).

Q: Proxy vs PgBouncer on ECS?
A: Self-managed pools work until failover and IAM auth become your weekend job. Proxy is the managed default for agent fleets on AWS.

RDS Proxy turns coding-agent SQL tools from a connection storm into a multiplexed, IAM-aware path. Keep tools stateless, timeouts tight, and credentials short-lived — then let Aurora scale without drowning in idle sockets.

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.