Checkpoint Lab — Pre-Processors, Post-Processors, Extractors, and Correlation
The checkpoint proves that independent virtual users can discover dynamic session state instead of replaying it. You will run a correct two-thread flow, intentionally point the extractor at a missing field, preserve the failure, and then restore the producer → variable → consumer chain without changing the target or copying a token manually.
Learning objectives
- Implement Start Session → extract token → prepare request → Use Session.
- Predict token/session behavior for two concurrent JMeter threads.
- Prove synthetic correlation state remains independent per thread.
- Break the JMESPath extractor so the explicit sentinel is produced.
-
Diagnose failure from JTL,
jmeter.log, variable/debug state, and target event evidence. - Restore the smallest correct change and state the production operating-model implications.
1. Assumptions and hard ceilings
| Item | Checkpoint baseline |
|---|---|
| JMeter | Apache JMeter 5.6.3. |
| Java | Java 17 JDK lab baseline; JMeter 5.6.3 requires Java 8+. |
| Plugins | None. Built-in JSON JMESPath Extractor + JSR223/Groovy only. |
| Target | http://127.0.0.1:8000 only. |
| Good profile | 2 threads × 2 loops → 4 Start + 4 Use requests. |
| Broken profile | 1 thread × 1 loop for failure diagnosis. |
| Token handling | Synthetic tokens only; load evidence records fingerprints/presence, not full token values. |
| Remote/CI | No live remote engines; local/free mandatory path only. |
2. Prepare a clean workspace
jmeter-ch09-checkpoint/
├── fixtures/correlation_fixture.py
├── plans/checkpoint-good.jmx
├── plans/checkpoint-broken.jmx
├── tools/analyze_correlation.py
├── evidence/
│ ├── predictions.txt
│ ├── extractor-settings.txt
│ └── validity.txt
└── results/
├── good/
├── broken/
└── repaired/
Start the fixture with a fresh event log and preflight
/health.
3. Build the correct checkpoint tree
Test Plan
└── Thread Group — 2 users × 2 loops
├── HTTP Request Defaults — HttpClient4 / 127.0.0.1:8000
├── Start Session — GET /session/start
│ ├── JSON JMESPath Extractor
│ │ variable: CORR_TOKEN
│ │ expression: session.token
│ │ match: 1
│ │ default: CORR_MISSING
│ └── JSR223 Assertion — CORR_TOKEN must not be CORR_MISSING
└── Use Session — GET /session/use?token=${CORR_TOKEN}&prepared=${PREPARED_TOKEN}&thread=${__threadNum}
├── JSR223 PreProcessor — uppercase CORR_TOKEN → PREPARED_TOKEN
└── JSON JMESPath Assertion — status == accepted
4. Predict before running
- Four Start Session responses should issue four distinct synthetic tokens/session IDs.
- Each Use Session should be accepted only when it carries the token produced earlier in its own thread/iteration plus the correct prepared value.
- Thread echo should show both thread 1 and 2.
- JTL should contain eight successful samples and zero failures in the good profile.
- Server logs should contain token fingerprints, not full token values.
5. One-thread authoring proof
Before the two-thread run, temporarily use 1 thread × 1 loop with Debug Sampler between Start and Use. Verify:
-
CORR_TOKENhas a synthetictok-...value; -
PREPARED_TOKENis not set until the PreProcessor runs before Use; - Use Session returns
status=accepted.
Then disable Debug Sampler/View Results Tree for load execution.
6. Run the good two-thread profile
mkdir -p results/good
jmeter -n \
-t plans/checkpoint-good.jmx \
-l results/good/results.jtl \
-j results/good/jmeter.log \
-Jjmeter.save.saveservice.print_field_names=true \
-Jjmeter.save.saveservice.response_code=true \
-Jjmeter.save.saveservice.response_message=true \
-Jjmeter.save.saveservice.assertion_results_failure_message=true
python tools/analyze_correlation.py results/good/results.jtl
curl --fail --silent http://127.0.0.1:8000/stats
7. Verify independent multi-user state
Inspect the synthetic server JSONL:
- 4 Start Session entries with distinct session IDs/token fingerprints;
- 4 Use Session entries with
prepared_ok=true; - Use entries include thread echo 1 or 2;
- no Use request has
token_fp=missing; - JTL good profile is 8/8 successful.
Exact request interleaving can differ. Independence is proven by unique producer state and valid downstream use, not by assuming a fixed thread execution order.
8. Intentionally break the extractor expression
Copy the plan to checkpoint-broken.jmx, reduce to 1
thread × 1 loop, and change only:
JMESPath expression:
session.token
becomes:
session.missingToken
Keep Default Value = CORR_MISSING. Predict that Start
Session still returns HTTP 200 and contains the real token, but the
extractor produces the sentinel and the extraction-guard assertion
fails.
9. Preserve the broken run
jmeter -n \
-t plans/checkpoint-broken.jmx \
-l results/broken/results.jtl \
-j results/broken/jmeter.log \
-Jjmeter.save.saveservice.print_field_names=true \
-Jjmeter.save.saveservice.assertion_results_failure_message=true
python tools/analyze_correlation.py results/broken/results.jtl
Do not delete or overwrite these artifacts after repair.
10. Diagnose the broken chain
| Stage | Expected broken evidence | Interpretation |
|---|---|---|
| Producer response | JSON contains session.token. |
Target emitted usable state. |
| Extractor config | Looks for session.missingToken. |
Parser contract is wrong. |
| Variable | CORR_TOKEN=CORR_MISSING. |
Explicit no-match sentinel. |
| Assertion/JTL | Producer sample failed with correlation-token message. | Failure attached to owning response. |
| Consumer | Should be skipped/stopped by chosen error policy, or if continued will fail predictably. | Do not treat downstream rejection as root cause. |
11. Apply the least destructive repair
Restore only the extractor expression to session.token.
Do not paste a token into the JMX, loosen the server, convert the
token into a global property, or add retries. Rerun the original
two-thread profile into results/repaired/ and compare
with the good baseline.
12. Optional second failure: scope, not expression
For a one-thread diagnostic only, move the correct extractor under
/noise or Thread Group. Preserve how the variable
becomes missing/overwritten. Restore the child-of-producer
placement. This separates “wrong expression” from “wrong
sample/scope” as two distinct correlation failure classes.
13. Generator and validity check
Record CPU/memory during the tiny good/repaired runs. Built-in JSON extraction plus one cached Groovy uppercase operation should have negligible cost here. If generator saturation appears, stop; do not infer target capacity from an invalid injector.
14. Required evidence packet
| Artifact | Required content |
|---|---|
| Good/repaired JMX | Extractor and PreProcessor scope/settings; two-thread profile. |
| Broken JMX | Only the intended expression/scope fault. |
| Synthetic response fragment |
Shows session.token exists without real
credentials.
|
| One-thread Debug evidence | Synthetic variable by thread; remove listener afterward. |
| Good/broken/repaired JTL | Success/failure samples and assertion messages. |
Matching jmeter.log |
Engine/script/extractor diagnostics. |
| Server JSONL/stats | Session IDs, token fingerprints, thread echo, prepared_ok. |
| Correlation diagram/settings note | Producer → extractor → variable → PreProcessor → consumer. |
| Validity statement | Independent local correlation proof, not production auth/capacity evidence. |
15. Verification checklist
- Target exactly
127.0.0.1:8000. - Good/repaired profile = 2 threads × 2 loops; broken profile = 1 × 1.
-
Extractor is child of Start Session and uses
session.tokenin good/repaired plans. - Default sentinel is explicit and producer assertion rejects it.
-
PreProcessor is child of Use Session and reads/writes
vars, notprops. -
Server evidence proves distinct token fingerprints/session IDs and
prepared_ok=true. - No real token/credential exists in JMX/JTL/logs.
- Broken evidence remains preserved after repair.
16. Validity statement
session.token into a
thread-local JMeter variable, a cached Groovy PreProcessor derived
request metadata immediately before the consuming sampler, and two
threads independently issued and consumed distinct synthetic session
tokens. An intentionally wrong JMESPath produced the explicit
CORR_MISSING sentinel and failed the producer
assertion; restoring only the expression repaired the workflow.
Server evidence used token fingerprints rather than raw values. This
demonstrates JMeter correlation, processor scope, and per-thread
state semantics; it does not establish production authentication
security, token lifetime behavior, or target capacity.”
17. Cleanup
- Stop the local fixture.
- Keep good/broken/repaired artifacts until review is complete.
- Remove Debug Sampler/View Results Tree from load profiles.
- Do not carry synthetic token dumps into real test practices.
- No production target, credential, certificate, remote engine, database, plugin, container, or system-wide JVM/OS setting was changed.
18. What Chapter 09 adds to the operating model
The performance-testing operating model now includes a correlation provenance contract: producer sampler, extraction source/expression/default, processor scope, variable ownership, transformation logic, consumer request, failure policy, and redaction strategy are all reviewable.
The current repository curriculum bridges next to Chapter 10, Regular Expressions, CSS/JQuery, XPath, JSONPath, and Boundary Extraction, where each extractor family is explored in depth. The published site may lag curriculum updates, so the repository curriculum remains the chapter-path/title source for generated content.
Knowledge check
What proves the two-thread plan has independent correlation state?
Distinct producer session/token fingerprints are later accepted in matching consumer requests, with no global property carrying the token.
Why is changing session.token to session.missingToken a useful broken example?
The producer remains correct while the extractor contract alone fails, producing a clear sentinel and attributable assertion failure.
Why should the broken JTL and jmeter.log be kept after repair?
They preserve the root-cause evidence and make the repaired comparison auditable instead of erasing the original failure.
What is the smallest repair for the broken expression?
Restore the correct JMESPath; do not hard-code a token, weaken the target, or introduce a shared property.
What is the bridge to Chapter 10?
Chapter 10 deepens the extraction toolbox—regex, CSS/JQuery, XPath, JSONPath, and boundary extraction—now that the causal correlation model is established.
Official references and version notes
- Elements of a Test Plan — Pre-Processor/Post-Processor purpose, scope, and execution order: configuration → pre-processors → timers → sampler → post-processors → assertions → listeners.
- Component Reference — Regular Expression, JSON/JMESPath, Boundary, JSR223 Pre/Post Processor, User Parameters, and Result Status Action Handler semantics.
- Functions and Variables — thread-local JMeter variables versus process-wide JMeter properties.
- Best Practices — CLI load execution and scripting/performance guidance.
- Apache JMeter downloads — current stable release and Java requirement.
Version-sensitive behavior was rechecked against current Apache
JMeter primary documentation on 2026-09-05. The course baseline
remains Apache JMeter 5.6.3 with a Java 17 JDK
for labs and no third-party plugins; JMeter 5.6.3 requires Java
8+. Pre-Processors execute before their in-scope sampler and
Post-Processors execute after the sampler but before Assertions.
Processor behavior is scope-driven rather than determined by
visual sibling order. Built-in Post-Processor extractors store
results in JMeter variables, which are normally thread-local. JSON
JMESPath Extractor accepts one JMESPath expression, can select a
match, and can set an explicit default when nothing matches.
Regular Expression and Boundary Extractors likewise support
explicit defaults, which are especially useful during debugging so
a missing extraction is distinguishable from a processor that
never ran. JSR223 Pre/Post Processors provide
vars (thread variables), props (shared
JMeter properties), and the relevant sampler/result context;
Groovy with compiled-script caching is preferred over BeanShell
when scripting is actually necessary. Mandatory labs use a
built-in extractor for correlation and only a tiny Groovy
PreProcessor for a deliberately simple transformation; Chapter 10
covers extractor families in greater depth.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.