Skip to content

Commit ceed91a

Browse files
Update api-load-testing.md
1 parent ec8e141 commit ceed91a

1 file changed

Lines changed: 97 additions & 88 deletions

File tree

docs/app/api-load-testing.md

Lines changed: 97 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,144 @@
1-
# Load Testing - Student Management API
1+
# Flask REST API Load Testing
2+
3+
This repository contains load testing scripts for the Student Management Flask API, which manages student records using PostgreSQL.
24

35
## Overview
46

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.
7+
The API provides endpoints to manage students:
68

7-
The load test covers the following endpoints:
9+
Method Endpoint Description
10+
GET / Home page
11+
GET /health Health check
12+
GET /students Get all students
13+
POST /students Add a new student
14+
GET /students/<id> Get a single student
15+
PUT /students/<id> Update a student
16+
DELETE /students/<id> Delete a student
817

9-
- `GET /students` — Retrieve all students
10-
- `POST /students` — Create a new student
18+
## Load Testing Goals:
1119

12-
## Prerequisites
20+
- Test the performance and stability of the API under concurrent requests.
21+
- Measure request throughput (requests per second), response times, and failure rates.
22+
- Identify potential bottlenecks for GET and POST requests.
23+
- Verify that the API can handle both read and write-heavy traffic.
1324

14-
- Python 3.10+
15-
- Locust 2.x
16-
- The Flask API must be running locally or remotely
25+
## Setup
1726

18-
Install Locust if not already installed:
27+
Activate virtual environment:
1928

2029
```bash
21-
pip install locust
30+
source .venv/bin/activate
2231
```
2332

24-
## Running the Load Test
25-
26-
1. Start your Flask API:
33+
Install dependencies:
2734

2835
```bash
29-
export FLASK_APP=app:create_app
30-
export FLASK_ENV=development
31-
flask run --host=0.0.0.0 --port=5000
36+
pip install -r requirements.txt
37+
pip install locust
3238
```
3339

34-
2. Open a new terminal and navigate to the `tests/` directory:
40+
Ensure API is running:
3541

3642
```bash
37-
cd tests
43+
flask run --host=0.0.0.0 --port=5000
3844
```
3945

40-
3. Run Locust:
46+
Verify /students and /health endpoints are accessible.
47+
48+
## Load Testing with Locust
49+
50+
### Locust Test Script
51+
52+
The load test script is located at `tests/load_test.py`.
53+
54+
It simulates Student API users performing:
55+
56+
- GET /students
57+
- POST /students with random student data
58+
59+
(Optional: can add PUT, DELETE, GET /students/<id>)
60+
61+
Run Locust:
4162

4263
```bash
64+
cd tests
4365
locust -f load_test.py --host=http://localhost:5000 --web-host 0.0.0.0
4466
```
4567

46-
4. Access the web interface in your browser and specify number of users and spawn rate:
68+
Locust web UI will be available at `http://<server-ip>:8089`.
4769

48-
```
49-
http://<your-server-ip>:8089
50-
```
70+
Configure number of users and spawn rate from the UI.
5171

52-
Start the test from the web UI.
72+
### Endpoints Tested in Locust
5373

54-
## Load Test Script (tests/load_test.py)
74+
- `/` (Home) — lightweight endpoint
75+
- `/health` (Health check) — lightweight endpoint
76+
- `/students` (GET/POST) — core student API
77+
- `/students/<id>` (GET/PUT/DELETE) — single student operations
5578

56-
```python
57-
from locust import HttpUser, task, between
58-
import random
79+
## Observations from Load Testing
5980

60-
class StudentApiUser(HttpUser):
61-
wait_time = between(1, 2)
81+
### GET / and /health
6282

63-
@task(2)
64-
def get_students(self):
65-
self.client.get("/students")
83+
- All requests succeeded.
84+
- Median response: 150–200ms.
85+
- Low payload, very fast and reliable.
6686

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-
```
87+
### GET /students
7888

79-
## Observations from Load Testing
89+
- Handles ~13 requests/sec for 1,300+ records.
90+
- Median response ≈ 291ms.
91+
- 95th percentile ≈ 780ms (due to large payloads).
92+
93+
### POST /students
8094

81-
The load test was run with 50 concurrent users and a spawn rate of 5 users per second.
95+
- Some failures observed due to duplicate emails (email is unique in DB).
96+
- Handles ~5–6 successful requests/sec with random payload.
97+
- Median response ≈ 325ms; maximum ≈ 1.6s.
98+
- Recommendation: generate unique test data for high-volume writes.
8299

83-
### Summary Table
100+
### Single student operations
84101

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 |
102+
- GET/PUT/DELETE /students/<id> are extremely fast (avg ~20ms).
103+
- Some GET failures occur if the student ID was deleted during the test.
90104

91-
### Observations
105+
### Overall
92106

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.
107+
- Aggregated throughput: ~32–35 requests/sec across endpoints.
108+
- Median response for all endpoints ≈ 48ms.
109+
- POST operations require better handling for unique constraints.
110+
- API is stable and performant for read-heavy workloads.
96111

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.
112+
## Load Testing Best Practices
100113

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.
114+
- Use unique test data for POST requests to avoid conflicts.
115+
- Seed the database with realistic test data (`seed.py`) before running tests.
116+
- Monitor database performance under concurrent writes.
117+
- Incrementally increase users in Locust to observe scaling limits.
118+
- Measure 95th percentile response times for production-level performance.
105119

106-
## Recommendations
120+
## Generating Test Data
107121

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.
122+
Seed the database with dummy students:
113123

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.
124+
```bash
125+
python seed.py
126+
```
127+
128+
This will insert 100 dummy students with unique emails.
129+
130+
## Running Advanced Tests
119131

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.
132+
To load test all endpoints including `/students/<id>` operations:
124133

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.
134+
Modify `load_test.py` to:
130135

131-
## Notes
136+
- Maintain a list of existing student IDs.
137+
- Perform GET/PUT/DELETE operations on valid IDs.
138+
- Avoid None or deleted IDs to reduce failures.
132139

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.
140+
Run Locust as usual:
141+
142+
```bash
143+
locust -f tests/load_test.py --host=http://localhost:5000 --web-host 0.0.0.0
144+
```

0 commit comments

Comments
 (0)