Vercel से AWS Aurora Postgres कनेक्ट करते समय max_connections एरर को कैसे रोकें
सर्वरलेस इन्फ्रास्ट्रक्चर ट्यूटोरियल ज्यादातर बस कुछ ही बार कनेक्ट बटन दबाने की प्रक्रिया दिखाते हैं। असली समस्या उसके बाद आती है। कोड डिप्लॉय करने और ट्रैफिक थोड़ा सा भी बढ़ते ही DB कनेक्शन क्रैश हो जाता है, स्टैटिक Access Key मैनेज करते समय पसीने छूट जाते हैं, या डेटा ट्रांसफर कॉस्ट के नाम पर कई सौ डॉलर का इनवॉइस मिल जाता है।
यहाँ Vercel फ्रंटएंड और AWS बैकएंड को एकीकृत करते समय आने वाली व्यावहारिक बाधाओं और उनके समाधानों को संक्षेप में दिया गया है।
1. Vercel और AWS Aurora Postgres कनेक्ट करते समय कनेक्शन पुलिंग सेट करना
PostgreSQL हर बार क्लाइंट के कनेक्ट होने पर एक नया प्रोसेस शुरू करता है। प्रति प्रोसेस यह 2MB से लेकर 8MB या उससे अधिक मेमोरी लेता है। चूंकि अनुरोध आने पर Vercel सर्वरलेस फ़ंक्शन असीमित रूप से स्टेटलेस इंस्टेंस बढ़ाते हैं, इसलिए यह कुछ ही सेकंड में Aurora Postgres की max_connections सीमा (100~300) को भर देता है।
अंततः, FATAL: sorry, too many clients already एरर आता है, DB CPU उपयोग 100% तक पहुँच जाता है और सर्विस बंद हो जाती है।
आपको सर्वरलेस फ़ंक्शंस और Aurora के बीच PgBouncer रखना होगा और ट्रांजैक्शन पुलिंग (Transaction Pooling) मोड चालू करना होगा। सेशन पुलिंग कनेक्शन को 1:1 पर रखता है इसलिए वह मददगार नहीं है, और स्टेटमेंट पुलिंग मल्टी-स्टेटमेंट ट्रांजैक्शंस का उपयोग नहीं कर सकती है। ट्रांजैक्शन पुलिंग केवल उसी समय के लिए DB कनेक्शन आवंटित करती है जब तक क्वेरी ट्रांजैक्शन निष्पादित हो रहा होता है और COMMIT होते ही इसे तुरंत पुल में वापस कर देती है।
यह pgbouncer.ini फ़ाइल के लिए कॉन्फ़िगरेशन मानक है।
`ini
[pgbouncer]
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 20
reserve_pool_size = 10
max_db_connections = 80
`
pool_mode = transaction के साथ ट्रांजैक्शन स्तर पर पुन: उपयोग को सक्षम करें।
max_client_conn को लगभग 10000 पर सेट करें और OS की ulimit -n सीमा बढ़ाएँ।
default_pool_size प्रति DB उपयोगकर्ता और DB जोड़ी में Aurora के लिए खुले रखे जाने वाले कनेक्शनों की संख्या है। इसे 10 से 20 के बीच रखें।
max_db_connections को ठीक 80 पर सेट करने से आप Aurora इंस्टेंस में जाने वाले वास्तविक कनेक्शनों की ऊपरी सीमा को जबरन नियंत्रित कर सकते हैं।
Vercel Fluid Compute वातावरण में, कई निष्पादन आह्वान (execution calls) वैश्विक स्कोप को साझा करते हैं। इसलिए, आपको मॉड्यूल स्कोप में DB ड्राइवर पुल घोषित करना होगा। @vercel/functions पैकेज के attachDatabasePool का उपयोग करने से फ़ंक्शन इंस्टेंस बंद होने से पहले निष्क्रिय कनेक्शन साफ हो जाते हैं।
`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;
`
कनेक्शन स्ट्रिंग को भी वातावरण के अनुसार अलग किया जाना चाहिए। स्थानीय विकास वातावरण (.env.local) में, सीधे Aurora पोर्ट (5432) से कनेक्ट करें या लोकल PgBouncer की ओर पॉइंट करें, और प्रोडक्शन (.env.production) में, PgBouncer पोर्ट (6432) और 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. Access Key के बजाय OIDC के साथ IAM अनुमतियाँ प्रबंधित करना
AWS_ACCESS_KEY_ID और AWS_SECRET_ACCESS_KEY को सीधे Vercel एनवायरनमेंट वेरिएबल्स में हार्डकोड करना खतरनाक है। यदि की (key) लीक हो जाती है, तो पूरा इन्फ्रास्ट्रक्चर प्रभावित हो सकता है। यदि आपने AdministratorAccess जैसी एडमिन अनुमतियाँ दी हैं, तो स्थिति और भी गंभीर हो जाती है।
OIDC (OpenID Connect) का उपयोग करें। यह Vercel द्वारा जारी किए गए हस्ताक्षरित JWT को AWS STS के AssumeRoleWithWebIdentity API पर भेजकर 1 घंटे का अस्थायी क्रेडेंशियल प्राप्त करने का एक तरीका है। हार्डकोडेड की (key) पूरी तरह से समाप्त हो जाती है।
AWS IAM Role ट्रस्ट रिलेशनशिप पॉलिसी में केवल न्यूनतम अनुमतियाँ ही परिभाषित की जानी चाहिए।
`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"
}
}
}
]
}
`
- IAM Identity Provider में
[oidc.vercel.com/my-team-slug](https://oidc.vercel.com/my-team-slug) रजिस्टर करें।
- अन्य संगठनों की पहुँच को रोकने के लिए
aud क्लेम को अपने Vercel टीम URL तक सीमित करें।
sub क्लेम शर्त के साथ सटीक रूप से प्रोजेक्ट नाम (my-ai-app) और एनवायरनमेंट (production) को लक्षित करें। Confused Deputy हमलों को रोकने के लिए यह मुख्य सेटिंग्स में से एक है।
अनुमति नीति (Permission Policy) में भी केवल आवश्यक संसाधनों को ही खोलें। यहाँ केवल विशिष्ट S3 बकेट और 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"
]
}
]
}
`
Node.js कोड में, अस्थायी क्रेडेंशियल्स प्राप्त करने के लिए @vercel/oidc-aws-credentials-provider का उपयोग करें।
`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. रीजन अंतर के कारण होने वाली API लेटेंसी को कम करना
यदि Vercel फ्रंटएंड टोक्यो PoP पर चल रहा है और AWS बैकएंड अमेरिका के पूर्व (us-east-1) में है, तो प्रत्येक अनुरोध के आने-जाने पर 150ms से 250ms की भौतिक लेटेंसी जुड़ जाती है।
अक्सर न बदलने वाले API प्रतिक्रियाओं (responses) को Vercel Edge Middleware स्तर पर कैश करना बेहतर होता है ताकि बैकएंड कॉल ही न हो।
`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;
}
`
s-maxage=60, stale-while-revalidate=120 सेट करने से Edge कैश 60 सेकंड के लिए तुरंत उत्तर देगा, और कैश समाप्त होने के बाद 120 सेकंड तक यह पुराना उत्तर दिखाता रहेगा जबकि बैकग्राउंड में कैश को अपडेट किया जाएगा।
सेवाओं के बीच ट्रेसिंग कनेक्ट करने के लिए, OpenTelemetry के W3C TraceContext मानक (traceparent हेडर) को प्रोपेगेट करना होगा। यह 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'],
},
},
});
}
`
AWS FastAPI स्तर पर भी इसी तरह एक OpenTelemetry रिसीवर संलग्न करें, जिससे फ्रंटएंड से बैकएंड तक एक ही 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. डेटा ट्रांसफर शुल्क और असीमित स्केलिंग लागत को रोकना
लागत दुर्घटनाएँ आमतौर पर दो स्थानों पर होती हैं: Vercel का Fast Data Transfer अतिरिक्त शुल्क (1TB पार करने पर 0.15प्रतिGB)औरAWSकाDataTransferOutशुल्क(0.09 प्रति GB)। इसके ऊपर, जब ट्रैफिक बढ़ता है और सर्वरलेस फ़ंक्शंस व Lambda एक साथ स्केल करना शुरू करते हैं, तो बिल की राशि बहुत तेजी से बढ़ जाती है।
यहाँ Vercel खर्च सीमा वेबहुक प्राप्त करने और Slack पर अलर्ट भेजने का एक API Route उदाहरण दिया गया है। सुरक्षा के लिए इसे HMAC SHA1 सिग्नेचर वेरिफिकेशन से गुज़रना होगा।
`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 खर्च चेतावनी: बजट सीमा (${event.payload.spendAmount} USD) तक पहुँच गई है।,
}),
});
return new Response('OK', { status: 200 });
}
`
इन दो सुरक्षा उपायों को अवश्य चालू रखें:
- Vercel प्रोजेक्ट के Settings > Billing में Pause production deployment चालू करें। यदि बजट सीमा पूरी हो जाती है, भले ही सेवा बंद हो जाए, फिर भी आपका बैंक बैलेंस सुरक्षित रहेगा।
- AWS Lambda के Reserved Concurrency (आरक्षित समवर्ती) को लगभग 50 तक सीमित करें। यह उस स्थिति को रोकता है जहाँ इंस्टेंस असीमित रूप से बढ़ते हैं, DB पर दबाव डालते हैं और एक साथ बड़ी लागत उत्पन्न करते हैं।