Chuyển đến nội dung chính

Bài 3: Shift-left Performance Testing trong CI/CD

Performance testing trong CI/CD pipelines, unit-level assertions, contract-based testing, developer-owned performance gates.

🔒 DevSecOps — Bài 3 Bài 3: Shift-left Performance Testing trong CI/CD

Performance Testing & Pentest: Quy trình Chuẩn Doanh nghiệp 2026

Phần 1: Nền tảng Performance Testing

xdev.asia

1. Shift-left Performance Testing là gì?

Traditional:
  Dev → QA → Staging → Performance Test → Production
                                 ↑
                          Phát hiện muộn, fix tốn kém

Shift-left:
  Dev (perf unit test) → PR (perf gate) → CI (load test) → Staging → Production
       ↑                      ↑                ↑
  Phát hiện sớm,      Auto regression    Continuous
  fix rẻ              detection          validation

2. Unit-level Performance Assertions

// Jest - Microbenchmark trong unit test
describe('ProductService', () => {
  it('findAll should respond within 50ms for 100 items', async () => {
    // Setup: seed 100 products
    const start = performance.now();
    
    const result = await service.findAll({ limit: 100 });
    
    const duration = performance.now() - start;
    expect(duration).toBeLessThan(50); // < 50ms
    expect(result).toHaveLength(100);
  });

  it('search should handle 1000 items within 200ms', async () => {
    const start = performance.now();
    
    await service.search({ query: 'test', limit: 1000 });
    
    expect(performance.now() - start).toBeLessThan(200);
  });
});
# Python - pytest-benchmark
import pytest

def test_serialize_large_payload(benchmark):
    data = generate_large_payload(10_000)
    
    result = benchmark(serializer.serialize, data)
    
    assert benchmark.stats['mean'] < 0.05  # < 50ms mean
    assert benchmark.stats['max'] < 0.1    # < 100ms max

3. PR Performance Gate

# .github/workflows/pr-perf-check.yml
name: PR Performance Gate

on:
  pull_request:
    branches: [main, develop]

jobs:
  perf-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Start application
        run: |
          docker compose -f docker-compose.test.yml up -d
          ./scripts/wait-for-healthy.sh http://localhost:3000/health

      - name: Run k6 smoke test
        uses: grafana/[email protected]
        with:
          filename: tests/performance/smoke.js
          flags: --out json=results.json

      - name: Check thresholds
        run: |
          # Parse k6 results, fail PR nếu vi phạm thresholds
          node scripts/check-perf-thresholds.js results.json

      - name: Comment PR with results
        uses: actions/github-script@v7
        with:
          script: |
            const results = require('./perf-summary.json');
            const body = `## ⚡ Performance Test Results
            | Metric | Value | Threshold | Status |
            |--------|-------|-----------|--------|
            | p95 Latency | ${results.p95}ms | < 300ms | ${results.p95 < 300 ? '✅' : '❌'} |
            | p99 Latency | ${results.p99}ms | < 1000ms | ${results.p99 < 1000 ? '✅' : '❌'} |
            | Error Rate | ${results.errorRate}% | < 0.1% | ${results.errorRate < 0.1 ? '✅' : '❌'} |
            | RPS | ${results.rps} | > 500 | ${results.rps > 500 ? '✅' : '❌'} |`;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body
            });

4. k6 Smoke Test cho CI

// tests/performance/smoke.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  // Smoke test: nhẹ, chạy nhanh (< 2 phút)
  vus: 5,
  duration: '1m',
  
  thresholds: {
    http_req_duration: ['p(95)<300', 'p(99)<1000'],
    http_req_failed: ['rate<0.01'],    // < 1% errors
    checks: ['rate>0.99'],             // > 99% checks pass
  },
};

export default function () {
  // Test critical user journeys
  const responses = http.batch([
    ['GET', `${__ENV.BASE_URL}/api/products`, null, { tags: { name: 'list-products' } }],
    ['GET', `${__ENV.BASE_URL}/api/categories`, null, { tags: { name: 'list-categories' } }],
  ]);

  responses.forEach((res) => {
    check(res, {
      'status is 200': (r) => r.status === 200,
      'response time < 500ms': (r) => r.timings.duration < 500,
    });
  });

  sleep(1);
}

5. Performance Regression Detection

# Nightly full load test với regression detection
name: Nightly Performance Test

on:
  schedule:
    - cron: '0 2 * * *'  # 2 AM hàng ngày

jobs:
  load-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run full load test
        run: |
          k6 run tests/performance/load.js \
            --out json=current-results.json \
            -e BASE_URL=https://staging.example.com

      - name: Compare with baseline
        run: |
          # Download previous results
          aws s3 cp s3://perf-results/baseline.json previous.json
          
          # Detect regressions (> 20% degradation)
          node scripts/compare-perf.js previous.json current-results.json \
            --threshold 20 \
            --output regression-report.json

      - name: Alert on regression
        if: failure()
        run: |
          # Gửi Slack alert khi phát hiện regression
          curl -X POST $SLACK_WEBHOOK \
            -H 'Content-Type: application/json' \
            -d @regression-report.json

      - name: Update baseline (if improved)
        if: success()
        run: |
          aws s3 cp current-results.json s3://perf-results/baseline.json

6. Contract-based Performance Testing

# performance-contract.yml
contracts:
  - service: user-service
    endpoints:
      - path: "GET /api/users/:id"
        slo:
          p95_latency_ms: 100
          p99_latency_ms: 300
          max_rps: 5000
          error_rate: 0.001
        test:
          vus: 50
          duration: 2m

      - path: "POST /api/users"  
        slo:
          p95_latency_ms: 200
          p99_latency_ms: 500
          max_rps: 1000
          error_rate: 0.001
        test:
          vus: 20
          duration: 2m

  - service: order-service
    dependencies:
      - user-service
      - payment-service
    endpoints:
      - path: "POST /api/orders"
        slo:
          p95_latency_ms: 500
          p99_latency_ms: 2000
          max_rps: 200

7. Tổng kết

  • Shift-left: Phát hiện performance issues sớm nhất có thể
  • Unit-level: Microbenchmarks trong test suites
  • PR gates: k6 smoke test chạy trên mỗi PR
  • Nightly test: Full load test + regression detection
  • Contracts: Định nghĩa SLO cho từng endpoint, auto-validate

Bài tiếp theo sẽ tìm hiểu k6 — Load Testing Framework từ Grafana.