Skip to main content
โšก Calmops

Performance Testing

Performance Testing

Performance testing ensures applications meet performance requirements. This article covers testing strategies and tools.

Introduction

Performance testing provides:

  • Load testing
  • Stress testing
  • Profiling
  • Bottleneck identification
  • Optimization guidance

Understanding performance testing helps you:

  • Identify bottlenecks
  • Optimize applications
  • Ensure scalability
  • Improve user experience
  • Plan capacity

Load Testing

Apache JMeter

# โœ… Good: Install JMeter
# Download from https://jmeter.apache.org/

# โœ… Good: Run load test
jmeter -n -t test-plan.jmx -l results.jtl -j jmeter.log

# โœ… Good: Generate report
jmeter -g results.jtl -o report/

k6 Load Testing

// โœ… Good: Install k6
// npm install -g k6

// โœ… Good: k6 test script
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 20 },   // Ramp up
    { duration: '1m30s', target: 100 }, // Stay at 100
    { duration: '30s', target: 0 }      // Ramp down
  ]
};

export default function () {
  const response = http.get('http://localhost:3000/api/users');
  
  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500
  });

  sleep(1);
}

// โœ… Good: Run k6 test
// k6 run load-test.js

// โœ… Good: k6 with thresholds
export const options = {
  thresholds: {
    http_req_duration: ['p(95)<500', 'p(99)<1000'],
    http_req_failed: ['rate<0.1']
  }
};

Artillery Load Testing

# โœ… Good: Artillery config
config:
  target: 'http://localhost:3000'
  phases:
    - duration: 60
      arrivalRate: 10
    - duration: 120
      arrivalRate: 50
    - duration: 60
      arrivalRate: 10

scenarios:
  - name: 'Get Users'
    flow:
      - get:
          url: '/api/users'
      - think: 5
      - get:
          url: '/api/users/{{ userId }}'

  - name: 'Create User'
    flow:
      - post:
          url: '/api/users'
          json:
            name: 'John'
            email: '[email protected]'

Stress Testing

Stress Test Script

// โœ… Good: Stress test with k6
import http from 'k6/http';
import { check } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 100 },
    { duration: '5m', target: 100 },
    { duration: '2m', target: 200 },
    { duration: '5m', target: 200 },
    { duration: '2m', target: 300 },
    { duration: '5m', target: 300 },
    { duration: '2m', target: 0 }
  ],
  thresholds: {
    http_req_duration: ['p(99)<1000'],
    http_req_failed: ['rate<0.05']
  }
};

export default function () {
  const response = http.get('http://localhost:3000/api/users');
  
  check(response, {
    'status is 200': (r) => r.status === 200
  });
}

Profiling

Node.js Profiling

// โœ… Good: CPU profiling
const v8Profiler = require('v8-profiler-next');

v8Profiler.startProfiling('test');

// Run code to profile
for (let i = 0; i < 1000000; i++) {
  Math.sqrt(i);
}

const profile = v8Profiler.stopProfiling('test');
profile.export((err, result) => {
  require('fs').writeFileSync('profile.cpuprofile', result);
  profile.delete();
});

// โœ… Good: Memory profiling
const heapdump = require('heapdump');

// Trigger heap dump
heapdump.writeSnapshot(`./heap-${Date.now()}.heapsnapshot`);

// โœ… Good: Flame graphs
// npm install -g 0x
// 0x node app.js
// Open http://localhost:7002

Chrome DevTools Profiling

// โœ… Good: Profile in browser
// 1. Open DevTools (F12)
// 2. Go to Performance tab
// 3. Click Record
// 4. Perform actions
// 5. Click Stop
// 6. Analyze results

// โœ… Good: Lighthouse performance audit
// 1. Open DevTools
// 2. Go to Lighthouse tab
// 3. Click Generate report
// 4. Review recommendations

Benchmarking

Benchmark.js

// โœ… Good: Install Benchmark.js
// npm install benchmark

const Benchmark = require('benchmark');

const suite = new Benchmark.Suite;

// Add tests
suite
  .add('Array#indexOf', () => {
    [1, 2, 3, 4, 5].indexOf(3);
  })
  .add('Array#findIndex', () => {
    [1, 2, 3, 4, 5].findIndex(x => x === 3);
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
  })
  .run({ 'async': true });

Custom Benchmarking

// โœ… Good: Simple benchmark
function benchmark(fn, iterations = 1000000) {
  const start = process.hrtime.bigint();
  
  for (let i = 0; i < iterations; i++) {
    fn();
  }
  
  const end = process.hrtime.bigint();
  const duration = Number(end - start) / 1000000; // Convert to ms
  
  console.log(`${fn.name}: ${duration.toFixed(2)}ms`);
}

// โœ… Good: Compare implementations
benchmark(() => {
  const arr = [1, 2, 3, 4, 5];
  arr.indexOf(3);
}, 1000000);

benchmark(() => {
  const arr = [1, 2, 3, 4, 5];
  arr.find(x => x === 3);
}, 1000000);

Performance Optimization

Frontend Optimization

// โœ… Good: Measure performance
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}: ${entry.duration}ms`);
  }
});

observer.observe({ entryTypes: ['measure', 'navigation'] });

// โœ… Good: Mark and measure
performance.mark('start');
// Do work
performance.mark('end');
performance.measure('work', 'start', 'end');

// โœ… Good: Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);

Backend Optimization

// โœ… Good: Query optimization
// Bad: N+1 queries
const users = await User.find();
for (const user of users) {
  user.posts = await Post.find({ userId: user._id });
}

// Good: Use populate
const users = await User.find().populate('posts');

// โœ… Good: Caching
const cache = new Map();

async function getUser(id) {
  if (cache.has(id)) {
    return cache.get(id);
  }
  
  const user = await User.findById(id);
  cache.set(id, user);
  
  return user;
}

// โœ… Good: Connection pooling
const pool = new Pool({
  max: 20,
  idleTimeoutMillis: 30000
});

Performance Monitoring

Real User Monitoring

// โœ… Good: Send performance data
function sendPerformanceData() {
  const perfData = performance.getEntriesByType('navigation')[0];
  
  const data = {
    dns: perfData.domainLookupEnd - perfData.domainLookupStart,
    tcp: perfData.connectEnd - perfData.connectStart,
    ttfb: perfData.responseStart - perfData.requestStart,
    download: perfData.responseEnd - perfData.responseStart,
    domInteractive: perfData.domInteractive - perfData.fetchStart,
    domComplete: perfData.domComplete - perfData.fetchStart,
    loadComplete: perfData.loadEventEnd - perfData.fetchStart
  };
  
  fetch('/api/performance', {
    method: 'POST',
    body: JSON.stringify(data)
  });
}

window.addEventListener('load', sendPerformanceData);

Best Practices

  1. Test realistic scenarios:

    // โœ… Good: Realistic load test
    // Simulate real user behavior
    // Use realistic data
    // Test peak hours
    
    // โŒ Bad: Unrealistic test
    // Simple requests
    // Minimal data
    // No variation
    
  2. Monitor key metrics:

    // โœ… Good: Monitor key metrics
    // - Response time (p50, p95, p99)
    // - Throughput (requests/sec)
    // - Error rate
    // - Resource usage
    
    // โŒ Bad: No monitoring
    
  3. Establish baselines:

    // โœ… Good: Establish baselines
    // Measure current performance
    // Set targets
    // Track improvements
    
    // โŒ Bad: No baselines
    

Summary

Performance testing is essential. Key takeaways:

  • Conduct load testing
  • Perform stress testing
  • Profile applications
  • Benchmark code
  • Monitor performance
  • Optimize bottlenecks
  • Establish baselines
  • Test continuously

Next Steps

Comments