Scaling & Load Management
Scale your application to handle increased traffic and optimize performance under load.
Scaling & Load Management
Horizontal Scaling
Load Balancing
Distribute traffic across multiple application instances.
# nginx.conf - Load balancer configuration
upstream app_servers {
server app1.example.com:3000;
server app2.example.com:3000;
server app3.example.com:3000;
# Health check
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Load balancing method
# ip_hash; # Session affinity
# least_conn; # Least connections
}
}
Auto-Scaling Configuration
Set up automatic scaling based on metrics.
# docker-compose.yml with scaling
version: '3.8'
services:
app:
image: your-app:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Vertical Scaling
Resource Optimization
Optimize server resources for better performance.
// next.config.mjs - Performance optimizations
/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable compression
compress: true,
// Optimize bundle
swcMinify: true,
// Enable experimental features
experimental: {
// Reduce bundle size
optimizePackageImports: ['@mui/material', 'lodash'],
// Improve build performance
turbo: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
},
// Image optimization
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
}
export default nextConfig
Memory Management
Optimize memory usage and prevent leaks.
// lib/memory-management.ts
export class MemoryManager {
private static instance: MemoryManager
private cache = new Map<string, any>()
private readonly maxCacheSize = 1000
static getInstance(): MemoryManager {
if (!MemoryManager.instance) {
MemoryManager.instance = new MemoryManager()
}
return MemoryManager.instance
}
set(key: string, value: any): void {
// Implement LRU cache eviction
if (this.cache.size >= this.maxCacheSize) {
const firstKey = this.cache.keys().next().value
this.cache.delete(firstKey)
}
this.cache.set(key, value)
}
get(key: string): any {
const value = this.cache.get(key)
if (value) {
// Move to end (most recently used)
this.cache.delete(key)
this.cache.set(key, value)
}
return value
}
clear(): void {
this.cache.clear()
}
getMemoryUsage(): NodeJS.MemoryUsage {
return process.memoryUsage()
}
}
Database Scaling
Read Replicas
Scale database reads with replica servers.
// lib/database-scaling.ts
import { Pool } from 'pg'
export class DatabaseManager {
private writePool: Pool
private readPools: Pool[]
private currentReadIndex = 0
constructor() {
// Master database for writes
this.writePool = new Pool({
connectionString: process.env.DATABASE_WRITE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
})
// Read replicas
this.readPools = [
new Pool({
connectionString: process.env.DATABASE_READ_URL_1,
max: 10,
}),
new Pool({
connectionString: process.env.DATABASE_READ_URL_2,
max: 10,
}),
]
}
async write(query: string, values?: any[]): Promise<any> {
const client = await this.writePool.connect()
try {
return await client.query(query, values)
} finally {
client.release()
}
}
async read(query: string, values?: any[]): Promise<any> {
// Round-robin load balancing for reads
const pool = this.readPools[this.currentReadIndex]
this.currentReadIndex = (this.currentReadIndex + 1) % this.readPools.length
const client = await pool.connect()
try {
return await client.query(query, values)
} finally {
client.release()
}
}
}
Connection Pooling
Optimize database connections for high load.
// lib/connection-pool.ts
import { Pool, PoolConfig } from 'pg'
const poolConfig: PoolConfig = {
// Connection limits
max: 20, // Maximum number of connections
min: 5, // Minimum number of connections
// Timeouts
connectionTimeoutMillis: 2000,
idleTimeoutMillis: 30000,
// Health checks
allowExitOnIdle: true,
// Connection validation
application_name: 'your-app',
}
export const db = new Pool(poolConfig)
// Monitor pool health
db.on('connect', (client) => {
console.log('New client connected:', client.processID)
})
db.on('error', (err) => {
console.error('Database pool error:', err)
})
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Closing database pool...')
await db.end()
process.exit(0)
})
Performance Monitoring
Load Testing
Test application performance under load.
// k6 load test script
import http from 'k6/http'
import { check, sleep } from 'k6'
export let options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 200 }, // Ramp up to 200 users
{ duration: '5m', target: 200 }, // Stay at 200 users
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<2000'], // 95% of requests under 2s
http_req_failed: ['rate<0.1'], // Error rate under 10%
},
}
export default function() {
let response = http.get('https://your-app.com/api/endpoint')
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 2000ms': (r) => r.timings.duration < 2000,
})
sleep(1)
}
Metrics Collection
Monitor key performance indicators.
// lib/metrics.ts
export class MetricsCollector {
private metrics = new Map<string, number>()
increment(metric: string, value = 1): void {
const current = this.metrics.get(metric) || 0
this.metrics.set(metric, current + value)
}
gauge(metric: string, value: number): void {
this.metrics.set(metric, value)
}
timing(metric: string, startTime: number): void {
const duration = Date.now() - startTime
this.metrics.set(metric, duration)
}
getMetrics(): Record<string, number> {
return Object.fromEntries(this.metrics)
}
// Export metrics in Prometheus format
exportPrometheus(): string {
let output = ''
for (const [key, value] of this.metrics) {
output += `${key} ${value}\n`
}
return output
}
}
// Usage example
const metrics = new MetricsCollector()
export function trackApiCall(endpoint: string) {
return function(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value
descriptor.value = async function(...args: any[]) {
const startTime = Date.now()
try {
const result = await method.apply(this, args)
metrics.increment(`api_calls_success_${endpoint}`)
metrics.timing(`api_response_time_${endpoint}`, startTime)
return result
} catch (error) {
metrics.increment(`api_calls_error_${endpoint}`)
throw error
}
}
}
}
CDN Configuration
Content Delivery Network
Optimize content delivery with CDN.
// next.config.mjs - CDN configuration
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ['cdn.example.com'],
loader: 'custom',
loaderFile: './lib/cdn-loader.js',
},
assetPrefix: process.env.NODE_ENV === 'production'
? 'https://cdn.example.com'
: '',
}
export default nextConfig
// lib/cdn-loader.js
export default function cdnLoader({ src, width, quality }) {
const params = new URLSearchParams({
url: src,
w: width.toString(),
q: (quality || 75).toString(),
})
return `https://cdn.example.com/api/image?${params}`
}
Scale your application effectively to handle growing traffic while maintaining optimal performance and user experience.