Skip to content

Queueing theory (M/M/1 & M/M/c)

Queueing theory gives exact formulas for a handful of idealized systems. YourSimulation reproduces those systems as small node graphs, and its simulated numbers match the formulas — which is exactly why you can trust it on the messier systems that have no formula. This page maps the theory onto the engine and shows the validation.

Kendall notation

A queue is summarized as A/S/c:

  • A — the arrival process (interarrival-time distribution).
  • S — the service-time distribution.
  • c — the number of parallel servers.

M means Markovian — exponential (memoryless) times. So M/M/1 is exponential arrivals, exponential service, one server; M/M/c is the same with c servers sharing one queue.

Mapping onto YourSimulation

An A/S/c queue is a four-node chain:

mermaid
graph LR
  src["source<br/>exp interarrival (A)"] --> q[queue] --> svc["resource<br/>c servers, exp service (S)"] --> out[sink]
  • arrival rate λ=1/interarrival mean
  • per-server service rate μ=1/service mean
  • servers on the resource is c

Key formulas

Utilization — the fraction of server capacity in use:

ρ=λcμ

The system is stable (queue does not grow without bound) iff ρ<1. See utilization.

M/M/1 — mean wait in queue and mean queue length:

Wq=ρμλ,Lq=λWq

(Lq=λWq is Little's law: the average number waiting equals the arrival rate times the average wait.)

M/M/c — with c servers the wait depends on the Erlang-C probability that an arriving entity finds all servers busy, C(c,λ/μ):

Wq=C(c,λ/μ)cμλ

The Erlang-C formula itself is standard; the docs don't re-derive it. (The validation suite computes it directly to check the engine — see validation.test.ts.)

Beyond M/M/c: abandonment and routing

Real systems rarely make customers wait forever, and rarely have a single line. Two extensions matter most, and YourSimulation models both.

Abandonment (reneging) — the Erlang-A model. Add impatience to M/M/c and you get M/M/c+M, known as Erlang-A: each waiting customer also has an exponential patience and abandons if service doesn't start in time. Closed forms exist but are unwieldy; the practical effect is intuitive — abandonment acts as a relief valve, so the system stays stable even when ρ1 (offered load above capacity), at the cost of lost customers. In a model this is a queue's reneging: { patience }. The behaviour is easy to sanity-check: under light load almost no one abandons (waits match M/M/c); as load climbs, the abandonment rate rises smoothly.

Routing — join-shortest-queue. With several parallel servers you can keep one shared line (M/M/c) or split arrivals across separate lines. Sending each arrival to the shortest line (JSQ) is near-optimal and, for a symmetric system, keeps the lines balanced so their utilizations converge. A branch in shortest-queue mode does exactly this — measuring whole-station occupancy (waiting + in service) with random tie-breaks. Probabilistic splitting, by contrast, lets lines drift out of balance and performs worse under the same load.

See the Blocks reference for how to wire reneging, routing, shared resource pools, and batching into a model.

Validation

These checks live in packages/engine/test/validation.test.ts and run on every commit, asserting the engine matches theory within tolerance (utilization to ±0.03, waits to ±10%). The "Engine" column below is the actual simulator output (mean ± ci95):

SystemMetricTheoryEngine (mean ± CI)
M/M/1, λ=0.1, μ=0.125 (ρ=0.8)utilization0.8000.794 ± 0.011
M/M/1, λ=0.1, μ=0.125 (ρ=0.8)Wq32.030.80 ± 3.04
M/M/1, λ=0.1, μ=0.125 (ρ=0.8)Lq3.23.07 ± 0.32
M/M/3, λ=1, mean svc 2.4 (ρ=0.8)Wq≈2.589 (Erlang-C)2.60 ± 0.10

The M/M/1 figures come from docs/examples/mm1.json; the M/M/3 figure from an equivalent 3-server model. Every theory value sits inside the engine's confidence band. (Note: the mm1.json example uses a shorter horizon/fewer replications than the validation suite, so its CI is wider — the test suite uses a longer run to assert the ±10% bound reliably.)

Try it

bash
npx tsx packages/engine/src/cli.ts run docs/examples/mm1.json --pretty
# utilization ≈ 0.8 on "svc"; avgWait ≈ 32 on "q"

Where to go next

  • Distributions — replace exponential service with realistic shapes (the "M" stops applying, the simulation keeps working).
  • Discrete-event simulation — how the engine actually computes these numbers.
  • Glossary — definitions of utilization, throughput, and more.