Skip to content

Commit ec8e141

Browse files
Add load testing documentation for Student Management API
Documented load testing procedures and results for the Student Management API, including setup, execution, and observations.
1 parent da50d8e commit ec8e141

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

docs/app/api-load-testing.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Load Testing - Student Management API
2+
3+
## Overview
4+
5+
This directory contains load testing scripts for the Student Management API. We use Locust to simulate concurrent users and measure API performance under load.
6+
7+
The load test covers the following endpoints:
8+
9+
- `GET /students` — Retrieve all students
10+
- `POST /students` — Create a new student
11+
12+
## Prerequisites
13+
14+
- Python 3.10+
15+
- Locust 2.x
16+
- The Flask API must be running locally or remotely
17+
18+
Install Locust if not already installed:
19+
20+
```bash
21+
pip install locust
22+
```
23+
24+
## Running the Load Test
25+
26+
1. Start your Flask API:
27+
28+
```bash
29+
export FLASK_APP=app:create_app
30+
export FLASK_ENV=development
31+
flask run --host=0.0.0.0 --port=5000
32+
```
33+
34+
2. Open a new terminal and navigate to the `tests/` directory:
35+
36+
```bash
37+
cd tests
38+
```
39+
40+
3. Run Locust:
41+
42+
```bash
43+
locust -f load_test.py --host=http://localhost:5000 --web-host 0.0.0.0
44+
```
45+
46+
4. Access the web interface in your browser and specify number of users and spawn rate:
47+
48+
```
49+
http://<your-server-ip>:8089
50+
```
51+
52+
Start the test from the web UI.
53+
54+
## Load Test Script (tests/load_test.py)
55+
56+
```python
57+
from locust import HttpUser, task, between
58+
import random
59+
60+
class StudentApiUser(HttpUser):
61+
wait_time = between(1, 2)
62+
63+
@task(2)
64+
def get_students(self):
65+
self.client.get("/students")
66+
67+
@task(1)
68+
def create_student(self):
69+
student_id = random.randint(1000, 9999)
70+
payload = {
71+
"name": f"Test User {student_id}",
72+
"domain": "Engineering",
73+
"gpa": round(random.uniform(6.0, 10.0), 2),
74+
"email": f"testuser{student_id}@example.com"
75+
}
76+
self.client.post("/students", json=payload)
77+
```
78+
79+
## Observations from Load Testing
80+
81+
The load test was run with 50 concurrent users and a spawn rate of 5 users per second.
82+
83+
### Summary Table
84+
85+
| Request | # Requests | # Failures | Median (ms) | Average (ms) | Min (ms) | Max (ms) | Avg Size (bytes) |
86+
|---------------|------------|------------|-------------|--------------|----------|----------|-------------------|
87+
| GET /students | 1257 | 0 | 13 | 17.87 | 5 | 159 | 40932.49 |
88+
| POST /students| 601 | 19 | 8 | 11.65 | 6 | 78 | 1934.76 |
89+
| **Aggregated**| 1858 | 19 | 11 | 15.86 | 5 | 159 | 28318.05 |
90+
91+
### Observations
92+
93+
- GET Requests:
94+
- No failures occurred.
95+
- Response times were fast (median: 13 ms, average: 17.87 ms), showing the API handles read-heavy operations well.
96+
97+
- POST Requests:
98+
- A small number of failures (19) occurred out of 601 requests (~3% failure rate).
99+
- Response times were slightly lower than GET requests (median: 8 ms, average: 11.65 ms), but failures indicate occasional issues with creating new entries under load.
100+
101+
- Overall Performance:
102+
- The API handled approximately 32 requests/sec (aggregated) under this simulated load.
103+
- Response times stayed under 200 ms for both GET and POST requests, indicating good responsiveness.
104+
- Failures on POST requests may require investigation into database constraints, concurrency handling, or server resource limits.
105+
106+
## Recommendations
107+
108+
- Investigate POST failures to identify whether they are caused by:
109+
- database constraints (unique indexes, FK constraints),
110+
- race conditions during concurrent inserts,
111+
- connection/timeouts to the database,
112+
- or insufficient server resources under burst load.
113+
114+
- Consider:
115+
- implementing database connection pooling,
116+
- adding retries for transient failures (with exponential backoff),
117+
- validating and sanitizing payloads before inserting,
118+
- introducing optimistic locking or deduplication where appropriate.
119+
120+
- Run additional tests:
121+
- with higher concurrency to observe scaling limits,
122+
- with realistic user behavior (think time, different endpoints),
123+
- varying payload sizes and database load.
124+
125+
- Use the Locust web interface to continuously monitor:
126+
- requests/sec (RPS),
127+
- failure rates,
128+
- response-time percentiles (median, 95th, 99th),
129+
- and request/response sizes.
130+
131+
## Notes
132+
133+
- Keep test environment as close to production as possible for meaningful results (same DB size/config, same instance types).
134+
- Capture and inspect server logs during the test to correlate errors and stack traces with failing requests.
135+
- If failures persist, run a focused test targeting POSTs to reproduce and capture detailed error responses from the API.

0 commit comments

Comments
 (0)