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):
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/
Windows:
chocolatey install k6
Verify installation:
k6 version
2. Start Application
Ensure the Atom backend is running:
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:
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:
cd backend/tests/load
k6 run test_api_load_baseline.js --duration 30s --vus 5
Full Baseline Test (10 Users)
Establish baseline performance:
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:
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:
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:
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:
# 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
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:
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:
- GET / (load homepage)
- POST /api/auth/login (authenticate)
- GET /dashboard (load dashboard)
- GET /api/v1/agents (list agents)
- POST /api/v1/agents/execute (execute agent)
- GET /api/v1/canvas/{id} (view canvas)
- POST /api/v1/canvas/present (present canvas)
- GET /api/v1/workflows (list workflows, optional)
- POST /api/auth/logout (logout, optional)
Purpose: Simulate realistic web UI user behavior
Troubleshooting
k6 Not Found
Symptom: k6: command not found
Solution:
# 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:
# 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.comwith passwordtest_password_123 - Ensure test user exists in database
- Check authentication endpoint is working:
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:
- Database overload: Reduce concurrent users, increase pool size
- Memory pressure: Check server memory usage, reduce user count
- Rate limiting: Check if API has rate limits configured
- External service failures: Check LLM provider, external APIs
Debugging:
# 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:
- Cache misses: Cold cache, low hit rate
- Database queries: Missing indexes, N+1 queries
- Network latency: Local vs remote database
- Resource contention: CPU, memory, disk I/O
Debugging:
# 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
- Warm up the cache: Run for 1-2 minutes before collecting metrics
- Start small: Begin with 10 users, scale up gradually
- Monitor resources: Watch CPU, memory, database connections during test
- Use realistic data: Test with production-like data volumes
- Run multiple times: Performance can vary due to system load
- Compare to baseline: Track performance over time to detect regressions
- Test in staging: Never run load tests in production without limits
- Automate in CI: Run smoke tests on every PR, full tests nightly
Next Steps
After running load tests:
- Review HTML report for detailed metrics (if using Locust)
- Compare P95 times to target thresholds
- Identify bottlenecks (database, cache, network)
- Optimize slow endpoints
- Re-run load tests to validate improvements
- 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.