Files
pos-system/.agent/rules/performance-optimization.md

458 lines
12 KiB
Markdown

---
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<any> {
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<any> {
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<User[]> {
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<User[]> {
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<T>(items: T[], batchSize: number = 100): Promise<T[]> {
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<void> {
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<void> {
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<Map<string, any>> {
const results = new Map<string, any>();
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