Chapter 25Lesson 05~320 minutes

Checkpoint Lab — Distributed Testing, Remote Engines, Network Topology, and Synchronization

The checkpoint treats two engines as a small production-like generator fleet. You must prove the load equation and shard allocation before execution, run either the secured low-scale RMI topology or the faithful independent-worker simulation, verify 6+6=12 achieved samples, then intentionally point engine-b at the wrong shard and diagnose the failure without changing the target or hiding the original evidence.

Checkpoint2 engines6+6=12Data shard mismatchDistributed evidence packet

Learning objectives

  • Inventory controller/engine versions, ports, files, properties and trust boundary.
  • Predict exact per-engine/aggregate load and data records.
  • Execute a tiny remote RMI/SSL run or faithful independent-worker simulation.
  • Verify aggregate/per-engine sample and target counts independently.
  • Trigger one engine-b data-file mismatch and interpret JTL/log/target evidence.
  • Repair only engine-b and prove a new clean 12-sample run.

1. Exact assumptions and hard ceilings

Item Checkpoint baseline
JMeter Apache JMeter 5.6.3 on controller + both engines.
Java Java 17 JDK on controller + both engines; no mixed Java baseline.
Plugins No third-party plugins; plugin/user-JAR manifest expected empty.
Target http://127.0.0.1:8025 only.
Engine A registry 1099, server engine 4000, engine.id=engine-a, shard a001-a006.
Engine B registry 1100, server engine 4100, engine.id=engine-b, shard b001-b006.
Controller callbacks client.rmi.localport=5000; up to 5000–5002.
RMI security SSL enabled; disposable shared lab keystore only.
Per-engine workload 2 threads ×3 loops ×1 sampler = 6.
Aggregate workload 2 engines ×6 = 12 target samples, peak 4 configured threads.
Pacing 50 ms.
Result sender StrippedBatch explicit/default.
Data policy No recycle; stop thread at EOF; six unique rows/shard.
Evidence aggregate JTL + ENGINE_ID/ACCOUNT_ID, controller/engine logs, target JSONL, versions/hashes/ports/resource state.
Fallback two independent CLI workers + JTL merge; does not validate RMI.
Abort: public RMI exposure, SSL disabled, version/JAR/hash mismatch, unexpected remote host, >12 target events in a normal run, duplicate/wrong-shard data, missing engine, controller/engine saturation, clock mismatch that invalidates chronology, or target not reset between runs.

2. Record the topology before execution

Controller:
  127.0.0.1
  callbacks 5000-5002
  JMX + remote-global.properties + aggregate JTL

Engine A:
  registry 1099
  server engine 4000
  engine.id=engine-a
  data.file=.../data/engine-a.csv

Engine B:
  registry 1100
  server engine 4100
  engine.id=engine-b
  data.file=.../data/engine-b.csv

Target:
  127.0.0.1:8025

3. Engine/controller inventory and file manifest

For every node record:

  • jmeter -v output = 5.6.3;
  • java -version = approved Java 17 build;
  • JMX SHA-256;
  • engine shard SHA-256;
  • third-party lib/ext plugin/JAR list (expected none);
  • RMI registry/server/callback ports and SSL keystore identity;
  • UTC epoch/time and generator CPU/heap/network baseline.

4. Predictions before the valid run

P1 — workload: engine-a=6, engine-b=6, aggregate JTL=12, target events=12.

P2 — data: engine-a uses exactly a001-a006; engine-b uses exactly b001-b006; 12 unique accounts; no duplicates.

P3 — properties: -Gthreads/-Gloops/-Grun.id/-Gtarget.* are identical on both engines; engine.id/data.file stay distinct because they were set at each server startup.

P4 — failure run: if engine-b is deliberately restarted with engine-a.csv, its six requests should fail 409 wrong_shard while RMI/controller and engine-a remain healthy.

5. Target authorization/preflight

curl.exe --fail --silent http://127.0.0.1:8025/health
curl.exe --fail --silent -X POST http://127.0.0.1:8025/reset
curl.exe --fail --silent http://127.0.0.1:8025/stats

State must show zero requests/errors before each run.

6. Execute the valid two-engine run

Start the two engines exactly as in Lesson 2, then run:

& "$env:JMETER_HOME\bin\jmeter.bat" `
  -n `
  -t .\plans\distributed-local.jmx `
  -q .\config\remote-results.properties `
  -R127.0.0.1:1099,127.0.0.1:1100 `
  -G.\config\remote-global.properties `
  -l .\results\p25-check-valid\aggregate.jtl `
  -j .\results\p25-check-valid\controller-jmeter.log

If RMI is not feasible on the workstation, run the two independent workers described in Lesson 2 with run ID p25-check-valid, merge their JTLs, and clearly label the evidence simulation—not RMI validation.

7. Verify valid run independently

python tools/verify_distributed.py   results/p25-check-valid/aggregate.jtl   results/server-events.jsonl   p25-check-valid   6

Require PASS. Also inspect controller/engine logs for RMI/CSV/script errors and record generator/network headroom. A 12-row central file alone is insufficient if, for example, all 12 somehow came from one engine.

8. Deliberate configuration mismatch: wrong engine-b shard

  1. Preserve the entire valid run.
  2. Stop only engine-b.
  3. Restart engine-b with engine.id=engine-b but data.file=.../engine-a.csv.
  4. Reset target state.
  5. Use new run ID p25-check-broken and run the same 2×3 plan.

Expected:

  • 12 offered samples still possible;
  • engine-a six successes;
  • engine-b six 409 wrong_shard failures;
  • aggregate JTL identifies ENGINE_ID=engine-b with a-prefixed ACCOUNT_ID;
  • target JSONL reason=wrong_shard for engine-b;
  • controller/RMI logs need not show transport failure, proving this is an engine data-state error.

9. Diagnose before repairing

Preserve:

  • broken aggregate JTL;
  • controller log;
  • engine-a/engine-b logs;
  • engine-b startup command/property manifest;
  • target event log/stats;
  • file hashes.

The causal chain is: engine-b startup property → wrong local CSV → ACCOUNT_ID values → HTTP query → target shard validation → 409. Do not disable assertions or change the target.

10. Least-destructive repair

  1. Stop only engine-b.
  2. Restore data.file=.../engine-b.csv.
  3. Restart engine-b with same secured RMI ports/version.
  4. Reset target state.
  5. Run new ID p25-check-repaired.
  6. Require verifier PASS (6+6=12, 12 unique accounts, zero failures).

11. Aggregate/result evidence requirements

Evidence Required observation
Total-load calculation 2 engines ×(2×3×1)=12; peak configured threads=4.
-R/-G command exact engine endpoints and remote-global properties.
File/plugin manifest JMX/shard hashes; identical core/plugin state; no third-party plugin.
Aggregate JTL 12 rows, ENGINE_ID/ACCOUNT_ID columns, zero failures in valid/repaired.
Per-engine count six engine-a + six engine-b in JTL and target events.
Controller log remote initialization/completion/result-transfer state.
Engine logs startup/RMI/CSV/script/test state for both engines.
Target JSONL 12 unique accounts valid/repaired; broken run shows six wrong_shard.
Generator state controller + both engines CPU/heap/network and sender mode.

12. Configured-versus-achieved validity statement

Example: “Apache JMeter 5.6.3/Java 17 ran the same JMX on two secured private/loopback engines. Each engine received global 2-thread ×3-loop workload properties but retained a unique engine ID and six-record CSV shard. The predicted total was 12 samples/4 configured threads. Controller aggregate JTL and target JSONL independently showed six samples per engine and 12 unique accounts; controller and engine logs/resource state showed no transport/generator saturation. A deliberate engine-b data.file mismatch produced six engine-b HTTP 409 wrong_shard failures while engine-a and RMI remained healthy; restoring only engine-b's shard returned the verifier to 6+6=12 successes. This validates distributed load/data/result math at tiny scale; it does not establish production generator or target capacity.”

13. Network/security verification

  • RMI SSL enabled; no server.rmi.ssl.disable=true.
  • Only approved private/loopback registry/server/callback ports reachable.
  • Controller callbacks 5000–5002 accounted for.
  • No public port-forward/NAT exposure.
  • Lab keystore treated as disposable and not reused as production PKI.
  • Controller/engine clocks recorded/synchronized.

14. Cleanup / rollback

  1. Stop both remote engines gracefully after evidence capture; -X is an optional CLI remote-exit mechanism when intentionally included in the run contract.
  2. Stop the localhost fixture.
  3. Preserve valid/broken/repaired JTL/log/manifests/events until review.
  4. Delete the disposable RMI keystore only after no lab engine/controller needs it.
  5. Remove temporary CSV shards/simulation files after the retention window.
  6. No production/public target, real credential, remote public RMI service, recorder CA, managed cloud, paid CI, database/message system or OS/JVM global tuning was changed.

15. What Chapter 25 adds to the operating model

The production performance-testing operating model now has a distributed generator contract: controller/engine inventory, exact JMeter/Java/plugin versions, engine-local properties/files, total-load equation, secured/fixed RMI topology, remote-global property manifest, data-shard ownership/hashes, sender/result strategy, central/per-engine logs/results, clock synchronization, controller/engine resource evidence, missing-engine policy, target per-engine counts and cleanup are required before distributed capacity results are trusted.

Chapter 26 moves to Load Generator Sizing, JVM Tuning, OS Limits, and Network Capacity. It measures how many threads/sockets/bytes/requests each generator can sustain before the generator—not the SUT—becomes the limiting system.

Knowledge check

What are the two most important pre-run predictions in this checkpoint?

Why does the broken engine-b run prove a data configuration failure rather than RMI failure?

If aggregate JTL has 12 rows but target shows 12 from engine-a and zero from engine-b, is the run valid?

What does the simulation fallback fail to validate?

What is Chapter 26's bridge?

Next chapter

Load Generator Sizing, JVM Tuning, OS Limits, and Network Capacity

Chapter 26 turns each distributed engine into a capacity-measured generator with explicit JVM, OS, socket and NIC headroom.

Official references and version notes

Version and compatibility note

Version-sensitive statements were 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; JMeter 5.6.3 requires Java 8+. JMeter remote mode sends the test plan to every remote server, but each engine runs the entire test plan; workload is not divided automatically. All controller/server nodes should run exactly the same JMeter version, and JMeter discourages mixing Java versions. External data files are not sent by the controller and must exist on every server in the expected path; plugins/user JARs likewise need an explicit identical deployment. CLI -r starts servers listed in remote_hosts; -Rhost1,host2 explicitly selects/overrides the server list; -Gname=value or -Gpropertyfile sends JMeter properties to remote servers. Since JMeter 4.0, RMI uses SSL by default. JMeter ships create-rmi-keystore, whose generated test certificate is documented as valid for seven days and uses default alias rmi/passphrase changeit; these defaults are suitable only for a disposable private lab. server.rmi.ssl.disable defaults to false and this chapter never disables it. The remote-testing manual describes dynamic server-engine ports conceptually, while the current properties reference lists server.rmi.localport default 4000; production/private labs should set registry/server/callback ports explicitly instead of relying on defaults. The controller reverse callback client.rmi.localport defaults to 0 (random); when set non-zero JMeter can use up to three consecutive ports. Current default remote sample sender mode is StrippedBatch: successful response bodies are stripped and results are batched. Remote mode can consume more resources than equivalent independent CLI workers, and the controller/client or its network can become the bottleneck.

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.