Install any skill in seconds. Free to start, no credit card required.
Get Started Free →AWS Aurora Serverless v2, RDS Proxy, Data API, connection pooling
.claude/skills/alinaqi-aws-aurora/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 185% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 114% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 148% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 182% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 134% | 0% |
Amazon Aurora is a MySQL/PostgreSQL-compatible relational database with serverless scaling, high availability, and enterprise features.
Sources: Aurora Docs | Serverless v2 | RDS Proxy
Use RDS Proxy for serverless, Data API for simplicity, connection pooling always.
Aurora excels at ACID-compliant workloads. For serverless architectures (Lambda), always use RDS Proxy or Data API to handle connection management. Never open raw connections from Lambda functions.
| Option | Best For | |--------|----------| | Aurora Serverless v2 | Variable workloads, auto-scaling (0.5-128 ACUs) | | Aurora Provisioned | Predictable workloads, maximum performance | | Aurora Global | Multi-region, disaster recovery | | Data API | Serverless without VPC, simple HTTP access | | RDS Proxy | Connection pooling for Lambda, high concurrency |
Lambda → RDS Proxy → Aurora
(pool)Lambda → Data API (HTTP) → AuroraApp Server → Aurora
(persistent connection)typescript// CDK example import * as rds from 'aws-cdk-lib/aws-rds'; const proxy = new rds.DatabaseProxy(this, 'Proxy', { proxyTarget: rds.ProxyTarget.fromCluster(cluster), secrets: [cluster.secret!], vpc, securityGroups: [proxySecurityGroup], requireTLS: true, idleClientTimeout: cdk.Duration.minutes(30), maxConnectionsPercent: 90, maxIdleConnectionsPercent: 10, borrowTimeout: cdk.Duration.seconds(30) });
typescript// lib/db.ts import { Pool } from 'pg'; import { Signer } from '@aws-sdk/rds-signer'; const signer = new Signer({ hostname: process.env.RDS_PROXY_ENDPOINT!, port: 5432, username: process.env.DB_USER!, region: process.env.AWS_REGION! }); // IAM authentication async function getPool(): Promise<Pool> { const token = await signer.getAuthToken(); return new Pool({ host: process.env.RDS_PROXY_ENDPOINT, port: 5432, database: process.env.DB_NAME, user: process.env.DB_USER, password: token, ssl: { rejectUnauthorized: true }, max: 1, // Single connection for Lambda idleTimeoutMillis: 120000, connectionTimeoutMillis: 10000 }); } // Usage in Lambda let pool: Pool | null = null; export async function handler(event: any) { if (!pool) { pool = await getPool(); } const result = await pool.query('SELECT * FROM users WHERE id = $1', [event.userId]); return result.rows[0]; }
bash# Key settings for Lambda workloads MaxConnectionsPercent: 90 # Use most of DB connections MaxIdleConnectionsPercent: 10 # Keep some idle for bursts ConnectionBorrowTimeout: 30s # Wait for available connection IdleClientTimeout: 30min # Close idle proxy connections # Monitor these CloudWatch metrics: # - DatabaseConnectionsCurrentlyBorrowed # - DatabaseConnectionsCurrentlySessionPinned # - QueryDatabaseResponseLatency
bash# Must be Aurora Serverless aws rds modify-db-cluster \ --db-cluster-identifier my-cluster \ --enable-http-endpoint
bashnpm install data-api-client
typescript// lib/db.ts import DataAPIClient from 'data-api-client'; const db = DataAPIClient({ secretArn: process.env.DB_SECRET_ARN!, resourceArn: process.env.DB_CLUSTER_ARN!, database: process.env.DB_NAME!, region: process.env.AWS_REGION! }); // Simple query const users = await db.query('SELECT * FROM users WHERE active = :active', { active: true }); // Insert with returning const result = await db.query( 'INSERT INTO users (email, name) VALUES (:email, :name) RETURNING *', { email: 'user@test.com', name: 'Test User' } ); // Transaction const transaction = await db.transaction(); try { await transaction.query('UPDATE accounts SET balance = balance - :amount WHERE id = :from', { amount: 100, from: 1 }); await transaction.query('UPDATE accounts SET balance = balance + :amount WHERE id = :to', { amount: 100, to: 2 }); await transaction.commit(); } catch (error) { await transaction.rollback(); throw error; }
python# requirements.txt boto3>=1.34.0 # db.py import boto3 import os rds_data = boto3.client('rds-data') CLUSTER_ARN = os.environ['DB_CLUSTER_ARN'] SECRET_ARN = os.environ['DB_SECRET_ARN'] DATABASE = os.environ['DB_NAME'] def execute_sql(sql: str, parameters: list = None): """Execute SQL via Data API.""" params = { 'resourceArn': CLUSTER_ARN, 'secretArn': SECRET_ARN, 'database': DATABASE, 'sql': sql } if parameters: params['parameters'] = parameters return rds_data.execute_statement(**params) def get_user(user_id: int): result = execute_sql( 'SELECT * FROM users WHERE id = :id', [{'name': 'id', 'value': {'longValue': user_id}}] ) return result.get('records', []) def create_user(email: str, name: str): result = execute_sql( 'INSERT INTO users (email, name) VALUES (:email, :name) RETURNING *', [ {'name': 'email', 'value': {'stringValue': email}}, {'name': 'name', 'value': {'stringValue': name}} ] ) return result.get('generatedFields') # Transaction def transfer_funds(from_id: int, to_id: int, amount: float): transaction = rds_data.begin_transaction( resourceArn=CLUSTER_ARN, secretArn=SECRET_ARN, database=DATABASE ) transaction_id = transaction['transactionId'] try: execute_sql( 'UPDATE accounts SET balance = balance - :amount WHERE id = :id', [ {'name': 'amount', 'value': {'doubleValue': amount}}, {'name': 'id', 'value': {'longValue': from_id}} ] ) execute_sql( 'UPDATE accounts SET balance = balance + :amount WHERE id = :id', [ {'name': 'amount', 'value': {'doubleValue': amount}}, {'name': 'id', 'value': {'longValue': to_id}} ] ) rds_data.commit_transaction( resourceArn=CLUSTER_ARN, secretArn=SECRET_ARN, transactionId=transaction_id ) except Exception as e: rds_data.rollback_transaction( resourceArn=CLUSTER_ARN, secretArn=SECRET_ARN, transactionId=transaction_id ) raise e
bashnpm install prisma @prisma/client npx prisma init
prisma// prisma/schema.prisma generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id Int @id @default(autoincrement()) email String @unique name String posts Post[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) author User @relation(fields: [authorId], references: [id]) authorId Int createdAt DateTime @default(now()) }
bash# Use RDS Proxy endpoint DATABASE_URL="postgresql://user:password@proxy-endpoint.proxy-xxx.region.rds.amazonaws.com:5432/mydb?schema=public&connection_limit=1"
typescript// handlers/users.ts import { PrismaClient } from '@prisma/client'; // Reuse client across invocations let prisma: PrismaClient | null = null; function getPrisma(): PrismaClient { if (!prisma) { prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL } } }); } return prisma; } export async function handler(event: any) { const db = getPrisma(); const users = await db.user.findMany({ include: { posts: true }, take: 10 }); return { statusCode: 200, body: JSON.stringify(users) }; }
typescript// CDK const cluster = new rds.DatabaseCluster(this, 'Cluster', { engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_15_4 }), serverlessV2MinCapacity: 0.5, // Minimum ACUs serverlessV2MaxCapacity: 16, // Maximum ACUs writer: rds.ClusterInstance.serverlessV2('writer'), readers: [ rds.ClusterInstance.serverlessV2('reader', { scaleWithWriter: true }) ], vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } });
| Workload | Min ACUs | Max ACUs | |----------|----------|----------| | Dev/Test | 0.5 | 2 | | Small Production | 2 | 8 | | Medium Production | 4 | 32 | | Large Production | 8 | 128 |
typescript// Data API Client v2 handles this automatically // For direct connections, implement retry logic: import { Pool } from 'pg'; async function queryWithRetry( pool: Pool, sql: string, params: any[], maxRetries = 3 ): Promise<any> { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await pool.query(sql, params); } catch (error: any) { // Aurora Serverless waking up if (error.code === 'ETIMEDOUT' || error.message?.includes('Communications link failure')) { if (attempt === maxRetries) throw error; // Exponential backoff await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); continue; } throw error; } } }
bash# Development (creates migration) npx prisma migrate dev --name add_users_table # Production (apply migrations) npx prisma migrate deploy # Generate client npx prisma generate
yaml# .github/workflows/deploy.yml - name: Run migrations run: | # Connect via bastion or use a migration Lambda npx prisma migrate deploy env: DATABASE_URL: ${{ secrets.DATABASE_URL }}
typescript// lambdas/migrate.ts import { execSync } from 'child_process'; export async function handler() { try { execSync('npx prisma migrate deploy', { env: { ...process.env, DATABASE_URL: process.env.DATABASE_URL }, stdio: 'inherit' }); return { statusCode: 200, body: 'Migrations applied' }; } catch (error) { console.error('Migration failed:', error); throw error; } }
yaml# docker-compose.yml services: app: build: . environment: DATABASE_URL: postgresql://user:pass@pgbouncer:6432/mydb pgbouncer: image: edoburu/pgbouncer environment: DATABASE_URL: postgresql://user:pass@aurora-endpoint:5432/mydb POOL_MODE: transaction MAX_CLIENT_CONN: 1000 DEFAULT_POOL_SIZE: 20
typescript// For long-running servers (not Lambda) import { Pool } from 'pg'; const pool = new Pool({ host: process.env.DB_HOST, port: 5432, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, max: 20, // Max connections idleTimeoutMillis: 30000, // Close idle after 30s connectionTimeoutMillis: 10000 }); // Use pool for all queries export async function query(sql: string, params?: any[]) { const client = await pool.connect(); try { return await client.query(sql, params); } finally { client.release(); } }
# Aurora
- CPUUtilization
- DatabaseConnections
- FreeableMemory
- ServerlessDatabaseCapacity (ACUs)
- AuroraReplicaLag
# RDS Proxy
- DatabaseConnectionsCurrentlyBorrowed
- DatabaseConnectionsCurrentlySessionPinned
- QueryDatabaseResponseLatency
- ClientConnectionsReceivedbash# Enable via console or CLI aws rds modify-db-cluster \ --db-cluster-identifier my-cluster \ --enable-performance-insights \ --performance-insights-retention-period 7
typescriptimport { Signer } from '@aws-sdk/rds-signer'; const signer = new Signer({ hostname: process.env.DB_HOST!, port: 5432, username: 'iam_user', region: 'us-east-1' }); const token = await signer.getAuthToken(); // Use token as password (valid for 15 minutes) const pool = new Pool({ host: process.env.DB_HOST, user: 'iam_user', password: token, ssl: true });
typescriptimport { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'; const client = new SecretsManagerClient({ region: 'us-east-1' }); async function getDbCredentials() { const response = await client.send( new GetSecretValueCommand({ SecretId: process.env.DB_SECRET_ARN }) ); return JSON.parse(response.SecretString!); }
bash# Cluster operations aws rds describe-db-clusters aws rds create-db-cluster --engine aurora-postgresql --db-cluster-identifier my-cluster aws rds delete-db-cluster --db-cluster-identifier my-cluster --skip-final-snapshot # Serverless v2 aws rds modify-db-cluster \ --db-cluster-identifier my-cluster \ --serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=16 # Data API aws rds-data execute-statement \ --resource-arn $CLUSTER_ARN \ --secret-arn $SECRET_ARN \ --database mydb \ --sql "SELECT * FROM users" # Proxy aws rds describe-db-proxies aws rds create-db-proxy --db-proxy-name my-proxy --engine-family POSTGRESQL ... # Snapshots aws rds create-db-cluster-snapshot --db-cluster-identifier my-cluster --db-cluster-snapshot-identifier backup-1 aws rds restore-db-cluster-from-snapshot --db-cluster-identifier restored --snapshot-identifier backup-1
max: 1 for Lambda, use pooling for serversOther measured skills in the registry, with their headline benchmark lift.