---
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
