TuBrief
구독 채널
비디오
커뮤니티

Cómo evitar el error max_connections al conectar Vercel con AWS Aurora Postgres

TuBrief 편집팀
2026년 7월 21일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

Español한국어English中文العربيةहिन्दीDeutschFrançaisPortuguêsРусскийBahasa Indonesia日本語

관련 영상

Ship 26 NYC - Taller - Creación de una aplicación de IA de pila completa con Vercel y AWS33:34

Ship 26 NYC - Taller - Creación de una aplicación de IA de pila completa con Vercel y AWS

Vercel

커뮤니티의 다른 글

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

2026년 9월 13일

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

2026년 9월 13일

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

2026년 9월 13일

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

2026년 9월 13일

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Cómo evitar el error max_connections al conectar Vercel con AWS Aurora Postgres

La mayoría de los tutoriales de infraestructura serverless solo muestran un par de clics para conectar los servicios. El verdadero problema viene después. Una vez que despliegas el código y recibes un poco de tráfico, las conexiones a la base de datos se agotan, empiezas a sudar frío administrando Access Keys estáticas o te llega una factura con cientos de dólares por costos de transferencia de datos.

Hemos resumido los cuellos de botella prácticos y las soluciones al integrar el frontend de Vercel con el backend de AWS.

1. Configuración de Connection Pooling al conectar Vercel y AWS Aurora Postgres

PostgreSQL crea un nuevo proceso cada vez que un cliente se conecta. Cada proceso consume entre 2 MB y más de 8 MB de memoria. Dado que las funciones serverless de Vercel escalan instancias sin estado de forma ilimitada cuando entra una solicitud, pueden alcanzar el límite de max_connections de Aurora Postgres (de 100 a 300) en cuestión de segundos.

Al final, aparece el error FATAL: sorry, too many clients already, la CPU de la base de datos se dispara al 100% y el servicio se cae.

Es necesario colocar PgBouncer entre la función serverless y Aurora, y activar el modo Transaction Pooling. El Session Pooling mantiene las conexiones en una relación 1:1, por lo que no ayuda, y el Statement Pooling no permite transacciones con múltiples sentencias. El Transaction Pooling asigna una conexión a la base de datos únicamente mientras se ejecuta la transacción de la consulta y la devuelve al pool inmediatamente cuando se emite un COMMIT.

A continuación, se muestra la referencia de configuración para el archivo pgbouncer.ini.

`ini
[pgbouncer]
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 20
reserve_pool_size = 10
max_db_connections = 80

`

  • Activa el reúso a nivel de transacción con pool_mode = transaction.
  • Establece max_client_conn en alrededor de 10000 y aumenta el límite ulimit -n del sistema operativo.
  • default_pool_size es el número de conexiones que se mantendrán abiertas hacia Aurora por cada par usuario/base de datos. Ajusta este valor entre 10 y 20.
  • Al fijar max_db_connections en 80, puedes controlar de forma estricta el límite superior de conexiones reales que entran a la instancia de Aurora.

En el entorno Vercel Fluid Compute, múltiples ejecuciones comparten el scope global. Por lo tanto, debes declarar el pool del driver de la base de datos en el scope del módulo. Usar attachDatabasePool del paquete @vercel/functions garantiza que las conexiones inactivas se limpien correctamente antes de que la instancia de la función se apague.

`typescript
import { Pool } from 'pg';
import { attachDatabasePool } from '@vercel/functions';

const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
idleTimeoutMillis: 5000,
});

attachDatabasePool(pool);

export default pool;

`

La cadena de conexión también debe dividirse según el entorno. En el entorno de desarrollo local (.env.local), conéctate directamente al puerto de Aurora (5432) o apunta a un PgBouncer local. En producción (.env.production), utiliza el puerto de PgBouncer (6432) y añade el parámetro pgbouncer=true.

`bash

.env.local (Direct Connection)

DATABASE_URL="postgresql://dbuser:dbpassword@aurora-cluster.us-east-1.rds.amazonaws.com:5432/app_dev?sslmode=require"

.env.production (PgBouncer)

DATABASE_URL="postgresql://dbuser:dbpassword@pgbouncer-proxy.internal:6432/app_prod?sslmode=require&pgbouncer=true"

`

2. Gestión de permisos IAM mediante OIDC en lugar de Access Keys

Ingresar directamente AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY en las variables de entorno de Vercel es peligroso. Si las llaves se filtran, toda la infraestructura queda expuesta. La situación es aún más grave si se han otorgado permisos de administrador como AdministratorAccess.

Utiliza OIDC (OpenID Connect). Con este método, Vercel emite un JWT firmado que se envía a la API AssumeRoleWithWebIdentity de AWS STS para obtener credenciales temporales con validez de 1 hora. Esto elimina por completo la necesidad de llaves hardcodeadas.

La política de relación de confianza del rol de AWS IAM debe definir únicamente los privilegios mínimos necesarios.

`json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.vercel.com/my-team-slug"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.vercel.com/my-team-slug:aud": "https://vercel.com/my-team-slug",
"oidc.vercel.com/my-team-slug:sub": "owner:my-team-slug:project:my-ai-app:environment:production"
}
}
}
]
}

`

  • Registra [oidc.vercel.com/my-team-slug](https://oidc.vercel.com/my-team-slug) en el IAM Identity Provider.
  • Restringe el claim aud a la URL de tu equipo en Vercel para evitar que otras organizaciones accedan.
  • Especifica con precisión el nombre del proyecto (my-ai-app) y el entorno (production) mediante las condiciones del claim sub. Esta es la configuración clave para prevenir ataques de Confused Deputy.

La política de permisos también debe habilitar solo los recursos necesarios. A continuación se muestra un ejemplo que otorga acceso únicamente a un bucket de S3 específico y permiso de invocación para una función Lambda.

`json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictedS3BucketAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-production-ai-assets",
"arn:aws:s3:::my-production-ai-assets/*"
]
},
{
"Sid": "RestrictedLambdaInvocation",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"arn:aws:lambda:us-east-1:123456789012:function:python-ml-inference-service"
]
}
]
}

`

En el código de Node.js, utiliza @vercel/oidc-aws-credentials-provider para obtener credenciales temporales.

`typescript
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';

const credentials = awsCredentialsProvider({
roleArn: process.env.AWS_ROLE_ARN!,
});

const s3Client = new S3Client({ region: 'us-east-1', credentials });
const lambdaClient = new LambdaClient({ region: 'us-east-1', credentials });

export async function POST(req: Request) {
const body = await req.json();

await s3Client.send(new PutObjectCommand({
Bucket: 'my-production-ai-assets',
Key: inputs/${Date.now()}.json,
Body: JSON.stringify(body),
}));

const lambdaRes = await lambdaClient.send(new InvokeCommand({
FunctionName: 'python-ml-inference-service',
Payload: Buffer.from(JSON.stringify(body)),
}));

return Response.json({
status: 'success',
result: JSON.parse(Buffer.from(lambdaRes.Payload!).toString()),
});
}

`

3. Reducción de la latencia de las API debido a diferencias de región

Si el frontend de Vercel se ejecuta en un PoP de Tokio y el backend de AWS está en EE. UU. Este (us-east-1), se añadirá una latencia física de 150 ms a 250 ms en cada ida y vuelta de la solicitud.

Para las respuestas de la API que no cambian con frecuencia, lo ideal es almacenarlas en caché en el Edge Middleware de Vercel para evitar realizar la llamada al backend.

`typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export const config = {
matcher: ['/api/v1/ml-models/:path*'],
};

export function middleware(request: NextRequest) {
const response = NextResponse.next();

response.headers.set(
'Cache-Control',
'public, s-maxage=60, stale-while-revalidate=120'
);
response.headers.set(
'Vercel-CDN-Cache-Control',
's-maxage=300, stale-while-revalidate=600'
);

return response;
}

`

Al especificar s-maxage=60, stale-while-revalidate=120, la respuesta se entrega inmediatamente desde la caché Edge durante 60 segundos. Si la caché expira, se seguirá sirviendo la versión antigua durante los siguientes 120 segundos mientras la caché se actualiza en segundo plano.

Para conectar el rastreo entre servicios, se debe propagar el estándar W3C TraceContext (header traceparent) de OpenTelemetry. A continuación se muestra la configuración para Vercel.

`typescript
// instrumentation.ts
import { registerOTel } from '@vercel/otel';

export function register() {
registerOTel({
serviceName: 'vercel-frontend-service',
instrumentationConfig: {
fetch: {
propagateContextUrls: ['api.my-aws-backend.com', '*.amazonaws.com'],
},
},
});
}

`

En el lado de FastAPI de AWS, al agregar un receptor de OpenTelemetry del mismo modo, el frontend y el backend quedan vinculados mediante un único Trace ID.

`python
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4318/v1/traces"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

app = FastAPI(title="Python ML Service")
FastAPIInstrumentor.instrument_app(app)

@app.get("/api/v1/ml-models/predict")
async def predict():
tracer = trace.get_tracer(name)
with tracer.start_as_current_span("ml_inference_execution"):
return {"status": "completed", "prediction": [0.95, 0.05]}

`

4. Prevención de cargos por transferencia de datos y escalado ilimitado

Los imprevistos de costos suelen ocurrir principalmente en dos lugares: los cargos por exceso en Fast Data Transfer de Vercel (0.15porGBdespueˊsde1TB)ylastarifasdeDataTransferOutenAWS(0.15 por GB después de 1 TB) y las tarifas de Data Transfer Out en AWS (0.15porGBdespueˊsde1TB)ylastarifasdeDataTransferOutenAWS(0.09 por GB). Si a esto se suma un pico de tráfico donde las funciones serverless y Lambda comienzan a escalar simultáneamente, el orden de magnitud de la factura cambiará drásticamente.

A continuación se muestra un ejemplo de API Route para recibir un webhook de límite de gasto de Vercel y enviar una notificación a Slack. Debe incluir verificación de firma HMAC SHA1 por seguridad.

`typescript
import crypto from 'crypto';

export async function POST(req: Request) {
const payload = await req.text();
const signature = req.headers.get('x-vercel-signature');

const expectedSignature = crypto
.createHmac('sha1', process.env.VERCEL_SPEND_WEBHOOK_SECRET!)
.update(payload)
.digest('hex');

if (signature !== expectedSignature) {
return new Response('Invalid Signature', { status: 401 });
}

const event = JSON.parse(payload);

await fetch(process.env.SLACK_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: Alerta de gasto de Vercel: se ha alcanzado el límite del presupuesto (${event.payload.spendAmount} USD).,
}),
});

return new Response('OK', { status: 200 });
}

`

Asegúrate de activar estos dos mecanismos de seguridad:

  1. En tu proyecto de Vercel, ve a Settings > Billing y activa Pause production deployment. Esto protege el saldo de tu cuenta bancaria suspendiendo el servicio si se alcanza el límite de presupuesto.
  2. Limita la Reserved Concurrency (concurrencia reservada) de AWS Lambda a un valor cercano a 50. Esto evita que las instancias crezcan indefinidamente, sobrecarguen la base de datos y generen cargos masivos de manera simultánea.