Lewati ke konten
Rama's logo Qisthi Ramadhani
Go back

PestPHP Stress Testing for Health Endpoint Benchmarking: How to Fix

TL;DR

Benchmarking health check endpoints is essential for validating application performance baselines, load balancer configuration, and Kubernetes readiness/liveness probe reliability. PestPHP’s built-in stress testing feature provides a developer-friendly way to run concurrent HTTP load tests without external tools like Apache Bench or k6. This guide walks you through identifying the symptoms, understanding the root cause, and implementing a production-tested fix with real code examples.


Symptoms

If you’re experiencing this issue, you’ll likely notice one or more of these signs in your application:

If any of these symptoms look familiar, you’re dealing with pestphp stress testing for health endpoint benchmarking. Read on to understand why it happens and how to fix it properly.


Root Cause

Benchmarking health check endpoints is essential for validating application performance baselines, load balancer configuration, and Kubernetes readiness/liveness probe reliability. PestPHP’s built-in stress testing feature provides a developer-friendly way to run concurrent HTTP load tests without external tools like Apache Bench or k6. Understanding how to write stress tests, interpret results (TTFB, throughput, success rate), and set meaningful thresholds ensures your health endpoints are production-ready. Without benchmarks, you deploy blind — unable to predict how your application will behave under real user load, unable to set accurate auto-scaling thresholds, and unable to detect performance regressions introduced by code changes or dependency updates.

Understanding the root cause is critical before applying a fix — treating only the symptoms often leads to the problem resurfacing in a different form, especially under production load.


How to Fix

Follow these steps in order. Each step builds on the previous one, and skipping steps may result in an incomplete fix.

Step 1: Install PestPHP if not already present: composer require pestphp/pest —dev

Install PestPHP if not already present: composer require pestphp/pest —dev — the stress testing feature is included in the core PestPHP package starting from v2.0

Step 2: Create a stress test file in tests/Stress/ directory

Create a stress test file in tests/Stress/ directory — organize stress tests separately from unit and feature tests so they can be run independently in CI pipelines

Step 3: Define endpoint targets and concurrency levels matching your expected production load

Define endpoint targets and concurrency levels matching your expected production load — start with your health check endpoint at 10 concurrent connections, then scale up to match your expected peak traffic

Step 4: Run the stress test: ./vendor/bin/pest stress http://localhost:8000/up —concurrency=10 —duration=10 and capture the baseline results before making any changes

Run the stress test: ./vendor/bin/pest stress http://localhost:8000/up —concurrency=10 —duration=10 and capture the baseline results before making any changes

Step 5: Analyze the results carefully: TTFB P95 should be under 100ms for health endpoints, success rate must be 100%, and throughput should match your expected requests per second

Analyze the results carefully: TTFB P95 should be under 100ms for health endpoints, success rate must be 100%, and throughput should match your expected requests per second

Step 6: Set up CI pipeline stress tests to catch performance regressions before deploy

Set up CI pipeline stress tests to catch performance regressions before deploy — add the stress test command to your GitHub Actions or GitLab CI workflow with threshold assertions

Step 7: Compare results across different frameworks, configurations, and infrastructure setups to make informed architecture decisions backed by real data rather than assumptions

Compare results across different frameworks, configurations, and infrastructure setups to make informed architecture decisions backed by real data rather than assumptions


Code Example

The following before/after comparison shows the core pattern you need to change. Pay attention to the comments explaining why each change matters.

Before (Broken)

// No benchmark — deploying without performance data
// routes/web.php
Route::get('/up', function () {
    return response('OK', 200);
});

After (Fixed)

// Stress test with PestPHP
// tests/Stress/HealthEndpointTest.php
use function Pest\Stressless\stress;

it('handles health check endpoint under load', function () {
    $result = stress('http://localhost:8000/up')
        ->concurrently(10)
        ->for(10)->seconds();

    expect($result->requests()->successRate())->toBe(100.0);
    expect($result->requests()->duration()->med())->toBeLessThan(50); // median < 50ms
    expect($result->requests()->duration()->p95())->toBeLessThan(100); // P95 < 100ms
});

it('handles database health check under load', function () {
    $result = stress('http://localhost:8000/up/database')
        ->concurrently(5)
        ->for(10)->seconds();

    expect($result->requests()->successRate())->toBe(100.0);
    expect($result->requests()->duration()->p95())->toBeLessThan(200);
});

Key Takeaways


Difficulty Level

Intermediate

This guide assumes familiarity with the framework and its core tooling. You should understand basic database concepts, configuration patterns, and be comfortable reading framework source code when needed. Prior experience with similar issues will help but is not required.


Frequently Asked Questions

How does PestPHP stress testing compare to Apache Bench or k6?

PestPHP stress tests integrate directly into your test suite and use PHP’s familiar assertion syntax. They’re ideal for CI/CD regression checks and developer workflows. For production load testing at scale (10,000+ concurrent users), use dedicated tools like k6, Locust, or Gatling. PestPHP is best for baseline benchmarks and automated regression detection.

What is a good TTFB threshold for health endpoints?

For health check endpoints: P50 under 10ms, P95 under 50ms, P99 under 100ms. If your health endpoint queries the database, add 20-50ms to each threshold. Load balancers typically have health check timeouts of 5-30 seconds, so anything under 1 second is functional — but faster is better for accurate load distribution.


Share this post on:
LLM-friendly version:
Open in ChatGPT Open in Claude

Related Posts


Previous Post
Laravel Race Condition in Cache and Database Locks: How to Fix
Next Post
PHP 8.4 Property Hooks Migration for Laravel: How to Fix