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.