Yegor Gaidar
Available · Q4 2026

Proof · 03 — interactive

Architecture simulator

Assemble a request path, push a load through it, and watch ten seconds of it happen. Every stage keeps its own backlog, so the page shows which tier saturates first, what that does to latency, and how retries turn one slow tier into an outage. Everything is computed in this page — the simulator makes no network calls.

00 Load 400 rps
01 Load balancer 2 000 rps

2 000 rps and 500 queued connections per instance (fixed).

02 App 640 rps

Capacity = replicas × concurrency ÷ service time. 64 waiting requests per replica.

03 Cache off

20 000 rps tier; hits never reach the DB.

04 Queue + workers off

One job per async request, one DB write per job.

05 Database 250 rps

Capacity = connections ÷ query time. 200 waiting queries per connection, then rejects.

App 640 rps · DB 250 rps · cache off · no workers

Idle — nothing has run yet

Offered load
—
Mean latency
—
Failing requests
—
Bottleneck
—
Sync path
LB 2 000 rps idle
App 640 rps idle
DB 250 rps idle

Run a scenario to plot the model output.

JavaScript is off, so the scenario cannot run. The model, its constants and its limits are written out below; nothing on this page talks to a server.

How it works

The model, in full

A fluid queue model: each stage holds a backlog in requests, serves at its capacity, and queues the rest. Waiting time is backlog ÷ capacity; mean latency is the sum of service times and waits along the path.

Constants and rules — documented defaults, not measurements
StageCapacityBuffer
LB2 000 rps × instances500 queued connections per instance
Appreplicas × concurrency ÷ service time64 waiting requests per replica
Cache20 000 rps; hits skip the DB5 000 queued lookups
Queueworkers ÷ job timeyour buffer; beyond it, jobs are rejected
DBconnections ÷ query time200 waiting queries per connection
  • Step size 250 ms, ten seconds, forty steps. No randomness anywhere: the same inputs always give the same trace.
  • 30% of requests wait for a worker; each processed job is one DB write, so slow workers load the database too.
  • Requests slower than the client timeout are retried by the client; the retry share comes from the previous step, which is what makes the load climb instead of settling.
  • A run fails when more than 5% of offered requests fail in one step — by timeout, or because a buffer was full and the request was rejected.
  • The fix button takes the smallest single change that the same model says passes with headroom (≤ 85% peak utilisation) and reruns it. No hidden tuning: the patch is printed before it is applied.

What this is not

  • Not a benchmark of your hardware. Nothing is measured — every number is the model's output for the configuration you set.
  • No latency distribution: a request class either fits the timeout or it does not, so timeouts arrive as a step, not a tail.
  • No autoscaling, no circuit breaker, no warm-up, no retry budget. Adding any of those is a different model.
  • Not a scan of anything: the page makes no network calls and stores nothing about you.

The useful question is not “will it hold 400 rps” but “which tier gives up first, and would the fix I picked have helped before the retries hid it”. That is what the run answers.