Skip to content

Commit 82a76a6

Browse files
committed
fix(db): optimize connection handling for Vercel serverless
- Reduce retry attempts to 1 in production (fail fast) - Disable keepAlive for serverless (connections are ephemeral) - Return 503 for DB connection errors (allows client retry) - Reset app instance on connection failure to force reconnect - Update .env.example with serverless DB recommendations
1 parent ddbdb70 commit 82a76a6

File tree

3 files changed

+49
-14
lines changed

3 files changed

+49
-14
lines changed

backend/.env.example

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,23 @@
66
# -----------------------------------------------------------------------------
77
# Database Configuration
88
# -----------------------------------------------------------------------------
9-
# Option 1: Use DATABASE_URL (recommended for production/Aiven)
9+
# Option 1: Use DATABASE_URL (recommended for production)
1010
# Format: postgres://username:password@host:port/database
11-
# DATABASE_URL=postgres://avnadmin:password@host.aivencloud.com:12345/defaultdb
11+
#
12+
# ⚠️ IMPORTANT FOR VERCEL SERVERLESS:
13+
# Serverless functions create many concurrent connections. You need a database
14+
# with connection pooling. Options:
15+
#
16+
# 1. Neon (RECOMMENDED - free tier includes pooling):
17+
# DATABASE_URL=postgres://user:pass@ep-xxx.region.aws.neon.tech/dbname?sslmode=require
18+
#
19+
# 2. Supabase (free tier with Supavisor pooling):
20+
# DATABASE_URL=postgres://postgres.xxx:pass@aws-0-region.pooler.supabase.com:6543/postgres
21+
#
22+
# 3. Aiven with PgBouncer (paid plans only):
23+
# DATABASE_URL=postgres://avnadmin:pass@host.aivencloud.com:6543/defaultdb?sslmode=require
24+
#
25+
# ❌ Aiven FREE tier does NOT include PgBouncer - will exhaust 20 connections quickly!
1226

1327
# Aiven CA Certificate (required for production)
1428
# Copy the content of ca.pem from Aiven Console and paste as a single line

backend/api/index.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,29 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
6666
return instance(req, res);
6767
} catch (error) {
6868
console.error('Handler error:', error);
69-
res.status(500).json({
70-
statusCode: 500,
71-
message: 'Internal server error',
72-
error: error instanceof Error ? error.message : 'Unknown error',
73-
});
69+
70+
// Check for database connection errors
71+
const errorMessage =
72+
error instanceof Error ? error.message : 'Unknown error';
73+
const isDbConnectionError =
74+
errorMessage.includes('connection') ||
75+
errorMessage.includes('ECONNREFUSED') ||
76+
errorMessage.includes('remaining connection slots');
77+
78+
if (isDbConnectionError) {
79+
// Reset app instance to force reconnection on next request
80+
app = null;
81+
res.status(503).json({
82+
statusCode: 503,
83+
message: 'Database temporarily unavailable. Please retry.',
84+
error: 'Service Unavailable',
85+
});
86+
} else {
87+
res.status(500).json({
88+
statusCode: 500,
89+
message: 'Internal server error',
90+
error: errorMessage,
91+
});
92+
}
7493
}
7594
}

backend/src/app.module.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,21 @@ import { UsersModule } from './users/users.module';
5555
? { rejectUnauthorized: true, ca: caCert }
5656
: { rejectUnauthorized: false },
5757
// Reduce retry attempts for serverless - fail fast instead of queuing
58-
retryAttempts: isProduction ? 2 : 10,
59-
retryDelay: 1000,
60-
// Connection pool settings for serverless environment
61-
// IMPORTANT: Use Aiven's PgBouncer (port 6543) for better pooling
58+
retryAttempts: isProduction ? 1 : 10,
59+
retryDelay: 500,
60+
// Connection pool settings for serverless (Vercel)
61+
// NOTE: Aiven free tier has NO PgBouncer. Consider Neon/Supabase for free pooling.
6262
extra: {
63-
// Max 1 connection per Lambda when using PgBouncer
63+
// Single connection per serverless function instance
6464
max: isProduction ? 1 : 10,
6565
min: 0,
6666
// Fail fast if no connection available
67-
connectionTimeoutMillis: 3000,
67+
connectionTimeoutMillis: isProduction ? 5000 : 10000,
6868
// Release idle connections quickly in serverless
69-
idleTimeoutMillis: 1000,
69+
idleTimeoutMillis: isProduction ? 1000 : 30000,
7070
allowExitOnIdle: true,
71+
// Disable keepalive for serverless - connections are short-lived
72+
keepAlive: !isProduction,
7173
},
7274
};
7375
}

0 commit comments

Comments
 (0)