Troubleshooting

Diagnose and resolve common deployment issues and production problems.

Troubleshooting

Common Deployment Issues

Build Failures

Diagnose and fix build-time problems.

Missing Dependencies

# Error: Module not found
npm list --depth=0  # Check installed packages
npm install --save missing-package

# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm cache clean --force
npm install

Environment Variable Issues

// lib/env-validation.ts
import { z } from 'zod'

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  NEXTAUTH_SECRET: z.string().min(32),
  NEXTAUTH_URL: z.string().url(),
})

export function validateEnvironment() {
  try {
    const env = envSchema.parse(process.env)
    console.log('✅ Environment variables validated')
    return env
  } catch (error) {
    console.error('❌ Environment validation failed:')
    if (error instanceof z.ZodError) {
      error.errors.forEach(err => {
        console.error(`  ${err.path.join('.')}: ${err.message}`)
      })
    }
    process.exit(1)
  }
}

Memory Issues During Build

// next.config.mjs - Increase memory for build
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Increase memory limit for build
  experimental: {
    craCompat: true,
    esmExternals: false,
  },
  
  // Optimize bundle size
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        fs: false,
        path: false,
        os: false,
      }
    }
    
    // Split chunks to reduce memory usage
    config.optimization.splitChunks = {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    }
    
    return config
  },
}

export default nextConfig

Runtime Issues

Application Crashes

Debug and resolve application crashes.

Memory Leaks

// lib/memory-monitor.ts
export class MemoryMonitor {
  private intervalId: NodeJS.Timeout | null = null
  
  start(): void {
    this.intervalId = setInterval(() => {
      const usage = process.memoryUsage()
      const heapUsedMB = Math.round(usage.heapUsed / 1024 / 1024)
      const heapTotalMB = Math.round(usage.heapTotal / 1024 / 1024)
      
      console.log(`Memory usage: ${heapUsedMB}MB / ${heapTotalMB}MB`)
      
      // Alert if memory usage is too high
      if (heapUsedMB > 500) {
        console.warn('⚠️ High memory usage detected:', heapUsedMB, 'MB')
        
        // Force garbage collection if available
        if (global.gc) {
          global.gc()
          console.log('🧹 Garbage collection triggered')
        }
      }
    }, 30000) // Check every 30 seconds
  }
  
  stop(): void {
    if (this.intervalId) {
      clearInterval(this.intervalId)
      this.intervalId = null
    }
  }
}

// Start monitoring in production
if (process.env.NODE_ENV === 'production') {
  const monitor = new MemoryMonitor()
  monitor.start()
  
  // Cleanup on exit
  process.on('SIGTERM', () => monitor.stop())
  process.on('SIGINT', () => monitor.stop())
}

Unhandled Exceptions

// lib/error-handler.ts
export function setupGlobalErrorHandling(): void {
  // Handle uncaught exceptions
  process.on('uncaughtException', (error) => {
    console.error('Uncaught Exception:', error)
    
    // Log to external service
    logErrorToService(error, 'uncaughtException')
    
    // Graceful shutdown
    process.exit(1)
  })
  
  // Handle unhandled promise rejections
  process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection at:', promise, 'reason:', reason)
    
    // Log to external service
    logErrorToService(new Error(String(reason)), 'unhandledRejection')
  })
}

async function logErrorToService(error: Error, type: string): Promise<void> {
  try {
    await fetch('/api/log-error', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message: error.message,
        stack: error.stack,
        type,
        timestamp: new Date().toISOString(),
      }),
    })
  } catch (logError) {
    console.error('Failed to log error to service:', logError)
  }
}

Database Connection Issues

Resolve database connectivity problems.

Connection Pool Exhaustion

// lib/db-diagnostics.ts
import { Pool } from 'pg'

export class DatabaseDiagnostics {
  constructor(private pool: Pool) {}
  
  async getConnectionStats(): Promise<any> {
    return {
      totalConnections: this.pool.totalCount,
      idleConnections: this.pool.idleCount,
      waitingClients: this.pool.waitingCount,
    }
  }
  
  async testConnection(): Promise<boolean> {
    try {
      const client = await this.pool.connect()
      await client.query('SELECT 1')
      client.release()
      return true
    } catch (error) {
      console.error('Database connection test failed:', error)
      return false
    }
  }
  
  async getLongRunningQueries(): Promise<any[]> {
    const query = `
      SELECT 
        pid,
        query,
        state,
        query_start,
        NOW() - query_start AS duration
      FROM pg_stat_activity
      WHERE state = 'active'
        AND NOW() - query_start > INTERVAL '5 minutes'
      ORDER BY duration DESC
    `
    
    try {
      const result = await this.pool.query(query)
      return result.rows
    } catch (error) {
      console.error('Failed to get long running queries:', error)
      return []
    }
  }
}

Connection Timeouts

// lib/db-retry.ts
export async function withRetry<T>(
  operation: () => Promise<T>,
  maxRetries = 3,
  delay = 1000
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation()
    } catch (error) {
      if (attempt === maxRetries) {
        throw error
      }
      
      console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms...`)
      await new Promise(resolve => setTimeout(resolve, delay))
      delay *= 2 // Exponential backoff
    }
  }
  
  throw new Error('Max retries exceeded')
}

// Usage example
const result = await withRetry(async () => {
  return await db.query('SELECT * FROM users WHERE id = $1', [userId])
}, 3, 1000)

Performance Issues

Slow Response Times

Identify and resolve performance bottlenecks.

API Response Analysis

// lib/performance-monitor.ts
export class PerformanceMonitor {
  private metrics = new Map<string, number[]>()
  
  measureApiCall(endpoint: string) {
    return (target: any, propertyName: string, descriptor: PropertyDescriptor) => {
      const originalMethod = descriptor.value
      
      descriptor.value = async function(...args: any[]) {
        const startTime = Date.now()
        
        try {
          const result = await originalMethod.apply(this, args)
          const duration = Date.now() - startTime
          
          this.recordMetric(endpoint, duration)
          
          if (duration > 2000) {
            console.warn(`⚠️ Slow API call: ${endpoint} took ${duration}ms`)
          }
          
          return result
        } catch (error) {
          const duration = Date.now() - startTime
          console.error(`❌ API call failed: ${endpoint} (${duration}ms)`, error)
          throw error
        }
      }
    }
  }
  
  private recordMetric(endpoint: string, duration: number): void {
    if (!this.metrics.has(endpoint)) {
      this.metrics.set(endpoint, [])
    }
    
    const measurements = this.metrics.get(endpoint)!
    measurements.push(duration)
    
    // Keep only last 100 measurements
    if (measurements.length > 100) {
      measurements.shift()
    }
  }
  
  getAverageResponseTime(endpoint: string): number {
    const measurements = this.metrics.get(endpoint) || []
    if (measurements.length === 0) return 0
    
    const sum = measurements.reduce((a, b) => a + b, 0)
    return sum / measurements.length
  }
  
  getPercentile(endpoint: string, percentile: number): number {
    const measurements = this.metrics.get(endpoint) || []
    if (measurements.length === 0) return 0
    
    const sorted = [...measurements].sort((a, b) => a - b)
    const index = Math.ceil((percentile / 100) * sorted.length) - 1
    return sorted[index]
  }
}

Database Query Optimization

-- Find slow queries
SELECT 
  query,
  calls,
  total_time,
  mean_time,
  rows
FROM pg_stat_statements
WHERE mean_time > 1000  -- Queries taking more than 1 second
ORDER BY mean_time DESC
LIMIT 10;

-- Check for missing indexes
SELECT 
  schemaname,
  tablename,
  seq_scan,
  seq_tup_read,
  idx_scan,
  idx_tup_fetch
FROM pg_stat_user_tables
WHERE seq_scan > idx_scan
  AND seq_tup_read > 10000
ORDER BY seq_tup_read DESC;

-- Find unused indexes
SELECT 
  schemaname,
  tablename,
  indexname,
  idx_scan,
  idx_tup_read,
  idx_tup_fetch
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

Diagnostic Tools

Health Check Endpoint

Implement comprehensive health monitoring.

// pages/api/health.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import { DatabaseDiagnostics } from '../../lib/db-diagnostics'

interface HealthStatus {
  status: 'healthy' | 'unhealthy' | 'degraded'
  timestamp: string
  checks: {
    database: boolean
    memory: boolean
    disk: boolean
  }
  metrics: {
    uptime: number
    memoryUsage: NodeJS.MemoryUsage
    responseTime: number
  }
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<HealthStatus>
) {
  const startTime = Date.now()
  
  try {
    // Database check
    const dbDiagnostics = new DatabaseDiagnostics(db)
    const dbHealthy = await dbDiagnostics.testConnection()
    
    // Memory check
    const memoryUsage = process.memoryUsage()
    const memoryHealthy = memoryUsage.heapUsed / memoryUsage.heapTotal < 0.9
    
    // Disk check (simplified)
    const diskHealthy = true // Implement disk space check
    
    const allHealthy = dbHealthy && memoryHealthy && diskHealthy
    const status: HealthStatus = {
      status: allHealthy ? 'healthy' : 'degraded',
      timestamp: new Date().toISOString(),
      checks: {
        database: dbHealthy,
        memory: memoryHealthy,
        disk: diskHealthy,
      },
      metrics: {
        uptime: process.uptime(),
        memoryUsage,
        responseTime: Date.now() - startTime,
      },
    }
    
    res.status(allHealthy ? 200 : 503).json(status)
  } catch (error) {
    const status: HealthStatus = {
      status: 'unhealthy',
      timestamp: new Date().toISOString(),
      checks: {
        database: false,
        memory: false,
        disk: false,
      },
      metrics: {
        uptime: process.uptime(),
        memoryUsage: process.memoryUsage(),
        responseTime: Date.now() - startTime,
      },
    }
    
    res.status(503).json(status)
  }
}

Log Analysis

Analyze application logs for issues.

#!/bin/bash
# log-analysis.sh

LOG_FILE="/var/log/app.log"
REPORT_FILE="/tmp/log-report.txt"

echo "Application Log Analysis Report" > $REPORT_FILE
echo "Generated: $(date)" >> $REPORT_FILE
echo "======================================" >> $REPORT_FILE

# Error count by type
echo "" >> $REPORT_FILE
echo "Error Count by Type:" >> $REPORT_FILE
grep -i "error" $LOG_FILE | awk '{print $3}' | sort | uniq -c | sort -nr >> $REPORT_FILE

# Response time analysis
echo "" >> $REPORT_FILE
echo "Slow Requests (>2s):" >> $REPORT_FILE
grep "response_time" $LOG_FILE | awk '$NF > 2000 {print}' | head -20 >> $REPORT_FILE

# Memory usage peaks
echo "" >> $REPORT_FILE
echo "High Memory Usage:" >> $REPORT_FILE
grep "memory_usage" $LOG_FILE | awk '$NF > 500 {print}' | tail -10 >> $REPORT_FILE

# Database connection issues
echo "" >> $REPORT_FILE
echo "Database Connection Issues:" >> $REPORT_FILE
grep -i "database.*error\|connection.*failed" $LOG_FILE | tail -10 >> $REPORT_FILE

echo "Log analysis complete. Report saved to $REPORT_FILE"

Diagnose and resolve deployment issues quickly with systematic troubleshooting approaches and diagnostic tools.