Chapter 09Lesson 04~160 minutes

Pre-Processors, Post-Processors, Extractors, and Correlation: Diagnostics, Failure Modes, and Production Practices

Correlation failures often present as 400/401 responses far downstream from the real cause. The diagnostic method is to reconstruct producer response → extractor → variable → Pre-Processor → consumer request state while preserving the first failed JTL, engine log, and target evidence.

DiagnosticsHard-coded tokenWrong sampleCross-thread raceSecret logging

Learning objectives

  • Detect hard-coded replay tokens and distinguish them from true response-derived state.
  • Identify an extractor that runs against the wrong sample/scope.
  • Diagnose one token shared across threads through a global property.
  • Recognize unresolved/literal variables and stale defaults.
  • Find Pre/Post-Processors scoped more broadly than intended.
  • Preserve correlation evidence without logging sensitive values.

1. Preserve first-failure evidence

Diagnostic reruns remain local: http://127.0.0.1:8000, ≤3 threads, ≤3 loops. Keep the failed JMX, JTL, jmeter.log, synthetic server-event log, exact non-secret CLI, and one-thread debug evidence before changing extractor or processor scope.

2. Diagnostic sequence

Correlation failure follows a producer-to-consumer chain

Follow the arrows as a causal data flow. The response exists before the extractor can create a variable, and the downstream request is constructed from that thread's own variable state.

flowchart TD
E[Preserve JMX + JTL + jmeter.log + target events] --> V[Confirm JMeter/Java/script versions]
V --> P[Identify producer response + expected dynamic field]
P --> X[Inspect Post-Processor type/scope/expression/default]
X --> S[Inspect thread-local variable after producer]
S --> R[Inspect Pre-Processor + downstream request construction]
R --> T[Compare target rejection/fingerprint evidence]
T --> G[Inspect generator CPU/GC/script errors]
G --> C[CI/container/distributed state if relevant]
C --> F[Smallest correction]
F --> N[Small bounded rerun]

3. Failure mode: replaying a recorded token

Broken plan:

Start Session — /session/start
Use Session — /session/use?token=tok-recorded-123&prepared=TOK-RECORDED-123

The Start Session response is ignored. Every thread replays the same historical token. The local fixture rejects it as unknown. Repair by extracting the current response token and consuming the per-thread variable.

4. Intentionally broken example: extractor attached to the wrong sample

Move JSON JMESPath Extractor from Start Session under /noise. The response does not contain session.token, so CORR_TOKEN=CORR_MISSING. Use Session receives that sentinel and fails.

Evidence chain: Start Session body contains a real synthetic token → extractor never processes that response → Debug Sampler after /noise shows sentinel → target Use Session logs token_fp=missing/unknown → JTL records 401/failed assertion.

Repair only the extractor scope: child of Start Session.

5. Failure mode: reusing one token across threads via property

Broken JSR223 PostProcessor:

props.put('CORR_TOKEN_GLOBAL', vars.get('CORR_TOKEN'))

Consumer:

${__P(CORR_TOKEN_GLOBAL,CORR_MISSING)}

Two threads now race to overwrite one process-wide property. A thread can send another user's token. Even when the server accepts a valid token, the session identity no longer belongs causally to the current virtual user.

Repair by removing the property mutation and reading ${CORR_TOKEN} from the current thread.

6. Failure mode: unresolved variable remains literal

If the request references ${SESSION_TOKEN} but the extractor creates CORR_TOKEN, JMeter does not magically map the names. The literal may appear in the outgoing request or an empty/default path may be used depending on component handling.

Search the JMX for both names, establish one canonical reference name, and use an explicit producer assertion. Do not patch the server to accept the literal.

7. Failure mode: Post-Processor scope is too broad

A Thread-Group-level extractor runs after Start Session, Use Session, and Noise. After the correct extraction, a later response without session.token may overwrite the variable with the default. This can make an unrelated sampler appear to “expire” the token.

Move the extractor to the producer sampler. Broad scope should be used only when every response in scope legitimately exposes the same contract.

8. Failure mode: PreProcessor scope is too broad

A Thread-Group-level token-preparation PreProcessor runs before Start Session—when there is no token yet—and before every other sampler. Its PREP_MISSING state may be confusing in debug output or accidentally influence samplers that never need it.

Move it under Use Session, the consumer that needs the derived value.

9. Failure mode: logging sensitive correlated values

This is inappropriate for real secrets:

log.info('token=' + vars.get('AUTH_TOKEN'))

jmeter.log is routinely attached to CI artifacts and support tickets. Log only synthetic values in this lab. In real testing, log a non-reversible fingerprint or simply correlation state (present/missing) and thread/label metadata.

10. Correlation failure versus server-side token expiry

A correct extractor can still produce a token that the SUT later rejects because of expiry, single-use semantics, clock skew, or session invalidation. Distinguish:

Evidence JMeter correlation defect SUT token/session behavior
Producer response Token missing or parser contract changed. Token present/valid format.
Debug variable Sentinel/literal/stale/cross-thread value. Matches producer value.
Consumer request Wrong/missing token. Correct token sent.
Target log Unknown/malformed token. Known token but expired/used/revoked.

11. Correlation cost versus target latency

Extractors and scripts run on the generator around the sample. Expensive parsing can reduce achieved throughput even when HTTP sampler elapsed and target service time remain stable. Compare generator CPU/GC and baseline/with-correlation throughput before attributing a slowdown to the server.

12. Distributed/CI note: variable scope does not cross engines

Thread-local variables live in each worker thread on each JMeter engine. Do not design a remote test that expects one engine's extracted variable to appear on another engine. Shared external state must belong to the SUT or an explicit test-data/control system, not an accidental JMeter property mailbox.

13. Shortcuts to reject

  • Do not replace failed extraction with a copied production token.
  • Do not store per-user tokens in global properties.
  • Do not add retries/long sleeps before proving the correlation chain.
  • Do not move extractors to broad scope just to make variables “available everywhere.”
  • Do not dump all variables/responses/headers under load to debug one token.
  • Do not disable TLS/RMI validation or redirect tests to a public echo service.
  • Do not delete the broken JTL/log after the repaired run succeeds.

Knowledge check

What is the smallest repair when Start Session contains the token but the extractor is under /noise?

Why is props.put(CORR_TOKEN_GLOBAL, ...) a multi-user bug?

How do you distinguish missing extraction from server-side expiry?

Why should real auth tokens not be written to jmeter.log?

Can a variable extracted on remote engine A be read directly by a thread on remote engine B?

Next lesson

Checkpoint: break and restore independent session correlation

Lesson 5 runs a two-step, two-thread session workflow, deliberately breaks the extractor contract/scope, preserves the failure, restores the causal mapping, and packages a correlation evidence set.

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.