Chapter 16Lesson 05~240 minutes

Checkpoint Lab — JDBC Database Load Testing and Connection-Pool Considerations

The checkpoint is deliberately small. Its purpose is to prove that you can predict connection demand, detect JMeter-side pool contention without blaming the database, restore a valid configuration, and preserve a read-only least-privilege evidence packet.

CheckpointPool starvationSELECT-onlyDB telemetryRestore

Learning objectives

  • Run two SELECT-only JMeter threads against the disposable H2 database.
  • Predict baseline physical connection demand from pool semantics.
  • Intentionally constrain a shared pool to one connection and observe acquisition wait/starvation.
  • Separate JMeter acquisition latency from H2 query execution time.
  • Restore the baseline per-thread pool and verify correctness/headroom.
  • Produce version/pool/query/session/result/cleanup evidence suitable for review.

1. Exact assumptions and ceilings

Item Checkpoint baseline
JMeter Apache JMeter 5.6.3.
Java Java 17 JDK; JMeter 5.6.3 requires Java 8+.
Database H2 2.5.250 TCP server, Java 11+ required.
Driver h2-2.5.250.jar in JMeter lib; org.h2.Driver.
Target jdbc:h2:tcp://127.0.0.1:9092/jmeterlab;IFEXISTS=TRUE.
Load account JMETER_READ, SELECT only; fake password.
Threads 2 maximum.
Baseline Max Connections=0, Max Wait=1000ms.
Broken profile Shared Max Connections=1, Max Wait=5ms.
SQL Prepared item lookup + read-only Pool Hold Probe only.
Duration Each profile ≤10 seconds.
Writes None during JMeter load.
Abort: non-loopback JDBC URL/server bind, any UPDATE/INSERT/DELETE/DDL in the JMeter plan, account stronger than SELECT-only, >2 threads, >3 JMETER_READ sessions, unexpected sustained CPU saturation, or evidence of credential leakage.

2. Setup and target authorization/preflight

Recreate the disposable database with Lesson 2's setup.sql, start H2 using -Dh2.bindAddress=127.0.0.1, then record:

  • JMeter 5.6.3 / Java version;
  • H2 2.5.250 / driver hash or filename;
  • 5,000 synthetic rows;
  • JMETER_READ rights = SELECT on CATALOG_ITEM;
  • initial JMETER_READ session count = 0;
  • H2/JMeter process CPU/memory baseline.

3. Exact baseline tree

Test Plan
├── JDBC Connection Configuration — labdb
│   Max Connections=0
│   Max Wait=1000 ms
│   URL=jdbc:h2:tcp://127.0.0.1:9092/jmeterlab;IFEXISTS=TRUE
│   Driver=org.h2.Driver
│   User=JMETER_READ
├── CSV Data Set Config — item-ids.csv
└── Thread Group — 2 threads × 10 loops
    └── Prepared Item Lookup
        Query Type=Prepared Select Statement
        SQL=SELECT ID,CATEGORY,PRICE_CENTS,DESCRIPTION
            FROM PUBLIC.CATALOG_ITEM WHERE ID=?
        Value=${item_id}
        Type=INTEGER
        Variables=DB_ID,DB_CATEGORY,DB_PRICE,DB_DESCRIPTION
        ├── assert DB_ID_# == 1
        └── assert DB_DESCRIPTION_1 contains synthetic-item-

4. Predictions before baseline

Prediction A — connections: with Max Connections=0 and two active threads, each thread owns a one-connection pool, so the DB should observe up to roughly two JMETER_READ sessions while queries are active.

Prediction B — correctness: 20 prepared lookup opportunities (2×10) should return exactly one synthetic row each and produce zero assertion failures.

Prediction C — JTL timing: first samples can have larger connection-acquisition latency because Preinit Pool=false; later samples should reuse each thread's connection.

5. Execute baseline and monitor

jmeter.bat -n `
  -t plans\checkpoint-jdbc.jmx `
  -l results\baseline\results.jtl `
  -j results\baseline\jmeter.log `
  -e -o results\baseline\report

In a second terminal, sample INFORMATION_SCHEMA.SESSIONS for USER_NAME='JMETER_READ'. After the run, save QUERY_STATISTICS and generator/H2 process utilization.

6. Verify baseline independently

  • JTL: zero failures; 20 Prepared Item Lookup samples plus any explicitly enabled reporting samples.
  • Assertions: every DB_ID_# is 1.
  • H2 sessions: no more than expected small connection demand.
  • Query statistics: lookup SQL execution count consistent with run.
  • Generator/H2 process: neither saturated.

7. Add a controlled Pool Hold Probe

Create a second checkpoint plan/profile containing one read-only heavier query per thread:

SELECT COUNT(*) AS MATCHES
FROM SYSTEM_RANGE(1, 3000000)
WHERE MOD(X, 7) = 0

This query is not a production benchmark. It exists only to hold a JDBC connection long enough to make client-pool contention visible in a tiny local experiment.

8. Intentionally constrain JMeter's shared pool

Change only:

Setting Baseline Broken profile
Threads 2 2
Max Connections 0 (per-thread pools) 1 shared connection
Max Wait 1000 ms 5 ms
Target/query local read-only same local read-only Pool Hold Probe

Keep run ≤10 seconds. Do not increase threads or DB privileges.

9. Predictions before broken run

Prediction D — acquisition: only one JMeter thread can hold the shared connection at a time. The other thread will wait; JDBC latency should increase, and on many machines Max Wait=5 ms may cause a pool-borrow timeout.

Prediction E — DB sessions: the database should see approximately one JMETER_READ physical session from that shared JMeter pool, even though two JMeter threads are configured.

Prediction F — target execution: H2 query execution time may remain similar to the valid profile; an acquisition timeout can occur before the second query even reaches the DB.

10. Run the constrained profile and preserve it

jmeter.bat -n `
  -t plans\checkpoint-jdbc-pool1.jmx `
  -l results\pool1\results.jtl `
  -j results\pool1\jmeter.log

Do not overwrite the baseline. Preserve any pool timeout/error text, JTL latency/elapsed, H2 session count/query stats, and generator state.

11. Diagnose from connection acquisition versus DB execution

Evidence Interpretation
High JTL latency; H2 query time stable JMeter pool wait dominates.
Pool timeout; no matching second DB execution Connection was not acquired before Max Wait.
One JMETER_READ session with two threads Shared pool physical-connection cap is observable.
H2 execution/CPU rises for both profiles The deliberately heavy diagnostic query itself is adding target cost; don't call it a pool-only capacity result.

The checkpoint is green when you can explain the symptom correctly—not when the broken run has zero errors.

12. Restore the valid configuration

Restore Max Connections=0, Max Wait=1000, and the original Prepared Item Lookup. Rerun the 2×10 baseline as results/restored/....

Require zero row/assertion failures and expected per-thread connection behavior.

13. Required target telemetry snapshot

Preserve:

SELECT USER_NAME, SESSION_STATE, COUNT(*) AS CONNECTIONS
FROM INFORMATION_SCHEMA.SESSIONS
WHERE USER_NAME='JMETER_READ'
GROUP BY USER_NAME, SESSION_STATE;

SELECT SQL_STATEMENT, EXECUTION_COUNT,
       MIN_EXECUTION_TIME, MAX_EXECUTION_TIME,
       AVERAGE_EXECUTION_TIME, CUMULATIVE_EXECUTION_TIME
FROM INFORMATION_SCHEMA.QUERY_STATISTICS
ORDER BY CUMULATIVE_EXECUTION_TIME DESC
FETCH FIRST 10 ROWS ONLY;

14. Evidence packet

Artifact Required content
Versions JMeter 5.6.3, Java 17, H2 2.5.250, H2 JDBC JAR filename/hash.
Connection config URL redacted for secrets, driver, pool variable, max connections/wait, preinit/autocommit/isolation.
Least privilege JMETER_READ SELECT-only grant; admin observer separated from load.
Query set Prepared Item Lookup and explicit diagnostic Pool Hold Probe.
Baseline/restored JTL Elapsed, JDBC latency, success/error, assertions.
Broken JTL/log Pool wait/timeout symptom preserved.
DB telemetry JMETER_READ session counts + QUERY_STATISTICS snapshots.
Generator/DB process state CPU/memory/headroom for baseline, broken, restored.
Validity statement Direct JDBC/JMeter pool findings are not application-pool/end-to-end capacity.
Cleanup H2 stopped; disposable DB/result files removed only after evidence review.

15. Validity statement

Example: “Using Apache JMeter 5.6.3/Java 17 with H2 2.5.250 on a loopback-only TCP server, the JMeter plan used a SELECT-only JMETER_READ account against 5,000 synthetic rows. The valid baseline used Max Connections=0, so two JMeter threads each owned a one-connection pool; prepared lookups returned exactly one row with zero assertion failures. A deliberately broken comparison used one shared JMeter connection with 5 ms Max Wait and a read-only CPU-heavier probe. The constrained profile showed acquisition wait/possible pool timeout while DB session/query evidence distinguished client-pool contention from target execution. Restoring Max Connections=0 returned the original valid behavior. These results characterize JMeter's JDBC client-pool and H2/query behavior in a tiny local experiment; they do not measure an application's connection pool or end-to-end production capacity.”

16. Verification checklist

  • H2/driver/JMeter/Java versions recorded.
  • Server bound only to 127.0.0.1:9092; no -tcpAllowOthers.
  • JMeter account is JMETER_READ with SELECT only.
  • All JMeter SQL is SELECT/read-only.
  • Baseline 2×10 lookups return one row each.
  • Broken pool profile preserved separately and correctly diagnosed.
  • Restored profile is max=0 / max-wait=1000 and passes.
  • Configured versus achieved sample counts and generator headroom recorded.
  • DB session/query evidence preserved independently.

17. Cleanup / rollback

  1. Stop JMeter runs and verify no active JMETER_READ sessions remain.
  2. Stop the H2 TCP server cleanly with Ctrl+C in its lab terminal.
  3. Retain JTL/log/telemetry evidence until review completes.
  4. After review, delete only the disposable db/, results/, and lab data files.
  5. Remove the H2 JAR from JMeter lib only if it was added solely for this lab and no other plans depend on it; restart JMeter after classpath changes.
  6. No production/shared DB, real credential, OS/JVM global tuning, remote engine, container, or paid service was modified.

18. What Chapter 16 adds to the operating model

The performance-testing operating model now has a database/JDBC contract: driver/DB version, authorized JDBC URL, least-privilege credential source, JMeter pool variable/max/wait/preinit/autocommit/isolation, exact prepared SQL/parameter types, row/result assertions, connection/session/query telemetry, generator state, configured-versus-achieved load, and an explicit statement separating direct DB results from application-pool/end-to-end results.

Chapter 17 moves to JMS, FTP, LDAP, TCP, and Other Protocol Test Patterns. The same discipline carries forward: each protocol introduces its own connection/session/data/credential/result semantics, but target authorization, bounded load, correlation/state ownership, causal telemetry, and cleanup remain unchanged.

Knowledge check

Why can the broken pool profile legitimately contain failures?

What proves the waiting thread may not have reached H2?

Why restore Max Connections=0 instead of simply raising threads?

What is the strongest evidence that JMETER_READ is least-privilege?

What is Chapter 17's core transition?

Next chapter

JMS, FTP, LDAP, TCP, and Other Protocol Test Patterns

Chapter 17 broadens JMeter beyond HTTP/JDBC and teaches how to model protocol-specific connections, sessions, acknowledgements/messages/files/directory operations, binary/text framing, credentials, and result evidence safely.

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.