Testing Concurrent Behavior
Concurrency tests must control or amplify orderings that ordinary unit tests rarely encounter.
Why This Matters
Use multiple real connections or processes, barriers, fault injection, and invariant assertions rather than sleeps alone.
Working Model
Concurrency tests must control or amplify orderings that ordinary unit tests rarely encounter. Correctness comes from preserving an explicit invariant across every permitted ordering, not from expecting one observed timing.
Practical Rules
- Start operations behind a synchronization barrier.
- Use separate database connections.
- Repeat stress tests but keep one deterministic scenario.
- Inject failure around commit and acknowledgement.
- Assert final invariants and side-effect counts.
Failure Modes
- Using one transaction or connection for both actors.
- Relying on tiny sleeps.
- Passing because the race did not happen.
- Checking responses but not durable state.
Verification
- Record timing and actor IDs.
- Run against supported database engines.
- Keep tests isolated and bounded.
- Make failures reproducible with seeds or barriers.
What You Should Be Able To Do
After this lesson, you should be able to explain deterministic interleaving tests, stress tests, fault injection, and invariant-based assertions, choose a suitable approach for a real PHP project, and verify the result instead of relying on assumptions.
Practice
Practice: Test A Lost Update
Create a two-connection test for optimistic locking.
Your answer must:
- state the intended outcome;
- show the commands, data flow, or implementation shape;
- identify at least one unsafe alternative;
- explain how the result will be verified.
Show solution
Read one version through both connections, update through the first, then attempt the stale second update and assert it affects zero rows and returns a conflict.
The important part is not memorising one command or vendor screen. The solution makes the invariant, failure behavior, and verification evidence explicit.
Practice: Test An Idempotent Retry
Simulate a committed operation whose response is lost.
Your answer must:
- state the intended outcome;
- show the commands, data flow, or implementation shape;
- identify at least one unsafe alternative;
- explain how the result will be verified.
Show solution
Execute and commit the first request but discard its response, retry with the same key, and assert one business row, one side effect, and the same logical result.
The important part is not memorising one command or vendor screen. The solution makes the invariant, failure behavior, and verification evidence explicit.
Practice: Design A Stress Test
Stress a conditional stock decrement without making the test destructive.
Your answer must:
- state the intended outcome;
- show the commands, data flow, or implementation shape;
- identify at least one unsafe alternative;
- explain how the result will be verified.
Show solution
Use isolated fixtures, bounded workers, known initial stock, collect all outcomes, assert successes equal available stock, and clean up deterministically.
The important part is not memorising one command or vendor screen. The solution makes the invariant, failure behavior, and verification evidence explicit.