Two clients hit a stateful Karate mock at the same moment. Does the mock handler run their matched Scenarios concurrently?
answer
- Ask what protects the shared globals
- Parallel at the socket, serial deeper in
- One request inside the handler at a time
- Correctness bought with throughput
basics
~10 sNo. Karate's mock handler serialises request handling - one request at a time goes through matching and step execution, whatever the server's threads are doing - because the retained globals are shared mutable state.
solid answer
~50 sNo. The handler's request entry point is guarded by mutual exclusion, so exactly one request at a time goes through scenario matching and step execution, however many threads the underlying server has. The reason is the design that makes a Karate mock stateful: one globals map and one engine per mock feature, both read and written by every request. Interleaving two requests would let one see the other's half-finished state. What you get from that is real: an ordinary read-modify-write on a mock's in-memory store needs no locking of your own, and a counter cannot lose an increment. What you pay is throughput - any slow step in a matched `Scenario`, such as a `call` or an outbound hop, holds the guard while every other client waits. A Karate mock is a correctness tool, and is not a sound stand-in when concurrency itself is what you are measuring.
go deeper
The takeaway is simply that a Karate mock handles one request at a time, so the counters and stores you write in a feature file are safe as they stand.
Explain the link: the handler is serialised because the globals map and the feature's engine are shared and written on every request.
Trace the cost to the work you put in a matched scenario - a call or an outbound hop blocks every other client - and use that to diagnose a mock that stalls for everyone at once.
Decide where a serialised stand-in stops being the right tool, and set the policy for splitting one shared stateful mock into several before its handler becomes the queue.
## The guarantee A Karate mock server accepts connections on a normal multi-threaded HTTP server, but the handler behind it is not concurrent. Its request entry point is guarded so that only one request is inside at a time. Everything a request does - evaluating each `Scenario` name expression in order, running the matched scenario's steps, writing its variables back to the globals - happens with that guard held. So the concurrency model of a Karate mock is: **parallel at the socket, serial at the handler**. ## Why it has to be that way The leaf's other facts force it. A mock feature has: - **One globals map** per handler, seeded by the `Background` and written back after every matched request. - **One runtime and engine** per mock feature, reused across requests rather than created fresh per call. Both are shared mutable state on the hot path of every request. Without the guard, two requests would interleave reads and writes of the same variables, and the classic increment - read `counter`, add one, write it back - would lose updates under exactly the load you would use a mock to simulate. Worse, request-scoped bindings such as the parsed body and matched path parameters live on that shared engine too, so an interleaved request could match one path and then execute against another's data. Serialising is the cheap, obviously-correct answer for a component whose job is to be a predictable stand-in. It also means the mock's own upstream documentation can promise that state "persists across requests" without any caveat about races. ## What you gain - **No locking in feature files.** `* def id = ~~(id + 1)` or `* cats[id] = cat` is atomic with respect to other requests. There is no Karate-level mutex to reach for, and none is needed. - **Deterministic ordering.** Requests are handled in the order the handler receives them, so a test that fires two calls and asserts on the resulting state gets a stable answer. - **First-match-wins stays meaningful.** Matching happens under the same guard, so the matching decision and the steps that follow cannot be split by another request. ## What you pay | Work inside a matched Scenario | Effect on other clients | |---|---| | A few `def` and `match` steps | negligible | | A `call` into another feature file | they wait for it | | An outbound HTTP hop from the scenario | they wait for the round trip | | A deliberate sleep | they wait for the whole sleep | The pattern to watch for is a mock that does real work per request. A mock that forwards to a live upstream is the sharpest case: every forwarded round trip is serialised, so the mock's throughput ceiling is one upstream call at a time no matter how many clients you point at it. An injected response delay is the useful exception. The delay is applied by the server layer after the handler has returned, so it does not hold the guard - a slow-by-design stub still serves other clients while it waits. ## Operating consequences 1. **Do not read a Karate mock's latency as the system's.** A queue in front of a serial handler produces numbers about the mock, not about the service it stands in for. 2. **Keep matched scenarios cheap.** The steps in a mock scenario are on the critical path of every other client. Prefer a literal or a variable already built in the `Background` to work done per request. 3. **Scale by starting more servers, not more threads.** Each `MockServer` is its own handler with its own guard and its own globals - which also means they share no state, so the choice buys parallelism and costs isolation in the same move. 4. **Debug hangs at the handler.** If a mock stops answering everyone at once, look for one scenario blocked on something external rather than for a networking problem; a single stuck request stops the whole mock. ## The interview framing What this really probes is whether you understand *why* a Karate mock can be stateful at all. Retained globals and a serialised handler are the same design decision seen from two sides: the state is safe because nothing else is running, and nothing else is running because the state has to be safe. A candidate who has only used mocks as fixed responders will assume concurrency; a candidate who has written a stateful one will have hit the ceiling.
- Given that, do you need any locking of your own around a counter in a mock feature?No. Matching and step execution for one request complete before the next request enters the handler, so a read-modify-write on a global cannot interleave. This is one of the few places in test tooling where the shared-state hazard is genuinely designed away rather than delegated to you.
- Your mock stops answering every client at once. What do you look at first?Whichever scenario is currently executing. Because the handler is serial, one request blocked on something external - an outbound call to a host that is not answering, a call into a feature that hangs - stops the entire mock. A total stall points at one stuck request, not at the network.
- How would you get more throughput out of a Karate mock without changing the feature file?Start more servers and spread clients across them. Each one is its own handler with its own guard and its own globals map, so they run in parallel - but they also share no state, so anything that relied on one accumulating store has to be rethought before you split it.
saying these in an interview costs you the question
- Assumes the handler runs scenarios on many threads
- Adds home-made locking inside the feature file
- Reads a Karate mock's throughput as the service's
- Blames the network when the whole mock stalls at once
- Thinks more server threads make a stateful mock faster