| # Load Testing with k6 | |
| This directory contains k6 load tests for validating Atom API performance under concurrent user load. | |
| ## Overview | |
| Load testing simulates multiple concurrent users interacting with the API to identify performance bottlenecks, establish capacity limits, and validate that performance benchmarks hold under load. | |
| **Why k6?** | |
| - Modern, developer-friendly load testing tool | |
| - JavaScript-based test scripts (easy to extend) | |
| - Great CI/CD integration | |
| - Built-in metrics and thresholds | |
| - Cloud-based execution available (k6 Cloud) | |
| **Why Add k6 Alongside Locust?** | |
| - **k6**: Better for CI/CD automation, simpler syntax, built-in thresholds | |
| - **Locust**: Better for interactive testing, web UI, distributed load testing | |
| ## Prerequisites | |
| ### 1. Install k6 | |
| **macOS (Homebrew):** | |
| ```bash | |
| brew install k6 | |
| ``` | |
| **Linux:** | |
| ```bash | |
| curl https://github.com/grafana/k6/releases/download/v0.51.0/k6-v0.51.0-linux-amd64.tar.gz -L | tar xvz | |
| sudo mv k6-v0.51.0-linux-amd64/k6 /usr/local/bin/ | |
| ``` | |
| **Windows:** | |
| ```bash | |
| chocolatey install k6 | |
| ``` | |
| **Verify installation:** | |
| ```bash | |
| k6 version | |
| ``` | |
| ### 2. Start Application | |
| Ensure the Atom backend is running: | |
| ```bash | |
| cd backend | |
| python -m uvicorn main:app --host 0.0.0.0 --port 8000 | |
| ``` | |
| ### 3. Create Test User (Optional) | |
| Load tests use `load_test@example.com` with password `test_password_123`. Create this user via: | |
| ```bash | |
| curl -X POST http://localhost:8000/api/auth/register \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"email": "load_test@example.com", "password": "test_password_123"}' | |
| ``` | |
| ## Running Tests | |
| ### Quick Test (Development) | |
| Run a quick test for fast feedback: | |
| ```bash | |
| cd backend/tests/load | |
| k6 run test_api_load_baseline.js --duration 30s --vus 5 | |
| ``` | |
| ### Full Baseline Test (10 Users) | |
| Establish baseline performance: | |
| ```bash | |
| cd backend/tests/load | |
| k6 run test_api_load_baseline.js | |
| ``` | |
| **Duration:** 6 minutes (2m ramp-up, 3m sustained, 1m ramp-down) | |
| **Users:** 10 concurrent | |
| **Thresholds:** p(95)<500ms, error rate <5% | |
| ### Moderate Load Test (50 Users) | |
| Test under moderate concurrent load: | |
| ```bash | |
| cd backend/tests/load | |
| k6 run test_api_load_moderate.js | |
| ``` | |
| **Duration:** 17 minutes (5m ramp-up, 10m sustained, 2m ramp-down) | |
| **Users:** 50 concurrent | |
| **Thresholds:** p(95)<800ms, error rate <10% | |
| ### High Load Test (100 Users) | |
| Test under high concurrent load: | |
| ```bash | |
| cd backend/tests/load | |
| k6 run test_api_load_high.js | |
| ``` | |
| **Duration:** 28 minutes (10m ramp-up, 15m sustained, 3m ramp-down) | |
| **Users:** 100 concurrent | |
| **Thresholds:** p(95)<1200ms, error rate <15% | |
| ### Web UI Load Test (20 Users) | |
| Test realistic web UI user flows: | |
| ```bash | |
| cd backend/tests/load | |
| k6 run test_web_ui_load.js | |
| ``` | |
| **Duration:** 9 minutes (3m ramp-up, 5m sustained, 1m ramp-down) | |
| **Users:** 20 concurrent | |
| **Thresholds:** p(95)<1000ms, error rate <10% | |
| ## Environment Variables | |
| Configure the API URL: | |
| ```bash | |
| # Default: http://localhost:8000 | |
| export API_URL="http://localhost:8000" | |
| # Run with custom API URL | |
| API_URL="http://staging.example.com" k6 run test_api_load_baseline.js | |
| ``` | |
| ## Interpreting Results | |
| ### Key Metrics | |
| **Requests Per Second (RPS):** | |
| - Measure of throughput | |
| - Higher is better (indicates capacity) | |
| - Target: Maintain RPS as user count increases | |
| **Response Time Percentiles:** | |
| - **P50 (Median)**: 50% of requests complete faster than this | |
| - **P95**: 95% of requests complete faster than this (SLA target) | |
| - **P99**: 99% of requests complete faster than this (tail latency) | |
| **Failure Rate:** | |
| - Percentage of failed requests (non-2xx status codes) | |
| - Target: <5% for baseline, <15% for high load | |
| - Common failures: 500 (server error), 503 (service unavailable) | |
| **Checks:** | |
| - Custom validation checks (e.g., "login successful", "agent execution started") | |
| - Pass rate indicates percentage of checks that passed | |
| - Target: >90% for baseline, >85% for high load | |
| ### Example Output | |
| ``` | |
| Baseline Load Test Summary | |
| ======================== | |
| Total Requests: 1234 | |
| Failed Requests: 45 | |
| Failure Rate: 3.65% | |
| Response Times: | |
| P50: 180ms | |
| P95: 420ms | |
| P99: 780ms | |
| Checks: | |
| Passed: 1150 | |
| Failed: 84 | |
| Pass Rate: 93.19% | |
| running (06.0s), 000/10 VUs, 1234 complete and 0 interrupted iterations | |
| ✓ /api/auth/login [ 96% ] | |
| ✓ has status 200 | |
| ✓ received token | |
| ✓ /api/v1/agents/execute [ 91% ] | |
| ✓ agent execution started | |
| ``` | |
| ### Performance Indicators | |
| **Good Performance:** | |
| - P95 response time meets thresholds | |
| - Failure rate below target | |
| - RPS scales linearly with user count | |
| - No response time spikes as user count increases | |
| **Performance Issues:** | |
| - P95 response time increases with user count (bottleneck) | |
| - Failure rate exceeds target (capacity limit) | |
| - RPS plateaus or decreases (system overload) | |
| - Response time spikes during test (resource exhaustion) | |
| ## CI/CD Integration | |
| ### GitHub Actions Workflow | |
| ```yaml | |
| name: Load Tests (k6) | |
| on: | |
| schedule: | |
| # Run daily at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| pull_request: | |
| paths: | |
| - 'backend/core/**' | |
| - 'backend/api/**' | |
| jobs: | |
| load-test: | |
| name: Run Load Tests | |
| runs-on: ubuntu-large | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install k6 | |
| run: | | |
| curl https://github.com/grafana/k6/releases/download/v0.51.0/k6-v0.51.0-linux-amd64.tar.gz -L | tar xvz | |
| sudo mv k6-v0.51.0-linux-amd64/k6 /usr/local/bin/ | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| cd backend | |
| pip install -r requirements.txt | |
| - name: Start application | |
| run: | | |
| cd backend | |
| python -m uvicorn main:app --host 0.0.0.0 --port 8000 & | |
| sleep 10 | |
| - name: Run baseline load test | |
| run: | | |
| cd backend/tests/load | |
| k6 run test_api_load_baseline.js --duration 2m --vus 10 | |
| - name: Upload load test results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: k6-load-test-results | |
| path: backend/tests/load/reports/ | |
| ``` | |
| ### Quick Smoke Test for CI | |
| For faster CI feedback, run a 1-minute smoke test: | |
| ```bash | |
| k6 run test_api_load_baseline.js --duration 1m --vus 5 | |
| ``` | |
| ## Test Scenarios | |
| ### test_api_load_baseline.js (10 Users) | |
| **Scenarios:** | |
| - 60% Authentication flow (login) | |
| - 40% Agent execution (list + execute) | |
| **Purpose:** Establish baseline performance metrics | |
| ### test_api_load_moderate.js (50 Users) | |
| **Scenarios:** | |
| - 50% Authentication (login, logout) | |
| - 30% Agent execution (GET agents, POST execute) | |
| - 20% Canvas operations (GET canvas, POST present) | |
| **Purpose:** Validate system performance under moderate load | |
| ### test_api_load_high.js (100 Users) | |
| **Scenarios:** | |
| - 40% Authentication | |
| - 35% Agent execution | |
| - 15% Canvas operations | |
| - 10% Workflow execution | |
| **Purpose:** Identify system breaking point and capacity limits | |
| ### test_web_ui_load.js (20 Users) | |
| **User Flow:** | |
| 1. GET / (load homepage) | |
| 2. POST /api/auth/login (authenticate) | |
| 3. GET /dashboard (load dashboard) | |
| 4. GET /api/v1/agents (list agents) | |
| 5. POST /api/v1/agents/execute (execute agent) | |
| 6. GET /api/v1/canvas/{id} (view canvas) | |
| 7. POST /api/v1/canvas/present (present canvas) | |
| 8. GET /api/v1/workflows (list workflows, optional) | |
| 9. POST /api/auth/logout (logout, optional) | |
| **Purpose:** Simulate realistic web UI user behavior | |
| ## Troubleshooting | |
| ### k6 Not Found | |
| **Symptom:** `k6: command not found` | |
| **Solution:** | |
| ```bash | |
| # macOS | |
| brew install k6 | |
| # Linux | |
| curl https://github.com/grafana/k6/releases/download/v0.51.0/k6-v0.51.0-linux-amd64.tar.gz -L | tar xvz | |
| sudo mv k6-v0.51.0-linux-amd64/k6 /usr/local/bin/ | |
| ``` | |
| ### Server Not Running | |
| **Symptom:** Connection refused errors | |
| **Solution:** | |
| ```bash | |
| # Check if server is running | |
| curl http://localhost:8000/health/live | |
| # Start server if not running | |
| cd backend | |
| python -m uvicorn main:app --host 0.0.0.0 --port 8000 | |
| ``` | |
| ### Authentication Failures | |
| **Symptom:** 401 Unauthorized errors | |
| **Solution:** | |
| - Load tests use `load_test@example.com` with password `test_password_123` | |
| - Ensure test user exists in database | |
| - Check authentication endpoint is working: | |
| ```bash | |
| curl -X POST http://localhost:8000/api/auth/login \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"email": "load_test@example.com", "password": "test_password_123"}' | |
| ``` | |
| ### High Failure Rate | |
| **Symptom:** >15% failure rate with 500 errors | |
| **Possible Causes:** | |
| 1. **Database overload**: Reduce concurrent users, increase pool size | |
| 2. **Memory pressure**: Check server memory usage, reduce user count | |
| 3. **Rate limiting**: Check if API has rate limits configured | |
| 4. **External service failures**: Check LLM provider, external APIs | |
| **Debugging:** | |
| ```bash | |
| # Check server logs | |
| tail -f logs/atom.log | grep ERROR | |
| # Monitor server resources | |
| htop # or top on macOS | |
| # Check database connections | |
| # For PostgreSQL: | |
| psql -c "SELECT count(*) FROM pg_stat_activity;" | |
| ``` | |
| ### Slow Response Times | |
| **Symptom:** P95 > 2x target thresholds | |
| **Possible Causes:** | |
| 1. **Cache misses**: Cold cache, low hit rate | |
| 2. **Database queries**: Missing indexes, N+1 queries | |
| 3. **Network latency**: Local vs remote database | |
| 4. **Resource contention**: CPU, memory, disk I/O | |
| **Debugging:** | |
| ```bash | |
| # Check cache hit rate | |
| curl http://localhost:8000/api/agent-governance/cache-stats | |
| # Profile database queries | |
| # Enable query logging in SQLAlchemy | |
| export SQLALCHEMY_ECHO=true | |
| # Run with profiling | |
| python -m cProfile -o profile.stats -m uvicorn main:app | |
| ``` | |
| ## Best Practices | |
| 1. **Warm up the cache**: Run for 1-2 minutes before collecting metrics | |
| 2. **Start small**: Begin with 10 users, scale up gradually | |
| 3. **Monitor resources**: Watch CPU, memory, database connections during test | |
| 4. **Use realistic data**: Test with production-like data volumes | |
| 5. **Run multiple times**: Performance can vary due to system load | |
| 6. **Compare to baseline**: Track performance over time to detect regressions | |
| 7. **Test in staging**: Never run load tests in production without limits | |
| 8. **Automate in CI**: Run smoke tests on every PR, full tests nightly | |
| ## Next Steps | |
| After running load tests: | |
| 1. Review HTML report for detailed metrics (if using Locust) | |
| 2. Compare P95 times to target thresholds | |
| 3. Identify bottlenecks (database, cache, network) | |
| 4. Optimize slow endpoints | |
| 5. Re-run load tests to validate improvements | |
| 6. Update baseline metrics if performance improved | |
| ## References | |
| - **k6 Documentation**: https://k6.io/docs/ | |
| - **k6 Examples**: https://k6.io/docs/examples/ | |
| - **Locust Load Tests**: `README.md` (Locust-based tests) | |
| - **Phase 208 Benchmarks**: `.planning/phases/208-integration-performance-testing/208-07-PERFORMANCE-METRICS.md` | |
| - **Monitoring Setup**: `backend/docs/MONITORING_SETUP.md` | |
| ## Comparison: k6 vs Locust | |
| | Feature | k6 | Locust | | |
| |---------|----|----| | |
| | Language | JavaScript | Python | | |
| | Learning Curve | Low (JS familiarity) | Low (Python familiarity) | | |
| | CI/CD Integration | Excellent (native CLI) | Good (requires web UI or headless mode) | | |
| | Interactive Web UI | No | Yes (built-in) | | |
| | Distributed Load | k6 Cloud (paid) | Built-in (master/worker) | | |
| | Thresholds | Built-in | Manual checks | | |
| | Best For | CI/CD, automation | Interactive testing, distributed load | | |
| **Recommendation:** Use k6 for CI/CD automation and Locust for interactive testing/distributed load. | |