The component that falls over first is rarely the one you are watching
Most capacity surprises are not about the component under pressure. They are about the quiet one behind it that every path happens to reach.
Ask someone which part of their system would break first under ten times the traffic and they will usually name the busiest thing they can see — the API, the web server, whatever shows up in the dashboard they look at most. It is a reasonable guess, and it is often wrong.
The thing that gives way first is usually one hop further in. Not because it is weak, but because it is reached by more paths than anyone counted.
Load follows every path, not the likely one#
Here is the shape of the problem. A system has two services. Each one is modest, well provisioned, nothing alarming. Both of them read the same database.
Each service, looked at on its own, is comfortable. The database is reached by both, so it carries the sum. Nobody drew that sum anywhere, because it does not appear on either service's diagram — it only appears when you add the arrows up.
This is why Diagravo propagates load the way it does. A Client's activeUsers
is treated as requests per second and carried outward along every connection
to every component reachable from it. A component reached by several clients,
or by several routes, adds up everything that arrives.
Data stores are the exception that makes the arithmetic honest: they count what reaches them but never pass it on. Two backends sharing one database do not inflate each other's load — but the database carries both.
A load balancer does not divide your load#
This is the part that surprises people most, and it is deliberate.
Put a load balancer in front of three backends and the instinct is that each backend now sees a third of the traffic. During playback, the balancer really does pick one target per request. But the capacity dots do not divide anything: each backend is shown the whole of the load.
That is not a bug in the model. It is the question the model is answering:
How loaded could this component get, via any path?
A third of the traffic is the average case. The whole of it is the case you get when two of the three are draining, or restarting, or the health check is flapping. If each backend cannot survive the full load, you do not have three backends — you have one backend and two spares you have not tested.
The one thing that genuinely reduces load on its way through is a rate limiter, which admits only part of a flood. Everything else passes it on intact.
Reading the result#
Load is divided by whichever setting is that component type's capacity, and the ratio decides what you see:
| Ratio of load to capacity | What it means |
|---|---|
| under 0.7 | fine |
| 0.7 and above | a warning in the Console |
| 0.9 and above | a Load Balancer starts steering away from it |
| 1.0 and above | overloaded |
A component with no capacity setting, or one set to 0, shows no dot at all.
That reads as "not modelled" — not as "safe". The blank ones are worth a second
look, because an unmodelled component is exactly where an unexamined assumption
sits.
How to actually find it#
The useful move is not to study the component you are worried about. It is to work backwards from the end of the paths:
- Find every component that is at the end of a path — the databases, the caches, the object stores, the queues that nothing reads from in the diagram.
- For each one, count how many distinct routes reach it. Anything reached by more than two is a candidate.
- Raise the client load until something turns. Watch which dot turns first, not which turns red hardest.
- If the first thing to turn is behind a load balancer, check whether it survives the undivided load. If not, the balancer is buying you less than you think.
That ordering matters. The component that turns first is the one that sets your ceiling. Everything that turns after it is a consequence, and fixing a consequence moves nothing.
What this does not tell you#
Worth being blunt about the limits, because a clean diagram is not a clean system.
This is a worst-case arithmetic model of fan-out and capacity. It does not know your query patterns, your connection pools, your GC pauses, your network, your retry storms or your cache hit rate. It will not predict a latency cliff, and it cannot tell you that a particular index is missing.
What it does do is make fan-out visible before traffic does. A diagram that adds the arrows up for you catches the two-services-one-database class of problem in about thirty seconds, and that class of problem is responsible for a disproportionate share of the incidents nobody saw coming.
Treat a clean run as a prompt for the next question — what else could reach that database? — rather than an answer.
The capacity rules, the per-component capacity settings and the attack scenarios are documented in full under Capacity, warnings and attack scenarios. How a request walks the diagram during playback, and the components that behave differently, are in How playback works. If you would rather just try it, the quickstart builds a three-component diagram and overloads it on purpose in about five minutes.
Want to try this on your own architecture? Open the editor — no sign-up needed.