Checkpoint Lab — REST and JSON API Performance Testing
The checkpoint validates the full operating discipline: generated IDs are correlated, each virtual user owns its resource, protocol/business assertions stay meaningful, one deliberate server failure is preserved rather than retried away, and cleanup succeeds even though the lifecycle contains that failure.
Learning objectives
- Create/read/update/delete one synthetic resource per user/iteration.
- Correlate server-generated IDs and assert ownership/version/value.
- Inject one controlled local failure without retrying it.
- Continue to per-item DELETE and then run an exact RUN_ID cleanup sweep.
- Predict request/resource/result changes before execution and verify them independently.
- Produce client/server/generator/cleanup evidence appropriate to a release decision.
1. Exact 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. |
| API fixture |
prompt14-api-fixture-v1, Python standard
library.
|
| Target | http://127.0.0.1:8000 only. |
| Load | 2 threads × 2 lifecycle loops = 4 created resources maximum. |
| Thread error action | Continue, so the deliberate 500 does not skip cleanup. |
| Failure injection | Exactly one controlled failure per thread or one selected opportunity; never retried. |
| Final cleanup |
Exact RUN_ID sweep, then list/metrics verify
zero leftovers.
|
| Secrets | None; synthetic owner/name data only. |
2. Setup and target authorization/preflight
$RunId = "RUN-CH14-CHECKPOINT"
(Invoke-WebRequest -UseBasicParsing http://127.0.0.1:8000/health).Content
(Invoke-WebRequest -UseBasicParsing http://127.0.0.1:8000/metrics).Content
(Invoke-WebRequest -UseBasicParsing "http://127.0.0.1:8000/v1/items?run_id=$RunId").Content
Before the run, require the run-scoped item count to be zero. If it is not, archive the listing and clean only that RUN_ID before starting.
3. Checkpoint JMeter tree
Test Plan
├── UDV / properties: BASE_HOST, BASE_PORT, RUN_ID
├── HTTP Request Defaults
├── HTTP Header Manager: Accept + Content-Type application/json
└── Thread Group — 2 threads × 2 loops
Action after Sampler error: Continue
└── Transaction Controller — Resource Lifecycle
├── Create Item
│ ├── status/JSON business assertions
│ └── JMESPath Extractor item.id -> ITEM_ID
├── Read Item
│ └── ownership/value assertions
├── Update Item
│ └── version=2/value=2 assertions
├── Throughput Controller — Inject Failure
│ Total executions=1; Per User=ON
│ └── Inject Synthetic Failure
│ POST /v1/items/${ITEM_ID}/inject-failure
│ Expected to fail with 500; preserve as deliberate failure evidence
└── Delete Item
DELETE /v1/items/${ITEM_ID}
Assert 204
The Throughput Controller controls how many branch opportunities execute; it is not an RPS controller. Per User=ON with Total Executions=1 produces at most one injected failure per thread (two total) in this 2-thread checkpoint.
4. Required predictions before execution
Prediction A — resource state: each of four lifecycle iterations creates a unique ID owned by that JMeter thread; normal per-item DELETE removes every item even though each thread encounters one deliberate 500.
Prediction B — error evidence: the run contains two intentional failed Inject Synthetic Failure samples (one per thread), while Create/Read/Update/Delete should pass.
Prediction C — cleanup: immediately after normal
run, active resources for the RUN_ID should already be zero; final
run-scoped cleanup should report deleted=0. If it
reports >0, that is valuable evidence that per-item cleanup
failed/skipped.
Prediction D — target/JTL distinction: transaction/reporting samples may add JTL rows, but server event counts include only actual HTTP operations.
5. Exact request contracts
Create JSON:
{
"run_id": "${RUN_ID}",
"owner": "user-${__threadNum}",
"name": "checkpoint-${__threadNum}-${__time()}",
"value": 1
}
Read: GET /v1/items/${ITEM_ID}
Update body:
{"value":2,"name":"updated-${__threadNum}"}
Inject: POST /v1/items/${ITEM_ID}/inject-failure
Delete: DELETE /v1/items/${ITEM_ID}
6. Preserve the injected failure rather than making it green
The injected endpoint returns HTTP 500 and leaves the resource
intact. Do not use Response Assertion Ignore Status, a script
calling prev.setSuccessful(true), or retries. The 500
is deliberately part of this checkpoint's error taxonomy.
Thread Group continues after sampler error so the subsequent DELETE still executes. This is a cleanup-control choice for the synthetic lab, not a general recommendation to continue after every production error.
7. Run checkpoint in CLI mode
jmeter.bat -n `
-t plans\checkpoint-api.jmx `
-JBASE_HOST=127.0.0.1 `
-JBASE_PORT=8000 `
-JRUN_ID=RUN-CH14-CHECKPOINT `
-l results\checkpoint\results.jtl `
-j results\checkpoint\jmeter.log `
-e -o results\checkpoint\report
Record generator CPU/memory during the short run. If the injector is unexpectedly saturated, the run is not valid for target-performance interpretation.
8. Verify normal per-item cleanup first
(Invoke-WebRequest -UseBasicParsing `
"http://127.0.0.1:8000/v1/items?run_id=RUN-CH14-CHECKPOINT").Content
(Invoke-WebRequest -UseBasicParsing http://127.0.0.1:8000/metrics).Content
Expected count=0. If count>0, save the listing before the final sweep so the cleanup failure remains diagnosable.
9. Run bounded run-scoped cleanup even after failure
This is a safety-net operation that can be run independently of the failed JMeter threads:
PowerShell:
Invoke-WebRequest -UseBasicParsing `
-Method DELETE `
"http://127.0.0.1:8000/v1/runs/RUN-CH14-CHECKPOINT/items"
Bash:
curl --fail --silent -X DELETE http://127.0.0.1:8000/v1/runs/RUN-CH14-CHECKPOINT/items
The endpoint can delete only resources tagged with that exact run ID. It cannot delete other runs by accident unless the operator supplies another run ID.
10. Verify zero leaked state
curl --fail --silent "http://127.0.0.1:8000/v1/items?run_id=RUN-CH14-CHECKPOINT"
curl --fail --silent http://127.0.0.1:8000/metrics
Checkpoint is not complete until the run-scoped list reports count 0.
11. Analyze result and service evidence
python tools/analyze_api_jtl.py results/checkpoint/results.jtl
python tools/analyze_api_events.py results/api-events.jsonl
Required error taxonomy:
- intentional synthetic 500 injections;
- unexpected protocol errors (must be zero);
- business/assertion failures (must be zero outside deliberate injected sampler);
- cleanup/resource leaks (must be zero after final verification).
12. Performance interpretation
Report Create/Read/Update/Delete latency/throughput separately. The synthetic fixture deliberately makes writes slightly slower, so operation mix affects aggregate throughput. The injected 500 path does minimal work and must not be counted as a “fast successful write.”
State configured load (2×2) and achieved valid samples, plus generator headroom and server event timing. This tiny loopback checkpoint validates mechanics; it cannot establish production capacity.
13. Required evidence packet
| Artifact | Required content |
|---|---|
| Fixture version/schema |
prompt14-api-fixture-v1, request/response
examples.
|
| Resolved run config | BASE_HOST/PORT, RUN_ID, 2 threads ×2 loops, error action. |
| Correlation proof | Four generated ITEM_ID values tied to correct owner/run. |
| Assertion evidence | Status + key JMESPath business checks; failure messages retained. |
| Error taxonomy | Two intentional 500s; zero unexpected protocol/business failures. |
| Operation metrics | Per-label JTL p50/p95/throughput + generator CPU/memory. |
| Server evidence | Operation/status counts + service_wall_ms. |
| Cleanup evidence | Post-run RUN_ID listing, sweep result, final count=0. |
| Raw artifacts |
JTL, matching jmeter.log, HTML report, exact
JMX/properties.
|
14. Verification checklist
- Target resolves exactly to 127.0.0.1:8000.
- Maximum four resources can be created.
- Each Create returns unique ITEM_ID and correct owner/run.
- Read and Update operate on that thread's correlated ITEM_ID.
- Exactly two deliberate 500 samples are preserved; no retry/Ignore Status hides them.
- Delete executes after injected failure because Thread Group action is Continue.
- Run-scoped cleanup targets only the checkpoint RUN_ID.
- Final RUN_ID item count is zero.
- JTL/jmeter.log/server events/generator state are preserved.
15. Validity statement
prompt14-api-fixture-v1 on 127.0.0.1:8000, two threads
executed two isolated resource lifecycles each. Create returned
server-generated IDs that were extracted into thread-local
variables; Read and Update assertions verified
owner/run/value/version. One controlled HTTP 500 was injected per
thread and preserved as a failure; the Thread Group continued so
per-item idempotent DELETE still executed. A final cleanup endpoint
scoped to RUN-CH14-CHECKPOINT was run independently and the
subsequent list verified zero leaked resources. Operation-level JTL
metrics, matching jmeter.log, generator utilization, and server
event timing were preserved. This proves REST/JSON correlation,
correctness, failure classification, and cleanup mechanics on a tiny
loopback fixture—not production capacity, public-API authorization,
or retry safety for arbitrary writes.”
16. Cleanup / rollback
- Verify current RUN_ID count=0.
- Run the exact RUN_ID sweep again if needed; its lab contract is idempotent.
- Stop
api_fixture.py. - Retain JTL/log/report/event evidence until review completes.
- Delete disposable synthetic artifacts according to local policy.
- No public API, real credential, recorder CA, remote engine, database, container, paid service, or system-wide JVM/OS configuration was changed.
17. What Chapter 14 adds to the operating model
The performance-testing operating model now has an API resource-lifecycle contract: base URL authorization, request/response schema, media headers, per-user resource ownership, correlated generated IDs, transport/business assertions, read/write metric populations, explicit error taxonomy, cleanup/sweep ownership, and zero-leak verification are reviewed before a result becomes release evidence.
Chapter 15 moves to Web Services, SOAP, Authentication, Tokens, and Session Workflows. The resource and JSON discipline from this chapter carries forward, while Chapter 15 adds authentication state, token/session renewal, credential boundaries, and SOAP/web-service protocol concerns.
Knowledge check
Why does the checkpoint keep the deliberate 500 as a failed sample?
It is intentional diagnostic evidence; turning it green would corrupt the error taxonomy and hide the failure path.
What allows cleanup to continue after that 500?
The bounded checkpoint's Thread Group action is Continue, followed by per-item DELETE and an independent RUN_ID sweep.
Why is the final RUN_ID list stronger than trusting DELETE samples alone?
It independently verifies target state contains zero resources from the run, catching skipped/failed cleanup.
Why must the injected 500 not be included as a fast successful write?
It takes a different error path and performs less work, so treating it as successful write performance would bias the metric.
What is the Chapter 15 bridge?
Authentication/tokens/session state and SOAP/web-service workflows build on the same correlated, asserted, safely scoped request lifecycle.
Official references and version notes
- Component Reference — HTTP Request, Header Manager, JSON/JSON JMESPath Extractor, JSON/JSON JMESPath Assertion, Response Assertion, Transaction Controller, and related core semantics.
- Elements of a Test Plan — scope/execution order and variable behavior.
- Getting Started — GUI authoring/debugging versus CLI load execution.
- Generating Dashboard Report — CSV result requirements, statistics, request summary, and error tables.
- Properties Reference — result-save fields including assertion failure messages.
- Best Practices — CLI execution, lean listeners, and injector validity.
- 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+. HTTP Request samplers are used directly for
GET/POST/PUT/DELETE. JSON JMESPath Extractor is a built-in
Post-Processor for extracting response values into JMeter
variables; JSON/JSON JMESPath Assertions parse JSON and fail on
missing/invalid paths before optional value comparison. Assertion
failure messages are enabled by default in CSV result output and
are kept explicit in chapter commands. Meaningful load runs use
CLI mode with raw CSV JTL plus matching jmeter.log;
HTML dashboard generation uses the same JTL. The mandatory API
fixture is Python-standard-library only and runs on loopback, so
no container, cloud service, database, external API, driver, or
plugin is required.
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.