|
| 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