Chapter 09Lesson 05~215 minutes

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.

CheckpointTwo-step sessionBroken extractorThread isolationEvidence packet

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.
Abort: target mismatch, >3 threads, >3 loops, unexpected external URL, real credential discovered in JMX/logs, unexpected 5xx, or unsafe generator pressure.

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

  1. Four Start Session responses should issue four distinct synthetic tokens/session IDs.
  2. Each Use Session should be accepted only when it carries the token produced earlier in its own thread/iteration plus the correct prepared value.
  3. Thread echo should show both thread 1 and 2.
  4. JTL should contain eight successful samples and zero failures in the good profile.
  5. 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_TOKEN has a synthetic tok-... value;
  • PREPARED_TOKEN is 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.token in good/repaired plans.
  • Default sentinel is explicit and producer assertion rejects it.
  • PreProcessor is child of Use Session and reads/writes vars, not props.
  • 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

Example: “Using Apache JMeter 5.6.3 and Java 17 against a loopback-only synthetic session service, a JSON JMESPath Post-Processor extracted 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

  1. Stop the local fixture.
  2. Keep good/broken/repaired artifacts until review is complete.
  3. Remove Debug Sampler/View Results Tree from load profiles.
  4. Do not carry synthetic token dumps into real test practices.
  5. 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?

Why is changing session.token to session.missingToken a useful broken example?

Why should the broken JTL and jmeter.log be kept after repair?

What is the smallest repair for the broken expression?

What is the bridge to Chapter 10?

Next chapter

Regular Expressions, CSS/JQuery, XPath, JSONPath, and Boundary Extraction

Correlation is now conceptually correct. Chapter 10 compares extractor languages and response formats so you can choose the narrowest robust parser for HTML, XML, JSON, text, headers, and bounded markers.

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 and compatibility note

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.

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