Chapter 16Lesson 01~150 minutes

JDBC Database Load Testing and Connection-Pool Considerations: Core Concepts and Mental Model

Chapter 15 treated identity/session state as a first-class workload dependency. Direct JDBC testing adds another specialized boundary: the load generator opens database connections itself, bypassing the application's HTTP layer, request validation, ORM, caches, service code, and application connection pool. That makes JDBC tests excellent for narrowly scoped database experiments—but dangerous to mislabel as end-to-end application capacity.

JDBCConnection poolPrepared SELECTDB telemetryValidity

Learning objectives

  • Explain the direct path from JMeter JDBC pool to database engine and why it differs from application traffic.
  • Distinguish JMeter's client-side JDBC pool from an application's datasource/ORM pool.
  • Understand JMeter JDBC connection-acquisition latency versus total sampler elapsed time.
  • Define driver, credential, query, row/result, telemetry, and validity state before changing the plan.
  • Inspect database rows/sessions/permissions non-destructively before load.
  • Recognize direct DB load as an explicitly authorized specialized experiment.

1. The practical problem: “database is slow” is not a workload definition

An API response can be slow because of application code, remote calls, connection-pool wait, SQL execution, lock waits, serialization, or generator/network effects. A direct JDBC test removes most application layers and asks a narrower question: how does this database/query/connection path behave when JMeter itself opens JDBC connections?

Mandatory safety boundary: all executable examples use a disposable H2 database bound to loopback port 9092, synthetic rows, a JMETER_READ account with SELECT permission only, maximum 2 JMeter threads, and finite short runs. Never substitute a production/shared database.

2. Mental model: JMeter pool → JDBC sampler → DB engine → evidence

Direct JDBC load path

The JMeter JDBC pool belongs to the load generator. It is not the application server pool, and direct JDBC latency is not end-to-end application latency.

flowchart TD
W[Threads / inputs / pacing] --> CC[JDBC Connection Configuration]
D[H2 driver in JMETER_HOME/lib] --> CC
CC --> P[JMeter DBCP connection pool]
P --> JR[JDBC Request]
JR -->|Prepared SQL + parameters| DB[H2 TCP database 127.0.0.1:9092]
DB --> RS[Rows / SQL errors]
RS --> A[Response/variable assertions]
JR --> J[JTL + jmeter.log]
DB --> S[INFORMATION_SCHEMA.SESSIONS]
DB --> Q[QUERY_STATISTICS]
G[Generator CPU / GC / sockets] --> V[Validity review]
J --> V
S --> V
Q --> V

Threads and data determine how many query opportunities JMeter creates. JDBC Connection Configuration resolves the driver/URL/user/pool policy and builds JMeter's client-side DBCP pool. JDBC Request borrows a connection, executes SQL, returns rows/errors, and records a sample. H2 independently exposes active sessions and query statistics. Generator CPU/GC/sockets are separate evidence. Nothing in this path measures an application's own web tier or its connection pool.

3. JMeter JDBC pool is not the application pool

JMeter creates its own JDBC connections. If the production application uses HikariCP, an application-server datasource, or an ORM-managed pool, those connections are not present in a direct JDBC JMeter plan.

Therefore “JMeter pool max=10” does not mean the application pool is 10, and “DB handles 10 JMeter connections” does not prove the application can handle 10 concurrent requests.

4. The surprising meaning of Max Number of Connections = 0

Current JMeter JDBC Connection Configuration documentation recommends 0 in most cases. Here, zero does not mean “no connections” or “unlimited shared pool.” It means each JMeter thread gets its own pool containing one connection, so connections are not shared between threads.

With 2 threads and baseline max=0, predict roughly two JMeter database sessions while both threads are active.

5. JDBC sampler latency has pool meaning

For JDBC Request, JMeter sets the sample's latency to the time taken to acquire a connection. Total elapsed includes connection acquisition plus query execution/result processing.

This makes a constrained shared pool diagnostically useful: rising latency with otherwise stable DB query time suggests client-side connection wait rather than database execution slowdown.

6. Driver state belongs to the JMeter JVM

JMeter does not bundle every vendor JDBC driver. The H2 JAR must be available on JMeter's runtime classpath—normally by copying h2-2.5.250.jar into JMETER_HOME/lib and restarting JMeter. The connection uses org.h2.Driver.

A “No suitable driver” or class-not-found error is generator/classpath state, not database saturation.

7. Credentials and least privilege

JDBC Connection Configuration can store a username/password in the test plan; its password field is not encrypted in JMX. The chapter therefore uses only fake lab credentials in the generated examples. In a real authorized environment, inject credentials through an approved secret mechanism and keep them out of source control/result artifacts.

The JMeter account receives SELECT on one synthetic table only. It cannot create/drop/update/delete that table.

8. Prepared parameters versus SQL string construction

Use Prepared Select Statement with ? placeholders and parameter values/types instead of concatenating arbitrary data into SQL. Prepared binding preserves type semantics and avoids SQL-injection mistakes caused by untrusted/string-interpolated inputs.

9. State to define before changing JDBC load

State Question
Generator JMeter/Java version; H2 JAR present; CPU/GC; result-save/listener cost?
Thread/workload Threads, loops, timers, query mix, configured versus achieved queries/s?
Pool Variable name, max connections, max wait, preinit, autocommit, isolation, validation query?
Variables/data Prepared parameter values/types; result variable names; row counts?
Protocol/driver JDBC URL, driver class/version, TCP/embedded mode, timeouts?
Credentials SELECT-only user in JMeter; admin observer separate and local only?
Target H2 version, rows/indexes, active sessions, query statistics, DB CPU/process state?
Results JTL elapsed/latency/error code/message plus matching jmeter.log?
Validity Direct DB test clearly separated from API/application-pool capacity?

10. Non-destructive inspection first

Before JMeter load, use the local H2 Shell/admin account to check:

SELECT COUNT(*) AS ROWS FROM PUBLIC.CATALOG_ITEM;
SELECT MIN(ID), MAX(ID), COUNT(DISTINCT CATEGORY) FROM PUBLIC.CATALOG_ITEM;
SELECT GRANTEE, RIGHTS, TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.RIGHTS
WHERE GRANTEE='JMETER_READ';
SELECT USER_NAME, SESSION_STATE, COUNT(*) AS SESSIONS
FROM INFORMATION_SCHEMA.SESSIONS
GROUP BY USER_NAME, SESSION_STATE;

Then connect as JMETER_READ and prove a SELECT succeeds. Do not “prove” read-only access by executing a destructive statement against any non-disposable database.

11. Database telemetry and visibility

H2 INFORMATION_SCHEMA.SESSIONS shows session IDs, users, executing statement, state, and blocker information. Only ADMIN users can see all sessions; ordinary users see only their own. This is why telemetry inspection uses a separate local admin shell, while JMeter stays least-privilege.

With SET QUERY_STATISTICS TRUE, INFORMATION_SCHEMA.QUERY_STATISTICS records execution counts and min/max/cumulative/average query time. This is target evidence, not a substitute for JTL.

12. Configured versus achieved database load

Configured load is threads/loops/timers/query opportunities. Achieved load is how many valid JDBC samples actually execute per unit time. A small JMeter pool, slow connection acquisition, query errors, generator CPU pressure, or DB saturation can reduce achieved load even when thread count is unchanged.

13. DevOps connection

Direct database load testing belongs in a production performance program as a controlled diagnostic/capacity experiment with explicit authorization, schema/data ownership, pool settings, driver version, SQL set, telemetry, and cleanup. It is not a shortcut around application-level testing.

Knowledge check

What does JMeter JDBC Max Connections = 0 mean?

What does JDBC sampler latency represent?

Why doesn't a direct JDBC result prove application capacity?

Why use a Prepared Select Statement?

Why does the admin monitor account stay outside the JMeter load plan?

Next lesson

Build the local H2/JDBC experiment

Lesson 2 initializes a synthetic H2 database, installs the driver, starts a localhost-only TCP server, configures JMeter's pool, runs prepared SELECTs, asserts rows, and observes session/query evidence under tiny load.

Official references and version notes

Version and compatibility note

Version-sensitive behavior was rechecked against current primary documentation on 2026-09-05. The course baseline is Apache JMeter 5.6.3 with a Java 17 JDK; JMeter 5.6.3 requires Java 8+. The mandatory database is H2 2.5.250 (released 2026-08-29), whose current engine requirements specify Java 11+ and which is tested with Java 11 and 17. The JDBC driver class is org.h2.Driver. Copy the H2 driver JAR into JMETER_HOME/lib and restart JMeter before authoring/running the JDBC plan. The H2 JAR is a JDBC driver dependency, not a JMeter plugin. JMeter's JDBC Connection Configuration uses Apache DBCP internally. Its documented recommended Max Number of Connections = 0 means each JMeter thread gets its own one-connection pool; a positive value creates a shared pool. JDBC Request latency is set to connection-acquisition time, so pool wait can be separated from total sample elapsed time. H2 server mode is bound to loopback only in this chapter; no -tcpAllowOthers is used.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.