Skip to main content

Component Performance Testing

What is Component Performance Testing?

Component performance testing measures the behaviour and resource consumption of a single service or component under controlled load conditions — in isolation from the full system. It identifies performance bottlenecks, resource limits, and regression in latency or throughput before a release.

Unlike full system load tests, component tests are faster to run and easier to diagnose because there is no noise from other services.


Goals

  • Establish a performance baseline for each service
  • Detect performance regressions introduced by code changes
  • Validate that the service meets NFR targets (latency, throughput, resource consumption)
  • Identify the breaking point (maximum RPS before errors occur)
  • Find memory leaks, thread pool exhaustion, and connection pool saturation

Performance Test Types

TypeWhat It Measures
Baseline / BenchmarkEstablishes normal performance under typical load
Load TestValidates performance at expected production load
Stress TestFinds the service's breaking point (keeps increasing load)
Spike TestValidates behaviour under sudden traffic spikes
Soak / Endurance TestDetects memory leaks and degradation over extended time (hours)
Capacity TestDetermines the maximum throughput before SLA breach

NFR Targets (Example)

Define measurable targets before writing tests:

MetricTargetBreach Threshold
p50 latency< 50ms> 100ms
p95 latency< 200ms> 400ms
p99 latency< 500ms> 1000ms
Throughput≥ 1000 RPS sustained< 800 RPS
Error rate< 0.1%> 0.5%
JVM heap (steady state)< 512MB> 800MB
CPU (steady state)< 60%> 85%

Tools

ToolLanguageBest For
GatlingScala DSLJava/Scala teams, CI integration, detailed reports
k6JavaScriptDeveloper-friendly, easy scripting
Apache JMeterGUI + XMLGUI-based test design, enterprise use
Wrk / Wrk2CLIQuick latency benchmarks
LocustPythonDistributed load testing

Gatling Performance Test (Java/Spring Ecosystem)

Gatling is the most idiomatic choice for Spring Boot teams — it integrates into Maven cleanly.

Maven Setup

<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>4.9.0</version>
<configuration>
<simulationClass>
com.yourorg.perf.TransactionServiceSimulation
</simulationClass>
</configuration>
</plugin>

Gatling Simulation

package com.yourorg.perf

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TransactionServiceSimulation extends Simulation {

val baseUrl = System.getProperty("perf.baseUrl", "http://localhost:8080")
val token = System.getProperty("perf.token", "test-jwt-token")

val httpProtocol = http
.baseUrl(baseUrl)
.header("Authorization", s"Bearer $token")
.header("Content-Type", "application/json")
.acceptHeader("application/json")

// Scenario: List transactions
val listTransactions = scenario("List Transactions")
.exec(
http("GET /api/v1/transactions")
.get("/api/v1/transactions")
.queryParam("fromDate", "2024-01-01")
.queryParam("toDate", "2024-01-31")
.check(status.is(200))
.check(responseTimeInMillis.lte(500))
.check(jsonPath("$.content").exists)
)

// Scenario: Create transaction
val createTransaction = scenario("Create Transaction")
.exec(
http("POST /api/v1/transactions")
.post("/api/v1/transactions")
.body(StringBody(
"""{"amount": 99.99, "currency": "USD", "description": "perf-test"}"""
))
.check(status.is(201))
.check(responseTimeInMillis.lte(300))
)

// Load profile: ramp to 1000 RPS over 2 minutes, hold for 5 minutes
setUp(
listTransactions.inject(
rampUsersPerSec(10).to(800).during(2.minutes),
constantUsersPerSec(800).during(5.minutes)
),
createTransaction.inject(
rampUsersPerSec(5).to(200).during(2.minutes),
constantUsersPerSec(200).during(5.minutes)
)
).protocols(httpProtocol)
.assertions(
global.responseTime.percentile(99).lte(500), // p99 < 500ms
global.responseTime.percentile(95).lte(200), // p95 < 200ms
global.successfulRequests.percent.gte(99.9) // Error rate < 0.1%
)
}

Analysing Results

Key JVM Metrics to Monitor During Tests

Use Spring Boot Actuator + Prometheus + Grafana:

// Add to application.yml for performance profiling
management:
metrics:
distribution:
percentiles-histogram:
http.server.requests: true
percentiles:
http.server.requests: 0.5, 0.75, 0.95, 0.99
endpoints:
web:
exposure:
include: health,metrics,prometheus

HikariCP Metrics (Watch During Tests)

hikaricp.connections.active      → should not stay at max
hikaricp.connections.pending → should be 0 or near 0
hikaricp.connections.timeout → should always be 0
hikaricp.connections.acquire → p99 should be < 10ms

JVM Heap Analysis

jvm.memory.used{area="heap"}     → watch for linear growth (memory leak)
jvm.gc.pause → frequent long GCs indicate heap pressure
jvm.threads.live → should be stable, not growing

Performance Regression Gate in CI

Block a release if performance regresses beyond thresholds:

# .github/workflows/perf.yml
- name: Run performance tests
run: |
mvn gatling:test \
-Dperf.baseUrl=${{ env.SIT_URL }} \
-Dperf.token=${{ secrets.PERF_TOKEN }}

- name: Fail on performance regression
if: failure()
run: |
echo "❌ Performance regression detected — release blocked"
exit 1

Exit Criteria

  • Baseline established and documented
  • All NFR targets met (p95, p99, throughput, error rate)
  • No memory leak detected in 30-minute soak test
  • JVM heap, CPU, and thread counts are stable under load
  • Results attached to Test Summary Report

Tip: Warm Up Before Measuring

Always include a warm-up ramp in Gatling simulations. JVM JIT compilation means the first few minutes of a test will show higher latency. Discard warm-up results from your assertions.