So verhindern Sie max_connections-Fehler bei der Verbindung von Vercel mit AWS Aurora Postgres
Die meisten Tutorials für Serverless-Infrastrukturen zeigen nur, wie man ein paar Mal auf Verknüpfen klickt. Das Problem kommt danach: Sobald der Code bereitgestellt ist und auch nur ein wenig Traffic entsteht, bricht die DB-Verbindung zusammen. Sie geraten ins Schwitzen, weil Sie statische Access Keys verwalten müssen, oder Sie erhalten eine Rechnung mit mehreren hundert Dollar für Datenübertragungskosten.
Hier sind die praktischen Engpässe und Lösungen zusammengefasst, die bei der Anbindung eines Vercel-Frontends an ein AWS-Backend auftreten.
1. Setting up Connection Pooling for Vercel and AWS Aurora Postgres
PostgreSQL startet jedes Mal einen neuen Prozess, wenn sich ein Client verbindet. Jeder Prozess verbraucht mindestens 2 MB bis 8 MB Speicher. Da Vercel Serverless Functions bei eingehenden Anfragen zustandslose Instanzen unbegrenzt hochskalieren, wird das max_connections-Limit von Aurora Postgres (100–300 Verbindungen) innerhalb weniger Sekunden erreicht.
Schließlich erscheint der Fehler FATAL: sorry, too many clients already, die CPU-Auslastung der DB erreicht 100 % und der Dienst stürzt ab.
Sie müssen PgBouncer zwischen den Serverless Functions und Aurora platzieren und den Transaction Pooling-Modus aktivieren. Session Pooling hilft nicht, da es Verbindungen 1:1 hält, und Statement Pooling unterstützt keine Multi-Statement-Transaktionen. Transaction Pooling weist die DB-Verbindung nur für die Dauer der Abfragetransaktion zu und gibt sie sofort nach dem COMMIT an den Pool zurück.
Hier ist die Konfiguration für die Datei pgbouncer.ini:
`ini
[pgbouncer]
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 20
reserve_pool_size = 10
max_db_connections = 80
`
- Aktivieren Sie die Wiederverwendung auf Transaktionsebene mit
pool_mode = transaction.
- Setzen Sie
max_client_conn auf etwa 10000 und erhöhen Sie das ulimit -n-Limit des Betriebssystems.
default_pool_size ist die Anzahl der Verbindungen, die pro DB-Benutzer und DB-Paar zu Aurora offengehalten werden sollen. Setzen Sie diesen Wert zwischen 10 und 20.
- Wenn Sie
max_db_connections fest auf 80 einstellen, können Sie die tatsächliche Obergrenze der Verbindungen zur Aurora-Instanz erzwungen steuern.
In der Vercel Fluid Compute-Umgebung teilen sich mehrere Ausführungsaufrufe den globalen Scope. Daher müssen Sie den DB-Treiber-Pool im Modulscope deklarieren. Mit dem Paket @vercel/functions und attachDatabasePool werden ungenutzte Verbindungen sauber aufgeräumt, bevor die Funktionsinstanz beendet wird.
`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;
`
Auch der Connection String sollte nach Umgebung getrennt werden. In der lokalen Entwicklungsumgebung (.env.local) verbinden Sie sich direkt mit dem Aurora-Port (5432) oder verweisen auf den lokalen PgBouncer. In der Produktion (.env.production) hängen Sie den PgBouncer-Port (6432) und den Parameter pgbouncer=true an.
`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. Managing IAM Permissions with OIDC Instead of Access Keys
Das direkte Eintragen von AWS_ACCESS_KEY_ID und AWS_SECRET_ACCESS_KEY in die Vercel-Umgebungsvariablen ist gefährlich. Wenn die Schlüssel durchsickern, wird die gesamte Infrastruktur kompromittiert. Wenn Sie Administratorrechte wie AdministratorAccess vergeben haben, wird die Situation noch dramatischer.
Verwenden Sie OIDC (OpenID Connect). Dabei wird ein von Vercel ausgestelltes, signiertes JWT an die AssumeRoleWithWebIdentity-API von AWS STS gesendet, um temporäre Anmeldeinformationen mit einer Gültigkeit von 1 Stunde zu erhalten. Dadurch entfallen fest codierte Schlüssel komplett.
In der AWS IAM Role Vertrauensstellungspolitik sollten nur die Mindestrechte definiert werden:
`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"
}
}
}
]
}
`
- Registrieren Sie
[oidc.vercel.com/my-team-slug](https://oidc.vercel.com/my-team-slug) im IAM Identity Provider.
- Beschränken Sie den
aud-Claim auf Ihre Vercel-Team-URL, um den Zugriff anderer Organisationen zu verhindern.
- Zielen Sie mit der
sub-Claim-Bedingung exakt auf den Projektnamen (my-ai-app) und die Umgebung (production). Dies ist die Schlüsselkonfiguration zur Verhinderung von Confused-Deputy-Angriffen.
Auch die Berechtigungsrichtlinie sollte nur die erforderlichen Ressourcen freigeben. Hier ist ein Beispiel, das nur Zugriff auf einen bestimmten S3-Bucket und das Aufrufen einer Lambda-Funktion gewährt:
`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"
]
}
]
}
`
In Node.js-Code rufen Sie temporäre Anmeldeinformationen mit @vercel/oidc-aws-credentials-provider ab:
`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. Reducing API Latency Caused by Regional Differences
Wenn Ihr Vercel-Frontend an einem Tokyo PoP läuft und das AWS-Backend in US-East (us-east-1) liegt, kommen bei jeder Anfrage 150 ms bis 250 ms physikalische Latenz hinzu.
Es ist sinnvoll, API-Antworten, die sich nicht häufig ändern, auf Vercel Edge Middleware-Ebene zu cachen, damit der Backend-Aufruf gar nicht erst weitergeleitet wird.
`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;
}
`
Wenn Sie s-maxage=60, stale-while-revalidate=120 angeben, wird 60 Sekunden lang sofort aus dem Edge-Cache geantwortet. Nach Ablauf des Caches wird bis zu 120 Sekunden lang vorerst die alte Antwort geliefert, während der Cache im Hintergrund aktualisiert wird.
Um Tracing zwischen Diensten zu verbinden, müssen Sie den W3C TraceContext-Standard (traceparent-Header) von OpenTelemetry weiterleiten. Hier ist die Vercel-Konfiguration:
`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'],
},
},
});
}
`
Wenn Sie auf der AWS FastAPI-Seite ebenfalls einen OpenTelemetry-Empfänger einrichten, werden Frontend und Backend unter einer einzigen Trace-ID verbunden.
`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. Preventing Data Transfer Fees and Unlimited Scaling Costs
Kostenunfälle passieren gewöhnlich an zwei Stellen: Bei Vercels Zusatzgebühren für Fast Data Transfer (über 1 TB kostet jedes weitere GB $0,15) und bei AWS Data Transfer Out (GB für $0,09). Wenn sich dann noch der Traffic häuft und Serverless Functions sowie Lambda gleichzeitig hochskalieren, erreichen die Rechnungen völlig neue Dimensionen.
Hier ist ein Beispiel für eine API-Route, die den Vercel Spend Limit Webhook empfängt und eine Benachrichtigung an Slack sendet. Aus Sicherheitsgründen sollte eine HMAC SHA1-Signaturprüfung durchgeführt werden:
`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: Vercel Ausgaben-Warnung: Budgetlimit (${event.payload.spendAmount} USD) erreicht.,
}),
});
return new Response('OK', { status: 200 });
}
`
Aktivieren Sie unbedingt diese zwei Sicherheitsnetze:
- Aktivieren Sie Pause production deployment in den Vercel-Projekteinstellungen unter Settings > Billing. Selbst wenn der Dienst bei Erreichen des Budgetlimits stoppt, schützen Sie so Ihr Bankkonto.
- Begrenzen Sie die Reserved Concurrency (reservierte Gleichzeitigkeit) von AWS Lambda auf etwa 50. Dies verhindert Szenarien, in denen unendlich viele Instanzen entstehen, die Datenbank überlasten und gleichzeitig massive Kosten verursachen.