--- trigger: always_on --- # Performance Optimization Patterns ## When to Use This Skill Use this skill when: - Optimizing database queries - Detecting and fixing memory leaks - Profiling application performance - Optimizing connection pooling - Improving caching strategies - Identifyings N+1 query problems - Optimizing batch operations - Improving application startup time - Reducing memory usage - Optimizing API response times ## Core Concepts ### Performance Metrics 1. **Response Time**: Time to process request 2. **Throughput**: Requests processed per second 3. **Memory Usage**: Memory consumption 4. **CPU Usage**: CPU utilization 5. **Database Query Time**: Query execution time ### Optimization Areas - Database queries - Memory management - Connection pooling - Caching - Batch processing - Lazy loading ## Database Query Optimization ### Query Analysis ```typescript // src/core/db/query-analyzer.ts // EN: Query performance analyzer // VI: Phân tích hiệu suất query import { PrismaClient } from '@prisma/client'; import { logger } from '@goodgo/logger'; export class QueryAnalyzer { constructor(private prisma: PrismaClient) {} /** * EN: Analyze query performance * VI: Phân tích hiệu suất query */ async analyzeQuery(query: string): Promise { const result = await this.prisma.$queryRawUnsafe(`EXPLAIN ANALYZE ${query}`); logger.info('Query analysis', { query, result }); return result; } /** * EN: Check for missing indexes * VI: Kiểm tra indexes bị thiếu */ async checkIndexes(tableName: string): Promise { const indexes = await this.prisma.$queryRawUnsafe(` SELECT * FROM pg_indexes WHERE tablename = '${tableName}' `); return indexes; } } ``` ### N+1 Query Prevention ```typescript // EN: Bad: N+1 queries // VI: Xấu: N+1 queries async function getUsersWithOrdersBad(): Promise { const users = await userRepository.findAll(); for (const user of users) { user.orders = await orderRepository.findByUserId(user.id); // N+1! } return users; } // EN: Good: Use include/join // VI: Tốt: Sử dụng include/join async function getUsersWithOrdersGood(): Promise { return await userRepository.findAll({ include: { orders: true, // EN: Single query with join / VI: Single query với join }, }); } ``` ### Batch Operations ```typescript // src/core/db/batch-operations.ts // EN: Batch database operations // VI: Batch database operations export class BatchOperations { /** * EN: Batch create operations * VI: Batch create operations */ async batchCreate(items: T[], batchSize: number = 100): Promise { const results: T[] = []; for (let i = 0; i < items.length; i += batchSize) { const batch = items.slice(i, i + batchSize); const batchResults = await prisma.$transaction( batch.map((item) => prisma.item.create({ data: item })) ); results.push(...batchResults); } return results; } /** * EN: Batch update operations * VI: Batch update operations */ async batchUpdate( updates: Array<{ id: string; data: any }>, batchSize: number = 100 ): Promise { for (let i = 0; i < updates.length; i += batchSize) { const batch = updates.slice(i, i + batchSize); await prisma.$transaction( batch.map(({ id, data }) => prisma.item.update({ where: { id }, data })) ); } } } ``` ## Memory Leak Detection ### Memory Profiling ```typescript // src/core/performance/memory-profiler.ts // EN: Memory leak detection // VI: Phát hiện memory leak import { performance } from 'perf_hooks'; import { logger } from '@goodgo/logger'; export class MemoryProfiler { private baseline?: NodeJS.MemoryUsage; private interval?: NodeJS.Timeout; /** * EN: Start memory profiling * VI: Bắt đầu memory profiling */ start(): void { this.baseline = process.memoryUsage(); this.interval = setInterval(() => { const current = process.memoryUsage(); const delta = { heapUsed: current.heapUsed - (this.baseline?.heapUsed || 0), heapTotal: current.heapTotal - (this.baseline?.heapTotal || 0), external: current.external - (this.baseline?.external || 0), }; logger.info('Memory usage', { current: { heapUsed: `${(current.heapUsed / 1024 / 1024).toFixed(2)} MB`, heapTotal: `${(current.heapTotal / 1024 / 1024).toFixed(2)} MB`, }, delta: { heapUsed: `${(delta.heapUsed / 1024 / 1024).toFixed(2)} MB`, }, }); // EN: Alert if memory growth is excessive // VI: Cảnh báo nếu memory tăng quá mức if (delta.heapUsed > 100 * 1024 * 1024) { // 100MB growth logger.warn('Potential memory leak detected', { delta }); } }, 60000); // EN: Check every minute / VI: Kiểm tra mỗi phút } stop(): void { if (this.interval) { clearInterval(this.interval); } } /** * EN: Force garbage collection (if --expose-gc flag is set) * VI: Ép garbage collection (nếu --expose-gc flag được set) */ forceGC(): void { if (global.gc) { global.gc(); } } } ``` ## Connection Pooling ### Database Connection Pool ```typescript // src/config/database.config.ts // EN: Optimize Prisma connection pool // VI: Tối ưu Prisma connection pool export const prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL, }, }, log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'], }); // EN: Connection pool configuration in DATABASE_URL // VI: Cấu hình connection pool trong DATABASE_URL // DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=20&pool_timeout=10" ``` ### HTTP Connection Pool ```typescript // src/core/clients/http-pool.config.ts // EN: HTTP connection pooling with keep-alive // VI: HTTP connection pooling với keep-alive import axios from 'axios'; import https from 'https'; const httpAgent = new https.Agent({ keepAlive: true, keepAliveMsecs: 1000, maxSockets: 50, maxFreeSockets: 10, timeout: 60000, }); export const httpClient = axios.create({ httpAgent, httpsAgent: httpAgent, timeout: 30000, }); ``` ## Performance Monitoring ```typescript // src/core/performance/performance-monitor.ts // EN: Performance monitoring middleware // VI: Middleware giám sát hiệu suất import { Request, Response, NextFunction } from 'express'; import { performance } from 'perf_hooks'; import { logger } from '@goodgo/logger'; export function performanceMonitor(req: Request, res: Response, next: NextFunction) { const start = performance.now(); const startMemory = process.memoryUsage(); res.on('finish', () => { const duration = performance.now() - start; const endMemory = process.memoryUsage(); logger.info('Request performance', { method: req.method, path: req.path, status: res.statusCode, duration: `${duration.toFixed(2)}ms`, memoryDelta: `${((endMemory.heapUsed - startMemory.heapUsed) / 1024 / 1024).toFixed(2)} MB`, }); // EN: Alert on slow requests // VI: Cảnh báo trên requests chậm if (duration > 1000) { logger.warn('Slow request detected', { path: req.path, duration: `${duration.toFixed(2)}ms`, }); } }); next(); } ``` ## Caching Optimization ```typescript // src/core/cache/cache-optimizer.ts // EN: Cache optimization strategies // VI: Chiến lược tối ưu cache export class CacheOptimizer { /** * EN: Preload frequently accessed data * VI: Tải trước dữ liệu thường truy cập */ async preloadHotData(keys: string[]): Promise { for (const key of keys) { // EN: Fetch and cache data // VI: Fetch và cache dữ liệu await cacheService.getOrSet(key, async () => { return await this.fetchData(key); }, 3600); } } /** * EN: Batch cache operations * VI: Batch cache operations */ async batchGet(keys: string[]): Promise> { const results = new Map(); const missing: string[] = []; // EN: Check cache for all keys // VI: Kiểm tra cache cho tất cả keys for (const key of keys) { const cached = await cacheService.get(key); if (cached) { results.set(key, cached); } else { missing.push(key); } } // EN: Fetch missing data // VI: Fetch dữ liệu thiếu if (missing.length > 0) { const data = await this.fetchBatch(missing); for (const [key, value] of Object.entries(data)) { results.set(key, value); await cacheService.set(key, value, 3600); } } return results; } } ``` ## Best Practices 1. **Database**: Use indexes, avoid N+1 queries, use batch operations 2. **Memory**: Monitor memory usage, detect leaks, use streaming for large data 3. **Caching**: Cache frequently accessed data, use appropriate TTLs 4. **Connection Pooling**: Configure pool sizes appropriately 5. **Profiling**: Profile regularly to identify bottlenecks 6. **Monitoring**: Monitor performance metrics continuously ## Common Mistakes 1. **N+1 Queries**: Fetching related data in loops ```typescript // ❌ BAD: N+1 for (const user of users) { user.orders = await orderRepo.findByUserId(user.id); } // ✅ GOOD: Use include const users = await userRepo.findAll({ include: { orders: true } }); ``` 2. **No Connection Pooling**: Creating new connections per request ```typescript // ❌ BAD: No pooling const client = new PrismaClient(); // per request // ✅ GOOD: Reuse singleton import { prisma } from './db'; // shared instance ``` 3. **Missing Indexes**: Slow queries on frequently searched columns ```prisma // ✅ Add indexes in schema @@index([createdAt]) @@index([userId, status]) ``` 4. **Unbounded Queries**: Fetching all data without limits ```typescript // ❌ BAD const all = await repo.findAll(); // ✅ GOOD const page = await repo.findAll({ take: 100, skip: offset }); ``` ## Quick Reference | Metric | Target | Alert Threshold | |--------|--------|-----------------| | **Response Time (p95)** | <200ms | >500ms | | **Memory Usage** | <70% | >85% | | **CPU Usage** | <60% | >80% | | **Query Time** | <50ms | >200ms | **Profiling Commands:** ```bash # CPU profiling node --prof app.js node --prof-process isolate-*.log > profile.txt # Memory snapshot node --inspect app.js # Use Chrome DevTools # Clinic.js npx clinic doctor -- node app.js npx clinic flame -- node app.js ``` **Prisma Query Optimization:** ```typescript // Select only needed fields await prisma.user.findMany({ select: { id: true, email: true } }); // Use include for relations await prisma.user.findMany({ include: { posts: true } }); // Batch operations await prisma.$transaction([...operations]); // Raw query for complex cases await prisma.$queryRaw`SELECT ... FROM ...`; ``` **Connection Pool Config:** ```bash # In DATABASE_URL ?connection_limit=20&pool_timeout=10 ``` **Performance Checklist:** - [ ] Add database indexes for frequent queries - [ ] Use pagination for list endpoints - [ ] Implement caching for hot data - [ ] Enable connection pooling - [ ] Profile and monitor regularly - [ ] Use batch operations for bulk updates ## Resources - [Node.js Performance](https://nodejs.org/en/docs/guides/simple-profiling/) - [Prisma Performance](https://www.prisma.io/docs/guides/performance-and-optimization) - [Caching Patterns](../caching-patterns/SKILL.md) - Caching strategies - [Observability & Monitoring](../observability-monitoring/SKILL.md) - Monitoring patterns - [Database & Prisma](../database-prisma/SKILL.md) - Database patterns