ai-templates
Version:
CLI tool for generating AI templates for different frameworks and tools
584 lines (486 loc) • 12.9 kB
Markdown
# Performance Optimization Guide - Frontend (React)
This command helps you identify and fix performance bottlenecks in your backend application.
## Step 1: Performance Baseline
### Setup Performance Monitoring
```bash
# Install performance monitoring tools
npm install --save-dev clinic autocannon
# Install APM agents (choose one)
npm install --save @sentry/node
npm install --save newrelic
npm install --save @appdynamics/appdynamics
```
### Establish Baseline Metrics
```bash
# Run load test to establish baseline
npx autocannon -c 100 -d 30 http://localhost:3000/api/health
# Save baseline results
npx autocannon -c 100 -d 30 --json http://localhost:3000/api/users > baseline-$(date +%Y%m%d).json
```
### Key Metrics to Track
- **Response Time**: p50, p95, p99
- **Throughput**: Requests per second
- **Error Rate**: Failed requests percentage
- **CPU Usage**: Average and peak
- **Memory Usage**: Heap size and usage
- **Database**: Query time and connection pool
## Step 2: Performance Profiling
### CPU Profiling
```bash
# Using clinic.js
npx clinic doctor -- node dist/server.js
# Generate flame graph
npx clinic flame -- node dist/server.js
# Using native V8 profiler
node --prof dist/server.js
# Process the log
node --prof-process isolate-*.log > profile.txt
```
### Memory Profiling
```bash
# Memory usage analysis
npx clinic heapprofiler -- node dist/server.js
# Detect memory leaks
node --expose-gc --inspect dist/server.js
# Use Chrome DevTools Memory Profiler
```
### Event Loop Monitoring
```typescript
// Add event loop lag monitoring
import { monitorEventLoopDelay } from 'perf_hooks';
const histogram = monitorEventLoopDelay({ resolution: 10 });
histogram.enable();
setInterval(() => {
console.log(`Event loop delay: ${histogram.mean / 1e6}ms`);
console.log(`Max delay: ${histogram.max / 1e6}ms`);
}, 5000);
```
## Step 3: Database Optimization
### Query Performance Analysis
```typescript
// Enable query logging with timing
const db = knex({
client: 'pg',
debug: true, // Development only
pool: {
afterCreate: (conn, done) => {
conn.query('SET statement_timeout = 5000', (err) => {
done(err, conn);
});
}
}
});
// Add query timing
db.on('query', (query) => {
console.time(`Query ${query.__knexQueryUid}`);
});
db.on('query-response', (response, query) => {
console.timeEnd(`Query ${query.__knexQueryUid}`);
});
```
### Optimize Slow Queries
```sql
-- Find slow queries in PostgreSQL
SELECT
query,
calls,
total_time,
mean_time,
max_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- Add missing indexes
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
CREATE INDEX CONCURRENTLY idx_orders_user_created ON orders(user_id, created_at);
```
### Connection Pool Optimization
```typescript
const db = knex({
client: 'pg',
connection: process.env.DATABASE_URL,
pool: {
min: 2,
max: 10,
acquireTimeoutMillis: 30000,
createTimeoutMillis: 30000,
destroyTimeoutMillis: 5000,
idleTimeoutMillis: 30000,
reapIntervalMillis: 1000,
createRetryIntervalMillis: 100
}
});
// Monitor pool usage
setInterval(() => {
const pool = db.client.pool;
console.log({
used: pool.numUsed(),
free: pool.numFree(),
pending: pool.numPendingCreates(),
queued: pool.numPendingAcquires()
});
}, 10000);
```
## Step 4: Caching Implementation
### In-Memory Caching
```typescript
import NodeCache from 'node-cache';
const cache = new NodeCache({
stdTTL: 600, // 10 minutes default
checkperiod: 120 // Check for expired keys every 2 minutes
});
// Cache decorator
function Cacheable(ttl: number = 600) {
return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = async function (...args: any[]) {
const key = `${propertyName}:${JSON.stringify(args)}`;
const cached = cache.get(key);
if (cached) {
return cached;
}
const result = await method.apply(this, args);
cache.set(key, result, ttl);
return result;
};
};
}
// Usage
class UserService {
@Cacheable(300)
async getUser(id: string) {
return await db('users').where({ id }).first();
}
}
```
### Redis Caching
```typescript
import Redis from 'ioredis';
const redis = new Redis({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
maxRetriesPerRequest: 3
});
// Caching middleware
async function cacheMiddleware(req: FastifyRequest, reply: FastifyReply) {
const key = `cache:${req.method}:${req.url}`;
const cached = await redis.get(key);
if (cached) {
reply.header('X-Cache', 'HIT');
return reply.send(JSON.parse(cached));
}
reply.header('X-Cache', 'MISS');
}
// Cache response
app.addHook('onSend', async (request, reply, payload) => {
if (reply.statusCode === 200 && request.method === 'GET') {
const key = `cache:${request.method}:${request.url}`;
await redis.setex(key, 300, JSON.stringify(payload));
}
return payload;
});
```
## Step 5: API Response Optimization
### Pagination Implementation
```typescript
interface PaginationParams {
page: number;
limit: number;
sort?: string;
order?: 'asc' | 'desc';
}
async function paginatedQuery(
query: Knex.QueryBuilder,
params: PaginationParams
) {
const { page = 1, limit = 20, sort = 'id', order = 'asc' } = params;
const offset = (page - 1) * limit;
// Get total count (optimized with separate query)
const [{ count }] = await query.clone().count('* as count');
// Get paginated results
const results = await query
.orderBy(sort, order)
.limit(limit)
.offset(offset);
return {
data: results,
pagination: {
total: parseInt(count),
page,
limit,
pages: Math.ceil(count / limit)
}
};
}
```
### Response Compression
```typescript
import compress from '@fastify/compress';
app.register(compress, {
global: true,
threshold: 1024, // Only compress responses larger than 1KB
encodings: ['gzip', 'deflate', 'br']
});
```
### Field Selection (Sparse Fieldsets)
```typescript
// Allow clients to select fields
app.get('/api/users/:id', async (request, reply) => {
const fields = request.query.fields?.split(',') || ['*'];
const user = await db('users')
.select(fields)
.where({ id: request.params.id })
.first();
return user;
});
```
## Step 6: Asynchronous Processing
### Job Queue Implementation
```typescript
import Queue from 'bull';
const emailQueue = new Queue('email', {
redis: {
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT
}
});
// Process jobs
emailQueue.process(async (job) => {
const { to, subject, body } = job.data;
await sendEmail(to, subject, body);
});
// Add job to queue instead of blocking
app.post('/api/notifications', async (request, reply) => {
const notification = await createNotification(request.body);
// Queue email sending
await emailQueue.add('send-notification-email', {
to: notification.userEmail,
subject: notification.title,
body: notification.message
});
// Return immediately
reply.code(201).send({ data: notification });
});
```
### Stream Processing
```typescript
// Stream large datasets
app.get('/api/export/users', async (request, reply) => {
reply.type('application/json');
reply.raw.write('[');
let first = true;
const stream = db('users').stream();
stream.on('data', (user) => {
if (!first) reply.raw.write(',');
first = false;
reply.raw.write(JSON.stringify(user));
});
stream.on('end', () => {
reply.raw.write(']');
reply.raw.end();
});
stream.on('error', (err) => {
reply.code(500).send({ error: err.message });
});
});
```
## Step 7: Fastify-Specific Optimizations
### Schema Compilation
```typescript
// Pre-compile schemas for better performance
const userSchema = {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
};
// Fastify will compile this once
app.get('/api/users/:id', {
schema: {
response: {
200: userSchema
}
}
}, handler);
```
### Use Fastify Plugins Efficiently
```typescript
// Register plugins correctly
app.register(async function (fastify) {
// Plugin-scoped decorators and hooks
fastify.decorate('utility', utilityFunction);
// Routes using this utility
fastify.get('/route1', handler1);
fastify.get('/route2', handler2);
});
```
### Optimize Logging
```typescript
// Use appropriate log levels
const logger = {
development: {
transport: {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname'
}
}
},
production: true, // Use default pino for production
test: false
};
app = fastify({ logger: logger[process.env.NODE_ENV] });
```
## Step 8: Infrastructure Optimization
### Clustering
```typescript
import cluster from 'cluster';
import os from 'os';
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork(); // Replace dead workers
});
} else {
// Worker process - start server
startServer();
}
```
### HTTP/2 Support
```typescript
import fs from 'fs';
const app = fastify({
http2: true,
https: {
allowHTTP1: true, // Fallback support
key: fs.readFileSync('path/to/key.pem'),
cert: fs.readFileSync('path/to/cert.pem')
}
});
```
## Step 9: Monitoring and Alerting
### Custom Metrics
```typescript
import { register, Counter, Histogram } from 'prom-client';
const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status']
});
const httpRequestTotal = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status']
});
app.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now();
});
app.addHook('onResponse', async (request, reply) => {
const duration = (Date.now() - request.startTime) / 1000;
const labels = {
method: request.method,
route: request.routerPath || request.url,
status: reply.statusCode
};
httpRequestDuration.observe(labels, duration);
httpRequestTotal.inc(labels);
});
// Metrics endpoint
app.get('/metrics', async (request, reply) => {
reply.type('text/plain');
return register.metrics();
});
```
## Step 10: Performance Testing
### Load Testing Scripts
```bash
# Create load test scenarios
cat > load-test.yml << EOF
config:
target: 'http://localhost:3000'
phases:
- duration: 60
arrivalRate: 10
name: "Warm up"
- duration: 120
arrivalRate: 50
name: "Ramp up"
- duration: 300
arrivalRate: 100
name: "Sustained load"
scenarios:
- name: "User Flow"
flow:
- post:
url: "/api/auth/login"
json:
email: "test@example.com"
password: "password"
capture:
json: "$.token"
as: "token"
- get:
url: "/api/users/profile"
headers:
Authorization: "Bearer {{ token }}"
EOF
# Run load test
npx artillery run load-test.yml
```
### Performance Report
```bash
# Generate performance report
mkdir -p reports/performance/$(date +%Y%m%d)
# Run comprehensive performance test
npm run build
npx clinic doctor -- node dist/server.js &
SERVER_PID=$!
sleep 5
# Run load test
npx autocannon -c 200 -d 60 --json http://localhost:3000/api/users > reports/performance/$(date +%Y%m%d)/load-test.json
# Stop server
kill $SERVER_PID
# Generate report
node scripts/generate-performance-report.js
```
## Performance Optimization Checklist
```markdown
## Performance Optimization Checklist
### Database
- [ ] Queries optimized with EXPLAIN
- [ ] Appropriate indexes created
- [ ] Connection pooling configured
- [ ] N+1 queries eliminated
- [ ] Read replicas utilized
### Caching
- [ ] Static content cached
- [ ] Database query results cached
- [ ] Redis/Memcached implemented
- [ ] Cache invalidation strategy
- [ ] CDN configured
### API Design
- [ ] Pagination implemented
- [ ] Response compression enabled
- [ ] Field selection available
- [ ] Batch endpoints created
- [ ] GraphQL for flexible queries
### Code Optimization
- [ ] Async/await used properly
- [ ] Memory leaks fixed
- [ ] Large objects streamed
- [ ] CPU-intensive tasks queued
- [ ] Dependencies minimized
### Infrastructure
- [ ] Horizontal scaling ready
- [ ] Load balancer configured
- [ ] Auto-scaling policies
- [ ] Resource limits set
- [ ] Monitoring alerts configured
```