KAHIBARO
Discord Login Register

26.12. Load Testing

Why Load Testing Matters

Load testing checks how your backend behaves when many users use it at the same time. You do not test if a single request works, you test if hundreds or thousands of requests per second still work correctly and fast enough.

Typical goals:

Key idea: Load testing focuses on performance and capacity under expected real-world traffic, not just on correctness of features.

You usually run load tests before a big launch, marketing campaign, or feature release, and regularly in CI/CD for critical systems.


Basic Load Testing Concepts

Users, Requests, and Throughput

When talking about load testing, you will see these terms again and again:

TermMeaningExample
Virtual userA simulated user performing actions against your API500 virtual users calling your API
RequestOne HTTP call to an endpointGET /products?page=1
ThroughputNumber of requests processed per time unit1000 requests per second (RPS)
ConcurrencyHow many requests or users are active at the same time300 concurrent users
LatencyTime from sending a request until the response is received250 ms
Response timeOften used as a synonym for latency250 ms
DurationHow long you run the load test10 minutes

We often care not just about the average response time but also the percentiles.

Example:

Important: A system can have a good average but terrible tail latencies. Always look at p95 and p99, not only the mean.


Types of Performance Tests

Load testing is part of a broader family of performance tests:

TypePurpose
Load testingTest behavior under expected normal or slightly higher than normal load
Stress testingPush the system beyond its limits until it fails
Soak testingRun with medium, realistic load for a long time to detect memory leaks or resource issues
Spike testingApply sudden, very large load to see if the system survives sudden traffic spikes

Focus here is load testing, but it helps to know the terms, because tools support multiple modes.


What to Test in a Backend

You can target many parts of your backend:

You usually do not test all endpoints. Instead, you pick:

  1. The endpoints with the highest traffic.
  2. The endpoints that use the most CPU or database time.
  3. Critical flows: login, add to cart, checkout, payment.

Example test focus for an e-commerce backend:


FlowExample endpoints
Browsing productsGET /products, GET /products/{id}
Cart and checkoutPOST /cart/items, POST /checkout
User accountPOST /auth/login, GET /me
Admin operationsPOST /admin/products, GET /admin/orders?status=pending

Defining Performance Goals

Before you run any tool, you must define what “good performance” means for your application.

Service Level Objectives (SLOs)

You can describe performance targets as SLOs. For example:

In math form:

This means: at least 99% of requests complete in 0.8 seconds or less.

Rule: Never run load tests without clearly defined targets. You need SLOs to know if the result is acceptable or not.

Determining Expected Load

You need an estimate of:

Example:

You may set your load test target at 1.5x or 2x this value to have a safety margin. For example, test at 600 RPS.


Building Realistic Load Scenarios

Realistic tests try to imitate real user behavior, not just call one endpoint in a tight loop.

User Journeys

A user journey is a sequence of actions that a typical user performs.

Example: “Anonymous browsing” journey

  1. GET /products?page=1
  2. GET /products/123
  3. GET /products/456
  4. GET /categories/electronics

Example: “Authenticated shopping” journey

  1. POST /auth/login
  2. GET /products?category=phones
  3. POST /cart/items
  4. POST /checkout

A load testing tool can simulate thousands of users following these journeys.

Think Time and Pauses

Real users do not click instantly. They read pages, think, move the mouse. If you send requests without delay, the load will be unrealistically high.

You introduce think time between actions, for example:

Example in pseudo-code:

python
def user_journey(client):
    client.get("/products?page=1")
    sleep(random_between(2, 5))
    client.get("/products/123")
    sleep(random_between(1, 3))
    client.post("/cart/items", json={"product_id": 123, "quantity": 1})
    sleep(random_between(1, 2))
    client.post("/checkout")

This creates more realistic traffic patterns.

Mix of Endpoints

In production, some endpoints are called more than others. For example:

Many tools allow you to define weights or define more users for some flows than others.

Example mix:

FlowShare of usersRough share of requests
Browsing60%~60%
Browsing + cart30%~30%
Browsing + cart + checkout10%~10%

This helps you see real bottlenecks, not only synthetic ones.


Popular Load Testing Tools

You do not have to write your own load simulation. There are many tools.

Some common ones:

ToolLanguage / InterfaceStyleGood for
abCLIVery simpleQuick single-endpoint tests
wrkCLI + LuaHigh performanceHeavy load, scripting
JMeterGUI / XMLEnterpriseComplex test plans, older setups
LocustPythonCode-basedPython backends, user-journey modeling
k6JavaScript + CLICode-basedCI/CD integration, modern workflows

For a Python-based backend, Locust is very popular, since you can describe tests as Python code.


Example: Simple CLI Load Test with `ab`

ab (ApacheBench) is a very small tool often available on Linux.

Example command:

bash
ab -n 1000 -c 50 https://api.example.com/products

Meaning:

ab prints:

Limitations:

Example: Load Test with Locust (Python)

Locust lets you define user classes and tasks in Python.

Basic Locust Script

Install Locust:

bash
pip install locust

Create locustfile.py:

python
from locust import HttpUser, task, between
class BrowsingUser(HttpUser):
    wait_time = between(1, 3)  # think time between tasks
    @task(3)  # higher weight: more frequent
    def list_products(self):
        self.client.get("/products?page=1")
    @task(1)
    def view_product(self):
        self.client.get("/products/123")

Run Locust:

bash
locust -f locustfile.py --host=https://api.example.com

Then open the web UI (default at http://localhost:8089), and:

Adding Authenticated Flows

Example with login and authenticated requests:

python
from locust import HttpUser, task, between
class AuthenticatedUser(HttpUser):
    wait_time = between(1, 3)
    def on_start(self):
        # called when a simulated user starts
        response = self.client.post(
            "/auth/login",
            json={"email": "test@example.com", "password": "secret"},
        )
        token = response.json()["access_token"]
        self.headers = {"Authorization": f"Bearer {token}"}
    @task
    def get_profile(self):
        self.client.get("/me", headers=self.headers)
    @task
    def list_orders(self):
        self.client.get("/orders", headers=self.headers)

This lets you test authorized endpoints under load.


How to Run Load Tests Safely

Never Attack Production by Surprise

Load tests can overload your system and affect real users. You must be careful.

Recommended:

Rule: Do not run large, uncontrolled load tests against production without approval and monitoring. You can cause an outage.

Control Test Parameters

Typical parameters you choose:

Example plan:

  1. Start with 100 users, run 5 minutes.
  2. Then 300 users, run 10 minutes.
  3. Then 600 users, run 10 minutes.

Observe behavior at each step.


Monitoring During Load Tests

Load test metrics are not enough. You also need backend-side metrics.

You should monitor:

Metric typeExamples
CPUCPU usage per instance
MemoryMemory usage, swap usage
DiskDisk I/O, disk latency, free space
NetworkIncoming and outgoing traffic, errors
DatabaseQuery time, CPU, locks, active connections
Application metricsRequests per second, errors, latency percentiles

During the test, watch for:

Example: If CPU reaches 100% and latency increases sharply at around 400 RPS, that is probably your current capacity limit with this configuration.


Interpreting Results

After the test you have two sides of data:

  1. Metrics from the load tool (latency, throughput, errors).
  2. Metrics from infrastructure and application monitoring.

You look for:

As throughput (RPS) increases, response time usually rises.

Example interpretation:

Conclusion:

Common Bottlenecks Revealed by Load Tests

Load tests often reveal:

  1. Slow database queries
    • Missing indexes.
    • SELECT * over large tables.
    • Expensive joins on big datasets.
  2. Insufficient connection pooling
    • Too few connections to the database.
    • Too many connections causing database contention.
  3. Lock contention
    • Many requests trying to update the same rows.
  4. Inefficient code
    • N+1 database queries (multiple queries inside loops).
    • Heavy JSON serialization, expensive computations.
  5. Missing caching
    • Recomputing expensive results for every request.
  6. External API dependencies
    • Remote service or payment provider responds slowly.

Load testing helps you see these problems in a realistic scenario.


Iterative Tuning Process

Load testing is not a one-time activity. You normally follow a loop:

  1. Baseline test
    • Run a test with current version.
    • Record metrics, capacity, latency.
  2. Identify bottlenecks
    • Use profiling, logs, database query analysis.
  3. Optimize

Examples:

  1. Retest
    • Run the same test scenario again.
    • Compare before and after.
  2. Repeat

You always keep a history of test results to track improvements.

Example:

Load Testing in CI/CD

For critical APIs, you can add automated load tests in your CI/CD pipeline. This is not always full-scale, but at least ensures you did not introduce huge regressions.

Possible approach:

For bigger, more expensive tests:

Practical Tips and Gotchas

  1. Warm-up phase
    • Many systems are slower at the very beginning due to:
      • Cold caches.
      • JIT compilation.
      • Lazy connections.
    • Either ignore the first minutes or run a warm-up step before measurements.
  2. Use realistic data
    • Test database size should be similar to production.
    • Using a tiny dataset can hide performance problems that appear with large data.
  3. Reset state between big tests
    • Clean database or reset it to a known state.
    • Ensure there is no leftover test data that pollutes results.
  4. Respect external services
    • Do not accidentally DDOS third-party APIs (payment providers, email services).
    • Use mock services or sandbox environments.
  5. Document each test

Write down:

This makes it easier to compare and track performance over time.


Summary

Load testing verifies that your backend can handle realistic traffic while meeting defined performance goals. You:

With regular load testing, you reduce the risk of slowdowns or outages when real users hit your application in large numbers.

Views: 8

Comments

Please login to add a comment.

Don't have an account? Register now!