Queue Simulation with M/M/c/K Algorithm
This post presents the M/M/c/K extension of the M/M/c queueing model, implemented in QueueForge. QueueForge now supports both models — M/M/c (infinite capacity) and M/M/c/K (finite capacity) — selectable via a dropdown in the parameters panel. If you are new to queueing theory and the M/M/c model, please read the introductory post Queue Simulation with M/M/c Algorithm first — it covers Kendall's notation, Poisson arrivals, the Erlang C formula, and Little's Law. This post focuses exclusively on the finite-capacity variant and how it changes the mathematical analysis.
The application is freely accessible at: https://computationalmindset.com/apps/QueueForge?model=mmck
From M/M/c to M/M/c/K: Finite System Capacity
The M/M/c model assumes an unlimited waiting room: any number of customers can queue up while all servers are busy. In practice, many real systems have a physical or logical upper bound on the total number of customers allowed (queue + servers combined). When the system is full, new arrivals are rejected (lost or blocked).
In Kendall's notation A/B/c/K/N/D, the fourth field K specifies the maximum number of customers in the system (waiting plus being served). An M/M/c/K system therefore has:
- M (first): Poisson arrivals with rate $\lambda$
- M (second): exponential service times with rate $\mu$ per server
- c: number of parallel identical servers
- K: maximum system capacity — must satisfy $K \geq c + 1$
Two immediate consequences distinguish M/M/c/K from M/M/c:
- Always stable: because the queue is bounded, the system is always in steady state regardless of the ratio ρ. Even with λ > cμ the system never overflows indefinitely.
- Rejection probability: a customer arriving when the system is at capacity K is turned away. This probability must be computed and factored into all performance metrics via an effective arrival rate.
The M/M/c/K Mathematical Model
State probabilities
The steady-state probability $P_n$ of having $n$ customers in the system is: $$P_n = \begin{cases} P_0 \cdot \dfrac{a^n}{n!} & 0 \le n \le c \\[6pt] P_0 \cdot \dfrac{a^c}{c!} \cdot \rho^{n-c} & c < n \le K \end{cases}$$ where $a = \lambda / \mu$ is the offered load and $\rho = \lambda / (c \cdot \mu)$ is the server utilization. The normalization constant $P_0$ (probability the system is empty) is obtained by imposing $\sum_{n=0}^{K} P_n = 1$: $$P_0 = \frac{1}{\displaystyle\sum_{k=0}^{c-1} \frac{a^k}{k!} \;+\; \frac{a^c}{c!} \cdot \frac{1 - \rho^{K-c+1}}{1 - \rho}} \qquad (\rho \ne 1)$$ When $\rho = 1$ the geometric sum degenerates and the denominator becomes: $$\sum_{k=0}^{c-1} \frac{a^k}{k!} + \frac{a^c}{c!} \cdot (K - c + 1)$$
Rejection probability
The probability that the system is full — and an arriving customer is rejected — is $P_K$: $$P_K = \begin{cases} P_0 \cdot \dfrac{a^K}{K!} & K \le c \\[6pt] P_0 \cdot \dfrac{a^c}{c!} \cdot \rho^{K-c} & K > c \end{cases}$$ $P_K$ is sometimes called the loss probability or blocking probability.
Effective arrival rate
Because a fraction $P_K$ of all arrivals is lost, the rate of customers that actually enter the system is: $$\lambda_{\text{eff}} = \lambda \cdot (1 - P_K)$$ All performance metrics must be computed with $\lambda_{\text{eff}}$ rather than the nominal $\lambda$, otherwise Little's Law would be violated.
Average queue length
The average number of customers waiting (not being served) is: $$L_q = \frac{a^c \cdot \rho \cdot P_0}{c!} \cdot \frac{1 - \rho^{K-c+1} - (K-c+1)\,\rho^{K-c}(1-\rho)}{(1-\rho)^2} \qquad (\rho \ne 1)$$ When $\rho = 1$: $$L_q = \frac{a^c \cdot P_0}{c!} \cdot \frac{(K-c)(K-c+1)}{2}$$
Remaining performance metrics
All other metrics are derived from $L_q$ and $\lambda_{\text{eff}}$ using Little's Law:
Average number of customers in the system $L$: $$L = L_q + \frac{\lambda_{\text{eff}}}{\mu}$$
Average waiting time in queue $W_q$: $$W_q = \frac{L_q}{\lambda_{\text{eff}}}$$
Average time in the system $W$: $$W = \frac{L}{\lambda_{\text{eff}}}$$
Comparison with M/M/c
When $K \to \infty$ the M/M/c/K formulas converge to the M/M/c results: $P_K \to 0$, $\lambda_{\text{eff}} \to \lambda$, and the $L_q$ formula reduces to the Erlang C expression $C(c,a) \cdot \rho / (1-\rho)$. In a finite-capacity system the queue is always shorter (some customers are rejected before queuing), so $L_q^{(K)} \le L_q^{(\infty)}$ for any $K$ and any $\rho$.
Numerical example: M/M/1/5 queue
Consider an overloaded single-server system with $c = 1$, $K = 5$, $\lambda = 10$ arrivals/min, and $\mu = 8$ customers/min — a configuration that would be unstable as a plain M/M/1 ($\rho = 1.25 > 1$), but is always stable under M/M/1/5:
- Server utilization: $\rho = 10 / (1 \times 8) = 1.25$ (overloaded, yet stable thanks to $K$)
- Rejection probability: $P_5 \approx 0.271$ (about 27.1% of arrivals are turned away)
- Effective arrival rate: $\lambda_{\text{eff}} = 10 \times (1 - 0.271) \approx 7.29$ customers/min
- Average queue length: $L_q \approx 2.22$ customers
- Average time in system: $W = L / \lambda_{\text{eff}} \approx 0.43$ min = 25.8 seconds
Simulation of M/M/c/K
The QueueForge simulation engine handles the finite-capacity case by extending the standard M/M/c time-stepped simulation with a single additional rule: when a new arrival is generated and the current number of customers in the system equals $K$, the customer is rejected and the rejection counter is incremented. All other mechanics (Bernoulli arrival trials with $p = \lambda \cdot \Delta t$, service completion trials with $p = \mu \cdot \Delta t$ per busy server, FIFO queue management) remain identical to the M/M/c simulation described in Queue Simulation with M/M/c Algorithm.
This allows the simulation to directly estimate the empirical rejection rate, which converges to the theoretical $P_K$ as simulation time increases. The rejection rate exposed by the simulation engine is: $$\hat{P}_K = \frac{\text{total rejected}}{\text{total arrivals}}$$
User Interface and Interaction
QueueForge provides a Queue Model dropdown at the top of the parameters panel
to switch between M/M/c and M/M/c/K at any time. The M/M/c/K model can also be opened directly via the
URL parameter ?model=mmck (e.g. .../QueueForge?model=mmck);
switching the dropdown updates the URL automatically so the link stays shareable.
When M/M/c/K is selected, an additional Max Capacity (K) slider appears alongside the standard parameters ($\lambda$, $\mu$, $c$, simulation speed). The metrics panel additionally shows:
- Rejection probability: theoretical PK and empirical rejection rate side by side
- Effective arrival rate: the actual throughput entering the system
Technical Implementation
The M/M/c/K model is implemented in the MMCKQueueingModel class
(src/models/MMCKQueueingModel.ts), a companion to the MMCQueueingModel
class for the unconstrained case. The simulation engine in QueueSimulation.ts accepts
an optional maxCapacity parameter: when provided it activates M/M/c/K mode transparently,
without changing the simulation loop structure.
The overall architecture remains the same four-layer design:
- Models layer (
models/):MMCKQueueingModelimplements P0, PK (Erlang B blocking probability), Lq and all derived metrics with the closed-form formulas above - Simulation layer (
simulation/):QueueSimulationwithmaxCapacityfor rejection tracking - Visualization layer (
visualization/): Canvas chart unchanged; the rejection rate is shown in the metrics panel - UI layer (
components/,App.tsx): React components with an additional K slider
The test suite in src/models/__tests__/MMCKQueueingModel.test.ts verifies known M/M/1/K and M/M/2/K
values, validates the Erlang B blocking probability against published tables,
checks that $P_K \to 0$ as $K \to \infty$, and confirms that $\lambda_{\text{eff}} \le \lambda$ always holds.
Try QueueForge
Experience QueueForge with the M/M/c/K model directly in your browser:
Launch QueueForge (M/M/c/K)
The application requires no installation and works on any modern web browser.
For the best experience, use a recent version of Chrome, Firefox, Safari, or Edge.
Development and Contribution
The M/M/c/K extension of QueueForge was developed with Claude Code following the BMAD (Breakthrough Method of Agile AI-driven Development) approach, as part of a continued exploration in AI-assisted software development for educational tools.
The project follows standard modern TypeScript and JavaScript development practices:
Clone the repository:
git clone https://github.com/ettoremessina/QueueForge.git
cd QueueForge
Install dependencies:
npm install
Run development server:
npm run dev
This starts a local development server with hot module replacement for rapid iteration.
Build for production:
npm run build
The optimized output is generated in the dist/ folder and can be deployed to any static hosting service.
Run tests:
npm test
Source Code and License
The complete source code is available on GitHub at this address: QueueForge.
These materials are distributed under MIT license; feel free to use, share, fork and adapt these materials as you see fit.
Also please feel free to submit pull-requests and bug-reports to this GitHub repository or contact me on my social media channels available on the top right corner of this page.