Load testing and capacity planning are critical for ensuring your web application can handle expected traffic while maintaining performance. The process involves simulating user requests to identify bottlenecks and determine the infrastructure needed to support your application at scale.
- Response Time: How long it takes for the server to respond to a request
- Throughput: Number of requests processed per second
- Error Rate: Percentage of failed requests
- Resource Utilization: CPU, memory, and network usage
JMeter
Apache JMeter is one of the most widely used open-source tools for load testing. Here's a basic test plan configuration:
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Web Load Test" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
</TestPlan>
Locust
Locust is a Python-based tool that allows you to write user scenarios as code:
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 5)
@task
def load_homepage(self):
self.client.get("/")
@task(3)
def browse_products(self):
self.client.get("/products")
Effective capacity planning involves:
- Establishing performance baselines
- Projecting future growth
- Identifying resource requirements
- Creating scaling strategies
When working with cloud providers like AWS or Azure:
- Use auto-scaling groups to handle traffic spikes
- Implement load balancers to distribute traffic
- Consider serverless options for unpredictable workloads
For an e-commerce site expecting 10,000 concurrent users during peak:
// Sample calculation for required instances
const requiredInstances = Math.ceil(
(expectedConcurrentUsers * averageRequestTime) /
(targetResponseTime * requestsPerSecondPerInstance)
);
Integrate load testing into your CI/CD pipeline:
# Sample GitHub Actions workflow
name: Load Test
on: [push]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Locust
run: |
pip install locust
locust -f locustfile.py --headless -u 100 -r 10 --run-time 1m
Don't forget to test database performance under load:
- Monitor query execution times
- Optimize indexes
- Consider read replicas for heavy read workloads
Key aspects to examine in your test results:
Metric | Acceptable Range |
---|---|
Response Time | < 2s for 95% of requests |
Error Rate | < 1% |
CPU Usage | < 70% peak |
When planning for web application capacity, we need to consider both immediate performance requirements and future scalability. The key metrics to monitor include:
- Requests per second (RPS)
- Response time percentiles (p50, p90, p99)
- Concurrent user sessions
- Resource utilization (CPU, memory, I/O)
Here are some industry-standard tools with example configurations:
// Example JMeter test plan snippet
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="User Load Simulation" enabled="true">
<intProp name="ThreadGroup.num_threads">100</intProp>
<intProp name="ThreadGroup.ramp_time">60</intProp>
<longProp name="ThreadGroup.duration">300</longProp>
</ThreadGroup>
For JavaScript-based applications, k6 provides excellent testing capabilities:
// k6 load test script example
import http from 'k6/http';
import { check, sleep } from 'k6';
export let options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 100 },
{ duration: '30s', target: 0 },
],
};
export default function () {
let res = http.get('https://api.example.com/v1/users');
check(res, { 'status was 200': (r) => r.status == 200 });
sleep(1);
}
The process typically follows these steps:
- Establish performance baselines with current traffic
- Identify current bottlenecks (CPU, memory, database, network)
- Project future growth requirements
- Simulate expected load patterns
- Determine infrastructure requirements
For AWS environments, use CloudWatch metrics with Auto Scaling:
# AWS CLI to set up scaling policy
aws autoscaling put-scaling-policy \
--policy-name cpu-scale-up \
--auto-scaling-group-name web-asg \
--scaling-adjustment 2 \
--adjustment-type ChangeInCapacity \
--cooldown 300
Database performance often becomes the limiting factor. Consider:
- Read replicas for read-heavy workloads
- Sharding for large datasets
- Connection pool optimization
// Example connection pool configuration for PostgreSQL
const pool = new Pool({
user: 'dbuser',
host: 'database.server.com',
database: 'mydb',
password: 'secretpassword',
port: 5432,
max: 20, // maximum number of clients in the pool
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
Here's how we implemented capacity planning for a Node.js API service:
// Load testing middleware for Express
app.use((req, res, next) => {
const start = process.hrtime();
res.on('finish', () => {
const duration = process.hrtime(start);
const ms = duration[0] * 1000 + duration[1] / 1e6;
metrics.timing('http_request', ms);
metrics.increment(`http.${req.method}.${res.statusCode}`);
});
next();
});
Essential metrics to monitor continuously:
Metric | Threshold | Tool |
---|---|---|
CPU Utilization | 70% | New Relic |
Memory Usage | 80% | Datadog |
API Latency (p99) | 500ms | Prometheus |
Error Rate | 1% | Grafana |