# Performance Optimization

## Response Time Optimization

**Response time optimization** focuses pada reducing latency dan improving user experience.

```yaml
# Response Time Optimization Strategies

## Frontend Optimization
browser_caching:
  - Set appropriate Cache-Control headers
  - Implement service workers
  - Use localStorage for static data
  - Optimize critical rendering path

resource_optimization:
  - Minify CSS and JavaScript
  - Compress images (WebP format)
  - Use lazy loading for images
  - Implement code splitting

network_optimization:
  - Use HTTP/2 or HTTP/3
  - Implement resource bundling
  - Reduce HTTP requests
  - Use CDN for static assets

## Backend Optimization
query_optimization:
  - Add database indexes
  - Use query caching
  - Implement pagination
  - Optimize complex queries

connection_pooling:
  - Reuse database connections
  - Implement connection timeouts
  - Configure pool sizes
  - Monitor connection metrics

caching_strategies:
  - Application-level caching
  - Database query caching
  - CDN implementation
  - API response caching

## Infrastructure Optimization
load_balancing:
  - Distribute traffic evenly
  - Implement health checks
  - Use geographic load balancing
  - Configure failover mechanisms

auto_scaling:
  - Scale based on metrics
  - Implement horizontal pod autoscaling
  - Configure scaling policies
  - Monitor scaling events

performance_monitoring:
  - Track response times
  - Monitor error rates
  - Set up alerts
  - Analyze performance trends
```

## Implementation Example

```javascript
// Performance Optimization Service
class PerformanceOptimizationService {
  constructor(metricsCollector, cacheManager, config = {}) {
    this.metricsCollector = metricsCollector;
    this.cacheManager = cacheManager;
    this.config = {
      slowQueryThreshold: config.slowQueryThreshold || 1000,
      enableQueryCache: config.enableQueryCache || true,
      enableCompression: config.enableCompression || true,
      maxCacheSize: config.maxCacheSize || 1000,
      ...config
    };

    this.queryCache = new Map();
    this.slowQueries = new Set();
  }

  async executeQuery(query, params = {}, options = {}) {
    const startTime = Date.now();
    const cacheKey = this.generateQueryCacheKey(query, params);

    try {
      // Check query cache first
      if (this.config.enableQueryCache && !options.skipCache) {
        const cachedResult = await this.queryCache.get(cacheKey);
        if (cachedResult) {
          this.metricsCollector.recordCacheHit('query');
          return cachedResult;
        }
      }

      // Execute query
      const result = await this.executeQueryWithRetry(query, params);

      // Cache result
      if (this.config.enableQueryCache && this.shouldCacheResult(result)) {
        await this.queryCache.set(cacheKey, result);
      }

      // Record metrics
      const executionTime = Date.now() - startTime;
      this.metricsCollector.recordQueryExecution(query, executionTime, params);

      // Check for slow queries
      if (executionTime > this.config.slowQueryThreshold) {
        this.handleSlowQuery(query, params, executionTime);
      }

      return result;
    } catch (error) {
      const executionTime = Date.now() - startTime;
      this.metricsCollector.recordQueryError(query, error, executionTime);
      throw error;
    }
  }

  async executeQueryWithRetry(query, params, maxRetries = 3) {
    let lastError;

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        // Execute query with timeout
        const result = await this.executeWithTimeout(query, params, 30000);
        return result;
      } catch (error) {
        lastError = error;

        // Retry on connection errors or timeouts
        if (this.shouldRetry(error) && attempt < maxRetries - 1) {
          const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }

        throw error;
      }
    }

    throw lastError;
  }

  async executeWithTimeout(query, params, timeout) {
    return new Promise(async (resolve, reject) => {
      const timeoutId = setTimeout(() => {
        reject(new Error(`Query timeout after ${timeout}ms`));
      }, timeout);

      try {
        const result = await this.database.query(query, params);
        clearTimeout(timeoutId);
        resolve(result);
      } catch (error) {
        clearTimeout(timeoutId);
        reject(error);
      }
    });
  }

  shouldRetry(error) {
    // Retry on connection errors, timeouts, or temporary failures
    return error.code === 'ECONNRESET' ||
           error.code === 'ETIMEDOUT' ||
           error.code === 'ENOTFOUND' ||
           error.message.includes('timeout') ||
           error.message.includes('connection');
  }

  shouldCacheResult(result) {
    // Don't cache large results or error results
    if (!result || result.error) {
      return false;
    }

    // Check result size
    const resultSize = JSON.stringify(result).length;
    if (resultSize > this.config.maxCacheSize * 1024) { // Convert KB to bytes
      return false;
    }

    return true;
  }

  generateQueryCacheKey(query, params) {
    // Generate deterministic cache key
    const queryHash = this.hashString(query);
    const paramsHash = this.hashString(JSON.stringify(params));
    return `query:${queryHash}:${paramsHash}`;
  }

  hashString(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32-bit integer
    }
    return hash.toString(36);
  }

  handleSlowQuery(query, params, executionTime) {
    const queryId = this.generateQueryCacheKey(query, params);

    if (!this.slowQueries.has(queryId)) {
      this.slowQueries.add(queryId);

      // Log slow query
      console.warn(`Slow query detected (${executionTime}ms):`, {
        query: query.substring(0, 100) + '...',
        params,
        executionTime
      });

      // Send alert
      this.metricsCollector.alert('slow_query', {
        query: query.substring(0, 100),
        executionTime,
        timestamp: new Date()
      });

      // Suggest optimization
      this.suggestQueryOptimization(query, params);
    }
  }

  async suggestQueryOptimization(query, params) {
    const suggestions = [];

    // Check for missing indexes
    const missingIndexes = await this.analyzeMissingIndexes(query);
    if (missingIndexes.length > 0) {
      suggestions.push({
        type: 'missing_index',
        description: 'Consider adding indexes for better performance',
        indexes: missingIndexes
      });
    }

    // Check for full table scans
    if (this.hasFullTableScan(query)) {
      suggestions.push({
        type: 'full_table_scan',
        description: 'Query may be performing full table scan',
        recommendation: 'Add WHERE clause or optimize query structure'
      });
    }

    // Check for N+1 query pattern
    if (this.hasNPlusOnePattern(query)) {
      suggestions.push({
        type: 'n_plus_one',
        description: 'Possible N+1 query pattern detected',
        recommendation: 'Consider using JOIN or batch queries'
      });
    }

    if (suggestions.length > 0) {
      console.log('Query optimization suggestions:', suggestions);
      this.metricsCollector.recordOptimizationSuggestions(suggestions);
    }
  }

  async analyzeMissingIndexes(query) {
    // This would typically involve database-specific analysis
    // Simplified example for demonstration
    const tables = this.extractTablesFromQuery(query);
    const missingIndexes = [];

    for (const table of tables) {
      // In a real implementation, this would query the database
      // for index usage statistics and query plans
      if (this.shouldHaveIndex(query, table)) {
        missingIndexes.push({
          table,
          columns: this.extractFilterColumns(query, table)
        });
      }
    }

    return missingIndexes;
  }

  extractTablesFromQuery(query) {
    // Simplified table extraction
    const tableRegex = /FROM\s+(\w+)/gi;
    const matches = [];
    let match;

    while ((match = tableRegex.exec(query)) !== null) {
      matches.push(match[1]);
    }

    return matches;
  }

  extractFilterColumns(query, table) {
    // Simplified column extraction
    const whereRegex = new RegExp(`WHERE\\s+${table}\\.(\\w+)\\s*=`, 'gi');
    const columns = [];
    let match;

    while ((match = whereRegex.exec(query)) !== null) {
      columns.push(match[1]);
    }

    return columns;
  }

  shouldHaveIndex(query, table) {
    // Simplified heuristic for index suggestion
    const hasWhereClause = query.toLowerCase().includes('where');
    const hasJoinClause = query.toLowerCase().includes('join');

    return hasWhereClause || hasJoinClause;
```
