A finite queue replaces unbounded accumulation with an admission-versus-rejection tradeoff. Bounded occupancy means storage is bounded; a system rejecting most requests can still fail its business objectives.
In M/M/1/K, K includes the job in service
Keep Poisson arrivals, independent exponential service and a single FCFS server, but allow at most K jobs inside. Full arrivals are rejected immediately without retries or abandonment. For offered-load ratio r=λ/μ, the finite-state stationary probabilities are:
πn=∑j=0Krjrn,n=0,…,K.
At r=1, every state has probability 1/(K+1); there is no singularity. With positive arrival and service rates the finite chain is positive recurrent even when offered traffic exceeds capacity. Here r is not actual utilization and may exceed one.
Poisson arrivals give rejection probability πK. Accepted throughput, busy fraction and mean residence of admitted jobs are:
Here L=∑nπn. Using external λ with internal occupancy incorrectly includes requests that never entered.
Preparing the visual
Finite queues and admission control · Experiment
M/M/1/K with μ=100/s. K includes the job in service; full arrivals are rejected without retries. The finite chain has a stationary distribution, but admission, rejection and delay jointly determine usefulness.
{"id":"queueing-08","title":"Finite queues and admission control · Experiment","summary":"M/M/1/K with μ=100/s. K includes the job in service; full arrivals are rejected without retries. The finite chain has a stationary distribution, but admission, rejection and delay jointly determine usefulness.","height":1000,"html":"<p class=\"intro\" data-i18n=\"scope\"></p><div class=\"controls\"><div class=\"control\"><label for=\"parameter-0\" data-i18n=\"rate\"></label><output id=\"value-0\" for=\"parameter-0\"></output><input type=\"range\" id=\"parameter-0\" data-parameter=\"0\" min=\"20\" max=\"200\" step=\"10\" value=\"120\"/></div><div class=\"control\"><label for=\"parameter-1\" data-i18n=\"limit\"></label><output id=\"value-1\" for=\"parameter-1\"></output><input type=\"range\" id=\"parameter-1\" data-parameter=\"1\" min=\"1\" max=\"30\" step=\"1\" value=\"5\"/></div></div><div class=\"tools\"><button id=\"reset\" data-i18n=\"reset\"></button></div><div id=\"timeline\"></div><div id=\"plots\"></div><div id=\"metrics\"></div><p id=\"status\" role=\"status\" aria-live=\"polite\"></p>","css":".plotarea{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px}.yticks{display:flex;flex-direction:column;justify-content:space-between;padding:3px 0;font-size:13px;font-variant-numeric:tabular-nums;color:var(--muted)}*{box-sizing:border-box}.intro{margin:0 0 16px;color:var(--muted);line-height:1.65}.controls{display:grid;gap:14px}.control{display:grid;grid-template-columns:1fr auto;gap:6px 12px;align-items:center}.control label{font-size:16px;line-height:1.5}.control input{grid-column:1/-1;width:100%;min-height:28px}.control output{font-variant-numeric:tabular-nums}.control select{grid-column:1/-1;width:100%;max-width:100%;padding:8px;font:inherit;background:var(--surface);color:var(--ink);border:1px solid var(--rule);border-radius:6px}.tools{margin:14px 0}.tools button{font:inherit;padding:8px 14px;min-height:42px}.chart{margin:22px 0}.chart h3{font-size:16px;margin:0 0 6px;line-height:1.5}.chart svg{display:block;width:100%;height:160px;overflow:hidden}.scale,.axes{font-size:14px;color:var(--muted);font-variant-numeric:tabular-nums}.axes{display:flex;justify-content:space-between;align-items:start;gap:8px;margin-top:6px}.axes span:nth-child(2){text-align:center;flex:1;min-width:0}.legend{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:8px;font-size:15px}.legend span{display:inline-flex;align-items:center;gap:7px}.legend i{display:inline-block;width:22px;flex-shrink:0}#metrics{border-top:1px solid var(--rule);padding-top:12px;display:grid;gap:8px}#metrics>div{display:flex;justify-content:space-between;gap:16px;font-size:15px;line-height:1.5}#metrics strong{font-weight:600;font-variant-numeric:tabular-nums;flex-shrink:0}#status{font-size:14px;color:var(--muted);min-height:3em;margin:14px 0 0}button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--accent);outline-offset:3px}@media(max-width:420px){.chart svg{height:145px}#metrics>div{flex-wrap:wrap;gap:3px 12px}}\n.job{margin:16px 0}.job-title{font-size:16px;font-weight:600}.job-times{display:flex;flex-wrap:wrap;gap:4px 16px;font-size:14px;color:var(--muted);line-height:1.6}.job svg{display:block;width:100%;height:26px;margin:6px 0}.timeline-key{display:flex;flex-wrap:wrap;gap:12px;font-size:15px;line-height:1.5}.timeline-key span{border-left:12px solid var(--accent);padding-left:6px}.timeline-key span:first-child{border-color:var(--muted)}\n.presets{display:flex;flex-wrap:wrap;grid-column:1/-1;gap:8px}.presets button{font:inherit;line-height:1.5;min-height:44px;max-width:100%;padding:8px 12px;border:1px solid var(--rule);border-radius:6px;color:var(--ink);background:var(--surface);text-align:left;white-space:normal}.presets button[aria-pressed=\"true\"]{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}\n","js":"const MODEL_ID=8, DEFAULTS=[120, 5];\n/* Independent teaching models. Seconds, jobs, and jobs/second unless stated.\n Pure functions: no DOM, clock, timers or external packages. */\nconst mean=a=>a.reduce((s,x)=>s+x,0)/a.length;\nconst curve=(name,points)=>({name,points});\nconst chart=(title,xlabel,curves,extra={})=>({title,xlabel,curves,...extra});\nfunction fifo(arrivals,services){let end=0;return arrivals.map((a,i)=>{const b=Math.max(a,end);end=b+services[i];return {id:i+1,a,b,d:end,s:services[i],w:b-a,t:end-a};});}\nfunction stepCounts(jobs,end,key){const events=jobs.map(j=>j[key]).filter(t=>t<=end).sort((a,b)=>a-b),p=[[0,0]];let n=0;for(const t of events)p.push([t,n],[t,++n]);p.push([end,n]);return p;}\nfunction occupancy(jobs,end){const ev=jobs.flatMap(j=>[[j.a,1],[j.d,-1]]).filter(e=>e[0]<=end).sort((a,b)=>a[0]-b[0]||a[1]-b[1]);let n=0;const p=[[0,0]];for(const [t,v]of ev){p.push([t,n],[t,n+v]);n+=v;}p.push([end,n]);return p;}\nfunction erlangC(c,rho){const a=c*rho;let term=1,sum=1;for(let n=1;n<c;n++){term*=a/n;sum+=term;}const tail=term*a/c/(1-rho);return tail/(sum+tail);}\nfunction finiteQueue(lambda,mu,K){const rho=lambda/mu;const w=Array.from({length:K+1},(_,n)=>rho**n),z=w.reduce((s,x)=>s+x,0),prob=w.map(x=>x/z),block=prob[K],through=lambda*(1-block),L=prob.reduce((s,x,n)=>s+n*x,0);return {prob,block,through,L,W:L/through,busy:1-prob[0]};}\nfunction seeded(seed){let s=seed>>>0;return()=>{s=(Math.imul(1664525,s)+1013904223)>>>0;return (s+.5)/4294967296;};}\nfunction simulateMM1(rho,count,seed){const random=seeded(seed),arr=[],srv=[];let t=0;for(let i=0;i<count;i++){t+=-Math.log(random())/(100*rho);arr.push(t);srv.push(-Math.log(random())/100);}return fifo(arr,srv);}\nfunction queueModel(id,p){let plots=[],metrics={},rows;\n if(id===1){const [gap,long]=p,jobs=fifo([0,gap,2*gap,3*gap],[long,1,1,1]),end=jobs.at(-1).d;\n rows=jobs;plots=[chart('cumulative','timeAxis',[curve('arrivals',stepCounts(jobs,end,'a')),curve('starts',stepCounts(jobs,end,'b')),curve('departures',stepCounts(jobs,end,'d'))])];metrics={meanWait:mean(jobs.map(j=>j.w)),meanTime:mean(jobs.map(j=>j.t)),lastDeparture:end};\n }else if(id===2){const [end]=p,jobs=fifo([0,1,2,6],[3,1,2,1]),overlap=j=>Math.max(0,Math.min(j.d,end)-j.a),area=jobs.reduce((s,j)=>s+overlap(j),0),done=jobs.filter(j=>j.d<=end),completedArea=done.reduce((s,j)=>s+j.t,0);\n plots=[chart('occupancy','timeAxis',[curve('inSystem',occupancy(jobs,end))],{yrange:[0,4]})];metrics={area,meanN:area/end,completedProduct:completedArea/end,boundaryArea:area-completedArea,completed:done.length};\n }else if(id===3){const [rate,window]=p,m=rate*window,prob=[];let q=Math.exp(-m);for(let n=0;n<=Math.ceil(m+7*Math.sqrt(m)+5);n++){if(n)q*=m/n;prob.push([n,q]);}\n plots=[chart('countProbability','countAxis',[curve('poisson',prob)],{bars:true,yrange:[0,Math.max(...prob.map(x=>x[1]))*1.12]}),chart('gapSurvival','timeAxis',[curve('exponential',Array.from({length:101},(_,i)=>{const t=5/rate*i/100;return [t,Math.exp(-rate*t)];}))],{yrange:[0,1]})];metrics={countMean:m,countVariance:m,emptyProbability:Math.exp(-m),meanGap:1/rate};\n }else if(id===4){const [rho]=p,delta=100*(1-rho),q99=Math.log(100)/delta;\n plots=[chart('tailProbability','timeAxis',[curve('systemTail',Array.from({length:101},(_,i)=>{const t=q99*1.2*i/100;return [t,Math.exp(-delta*t)];})),curve('waitingTail',Array.from({length:101},(_,i)=>{const t=q99*1.2*i/100;return [t,rho*Math.exp(-delta*t)];}))],{yrange:[0,1]}),chart('latency','rhoAxis',[curve('meanTime',Array.from({length:95},(_,i)=>{const r=(i+1)/100;return [r,1/(100*(1-r))];})),curve('p99',Array.from({length:95},(_,i)=>{const r=(i+1)/100;return [r,Math.log(100)/(100*(1-r))];}))],{event:rho})];metrics={utilization:rho,meanTime:1/delta,meanWait:rho/delta,p99:q99,zeroWait:1-rho,meanN:rho/(1-rho)};\n }else if(id===5){const [ca2,cs2]=p,points=f=>Array.from({length:95},(_,i)=>{const r=(i+1)/100;return[r,f(r)];}),pk=r=>r/(1-r)*(1+cs2)/2*.01,king=r=>r/(1-r)*(ca2+cs2)/2*.01;\n plots=[chart('waitingSeconds','rhoAxis',[curve('poissonReference',points(pk)),curve('kingman',points(king))],{event:.8})];metrics={pkWait:pk(.8),kingmanWait:king(.8),secondMoment:(1+cs2)*.0001,residualBusy:(1+cs2)*.005};\n }else if(id===6){const [c,rho]=p,C=erlangC(c,rho),wait=C/(c*100*(1-rho));\n const pts=f=>Array.from({length:95},(_,i)=>{const r=(i+1)/100;return[r,f(r)];});\n plots=[chart('waitingSeconds','rhoAxis',[curve('pooled',pts(r=>erlangC(c,r)/(c*100*(1-r)))),curve('split',pts(r=>r/(100*(1-r))))],{event:rho})];metrics={arrivalRate:c*100*rho,waitProbability:C,pooledWait:wait,splitWait:rho/(100*(1-rho)),meanTime:wait+.01};\n }else if(id===7){const [long,policy]=p,sizes=[long,1,1,1,1],order=policy?[1,2,3,4,0]:[0,1,2,3,4];let end=0;rows=order.map(i=>{const b=end;end+=sizes[i];return{id:i+1,a:0,b,d:end,s:sizes[i],w:b,t:end};});\n plots=[];metrics={meanWait:mean(rows.map(j=>j.w)),meanTime:mean(rows.map(j=>j.t)),longWait:rows.find(j=>j.id===1).w,maxSlowdown:Math.max(...rows.map(j=>j.t/j.s)),makespan:end};\n }else if(id===8){const [lambda,K]=p,q=finiteQueue(lambda,100,K);\n plots=[chart('stateProbability','countAxis',[curve('stationary',q.prob.map((v,n)=>[n,v]))],{bars:true})];metrics={offeredRatio:lambda/100,blocking:q.block,acceptedRate:q.through,meanN:q.L,meanTime:q.W,utilization:q.busy};\n }else if(id===9){const [lambda,feedback]=p,visit=1/(1-feedback),local=lambda*visit,stable=local<100,demands=[visit*.01,visit*.004];\n plots=[chart('utilizationChart','nodeAxis',[curve('nodeLoad',[[1,lambda*demands[0]],[2,lambda*demands[1]]])],{bars:true,yrange:[0,Math.max(1.2,lambda*demands[0]*1.1)]})];metrics={visits:visit,internalRate:local,bottleneckBound:100/visit,stable,networkTime:stable?visit*(1/(100-local)+1/(250-local)):null};\n }else if(id===10){const [rho,count,seed]=p,jobs=simulateMM1(rho,count,seed),warm=Math.floor(count/5),cohort=jobs.slice(warm),end=jobs.at(-1).a,done=cohort.filter(j=>j.d<=end),times=cohort.map(j=>j.t).sort((a,b)=>a-b),q99=times[Math.ceil(.99*times.length)-1],theory=1/(100*(1-rho)),max=Math.max(q99*1.2,theory*Math.log(100)*1.2);\n let ix=0;const ecdf=Array.from({length:151},(_,i)=>{const t=max*i/150;while(ix<times.length&×[ix]<=t)ix++;return[t,1-ix/times.length];});\n plots=[chart('tailProbability','timeAxis',[curve('empirical',ecdf),curve('stationaryTheory',Array.from({length:151},(_,i)=>{const t=max*i/150;return[t,Math.exp(-t/theory)];}))],{yrange:[0,1]})];metrics={sampleCount:times.length,censored:cohort.length-done.length,cohortMean:mean(times),completedMean:done.length?mean(done.map(j=>j.t)):null,theoryMean:theory,sampleP99:q99,theoryP99:theory*Math.log(100)};\n }else if(id===11){const [capacity,prob]=p,base=60,factor=1+prob+prob*prob,normal=base*factor,burst=120*factor,Qpeak=Math.max(0,burst-capacity)*30,drain=capacity>normal?Qpeak/(capacity-normal):Qpeak===0?0:null;\n const pts=Array.from({length:241},(_,i)=>{const t=i,rate=t<20||t>=50?normal:burst;let Q;if(t<20)Q=Math.max(0,normal-capacity)*t;else if(t<50)Q=Math.max(0,normal-capacity)*20+Math.max(0,burst-capacity)*(t-20);else Q=Math.max(0,Math.max(0,normal-capacity)*20+Qpeak+(normal-capacity)*(t-50));return [t,Q];});\n plots=[chart('backlog','timeAxis',[curve('queuedWork',pts)]),chart('rateChart','timeAxis',[curve('attemptRate',[[0,normal],[20,normal],[20,burst],[50,burst],[50,normal],[240,normal]]),curve('capacity',[[0,capacity],[240,capacity]])])];metrics={attemptFactor:factor,normalAttempts:normal,burstBacklog:pts[50][1],canDrain:capacity>normal,drainSeconds:capacity>normal?pts[50][1]/(capacity-normal):null,endBacklog:pts.at(-1)[1]};\n }\n return {plots,metrics,...(rows?{rows}:{})};\n}\nif(typeof module!=='undefined')module.exports={queueModel,fifo,erlangC,finiteQueue,simulateMM1};\n\nconst $=s=>document.querySelector(s), colors=['var(--accent)','#b77818','#269282'],dash=['','7 4','2 4'];\nconst number=x=>Number.isFinite(x)?viz.number(x,Number.isInteger(x)?0:Math.abs(x)<.01&&x!==0?5:3):'—';\nfunction plot(parent,spec){\n const all=spec.curves.flatMap(s=>s.points),xx=all.map(p=>p[0]),yy=all.map(p=>p[1]);\n const zero=true;\n let lo=zero?Math.min(0,...yy):Math.min(...yy),hi=zero?Math.max(0,...yy):Math.max(...yy),xmin=Math.min(...xx),xmax=Math.max(...xx);\n if(hi===lo)hi=lo+1;if(xmin===xmax)xmax=xmin+1;\n const pad=(hi-lo)*.08;lo=Math.max(0,lo-pad);hi+=pad;\n if(spec.bars){xmin-=.5;xmax+=.5;}if(spec.xrange)[xmin,xmax]=spec.xrange;if(spec.yrange)[lo,hi]=spec.yrange;\n const sx=x=>8+(x-xmin)/(xmax-xmin)*584,sy=y=>8+(hi-y)/(hi-lo)*144;\n const section=document.createElement('section');section.className='chart';\n const title=document.createElement('h3');title.textContent=viz.t(spec.title);section.append(title);\n const scale=document.createElement('div');scale.className='scale';scale.textContent=viz.t('vertical')+' '+number(lo)+' … '+number(hi);section.append(scale);\n const ns='http://www.w3.org/2000/svg',svg=document.createElementNS(ns,'svg');svg.setAttribute('viewBox','0 0 600 160');svg.setAttribute('preserveAspectRatio','none');svg.setAttribute('role','img');svg.setAttribute('aria-label',viz.t(spec.title));\n function node(name,attrs){const n=document.createElementNS(ns,name);for(const [k,v]of Object.entries(attrs))n.setAttribute(k,v);svg.append(n);return n;}\n for(let j=0;j<=4;j++)node('line',{x1:8,x2:592,y1:8+j*36,y2:8+j*36,stroke:'var(--rule)','stroke-width':1});\n if(lo<=0&&hi>=0)node('line',{x1:8,x2:592,y1:sy(0),y2:sy(0),stroke:'var(--muted)','stroke-width':1});\n if(spec.scatter)node('line',{x1:sx(0),x2:sx(0),y1:8,y2:152,stroke:'var(--muted)','stroke-width':1});\n if(spec.event!==undefined&&spec.event>=xmin&&spec.event<=xmax)node('line',{x1:sx(spec.event),x2:sx(spec.event),y1:8,y2:152,stroke:'var(--muted)','stroke-dasharray':'3 3'});\n spec.curves.forEach((s,i)=>{if(spec.bars){for(const [x,y]of s.points)node('line',{x1:sx(x),x2:sx(x),y1:sy(0),y2:sy(y),stroke:colors[i%3],'stroke-width':Math.min(16,400/s.points.length)});}else if(spec.scatter||spec.dots===i){for(const [x,y] of s.points)node('circle',{cx:sx(x),cy:sy(y),r:spec.scatter?5:2.8,fill:colors[i%3]});}else{let pts=s.points;if(spec.title==='replicas')pts=pts.flatMap((p,j)=>j?[[p[0],pts[j-1][1]],p]:[p]);node('path',{d:pts.map(([x,y],j)=>(j?'L':'M')+sx(x).toFixed(2)+' '+sy(y).toFixed(2)).join(' '),fill:'none',stroke:colors[i%3],'stroke-width':2,'stroke-dasharray':dash[i%3],'vector-effect':'non-scaling-stroke'});}});\n const area=document.createElement('div');area.className='plotarea';const ticks=document.createElement('div');ticks.className='yticks';for(const v of [hi,(hi+lo)/2,lo]){const label=document.createElement('span');label.textContent=number(v);ticks.append(label);}area.append(ticks,svg);section.append(area);\n const axes=document.createElement('div');axes.className='axes';const fmt=x=>spec.logx?number(10**x):number(x);\n for(const text of [fmt(spec.bars?xmin+.5:xmin),viz.t(spec.xlabel),fmt(spec.bars?xmax-.5:xmax)]){const a=document.createElement('span');a.textContent=text;axes.append(a);}section.append(axes);\n const legend=document.createElement('div');legend.className='legend';spec.curves.forEach((s,i)=>{const el=document.createElement('span'),sw=document.createElement('i');sw.style.borderTop=`3px ${i===0?'solid':i===1?'dashed':'dotted'} ${colors[i%3]}`;el.append(sw,document.createTextNode(viz.t(s.name)));legend.append(el);});section.append(legend);parent.append(section);\n}\nfunction timeline(rows){\n const host=$('#timeline');host.replaceChildren();if(!rows.length)return;\n const key=document.createElement('div');key.className='timeline-key';for(const k of ['waiting','service']){const e=document.createElement('span');e.textContent=viz.t(k);key.append(e);}host.append(key);\n const max=Math.max(...rows.map(r=>r.d)),ns='http://www.w3.org/2000/svg';\n for(const r of rows){const box=document.createElement('div');box.className='job';const title=document.createElement('div');title.className='job-title';title.textContent=viz.t('job')+' '+r.id;box.append(title);\n const svg=document.createElementNS(ns,'svg');svg.setAttribute('viewBox','0 0 600 26');svg.setAttribute('preserveAspectRatio','none');svg.setAttribute('role','img');svg.setAttribute('aria-label',viz.t('waiting')+' '+number(r.w)+' s; '+viz.t('service')+' '+number(r.s)+' s');\n for(const [a,b,color]of [[r.a,r.b,'var(--muted)'],[r.b,r.d,'var(--accent)']]){const rect=document.createElementNS(ns,'rect');rect.setAttribute('x',a/max*600);rect.setAttribute('y','3');rect.setAttribute('width',(b-a)/max*600);rect.setAttribute('height','20');rect.setAttribute('fill',color);svg.append(rect);}box.append(svg);\n const text=document.createElement('div');text.className='job-times';for(const [k,val]of [['arrivalTime',r.a],['startTime',r.b],['endTime',r.d]]){const e=document.createElement('span');e.textContent=viz.t(k)+' '+number(val)+' s';text.append(e);}box.append(text);host.append(box);\n }const axis=document.createElement('div');axis.className='axes';for(const s of ['0',viz.t('timeAxis'),number(max)]){const e=document.createElement('span');e.textContent=s;axis.append(e);}host.append(axis);\n}\n\nfunction update(){\n const values=[...document.querySelectorAll('[data-parameter]')].map(e=>+(e.matches('.presets')?e.dataset.value:e.value));\n document.querySelectorAll('input[data-parameter]').forEach(e=>$('#value-'+e.dataset.parameter).textContent=number(+e.value));\n document.querySelectorAll('[data-choice]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.choice===b.parentElement.dataset.value)));\n const r=queueModel(MODEL_ID,values);document.body.dataset.result=JSON.stringify(r.metrics);document.body.dataset.parameters=JSON.stringify(values);\n timeline(r.rows||[]);$('#plots').replaceChildren();r.plots.forEach(s=>plot($('#plots'),s));$('#metrics').replaceChildren();\n for(const [k,v]of Object.entries(r.metrics)){const line=document.createElement('div'),label=document.createElement('span'),value=document.createElement('strong');label.textContent=viz.t(k);value.textContent=typeof v==='boolean'?viz.t(v?'yes':'no'):number(v);line.append(label,value);$('#metrics').append(line);}\n $('#status').textContent=viz.t('updated');\n}\ndocument.querySelectorAll('input[data-parameter]').forEach(e=>e.addEventListener('input',update));\ndocument.querySelectorAll('[data-choice]').forEach(b=>b.addEventListener('click',()=>{b.parentElement.dataset.value=b.dataset.choice;update();}));\n$('#reset').addEventListener('click',()=>{document.querySelectorAll('[data-parameter]').forEach((e,i)=>{if(e.matches('.presets'))e.dataset.value=DEFAULTS[i];else e.value=DEFAULTS[i];});update();});update();\n","audio":false,"strings":{"acceptedRate":"Accepted and completed rate / s⁻¹","blocking":"Arrival rejection probability","countAxis":"Job count n","limit":"Total capacity K (includes service)","meanN":"Mean jobs in system L","meanTime":"Mean system time / s","no":"No","offeredRatio":"Offered load λ/μ","rate":"Arrival rate λ / s⁻¹","reset":"Reset experiment","scope":"M/M/1/K with μ=100/s. K includes the job in service; full arrivals are rejected without retries. The finite chain has a stationary distribution, but admission, rejection and delay jointly determine usefulness.","stateProbability":"Stationary probability πₙ","stationary":"Finite system","updated":"Recalculated; — means not applicable under these conditions.","utilization":"Busy fraction","vertical":"Vertical range","yes":"Yes"}}
At the defaults λ=120/s, μ=100/s and K=5, rejection is about 25.1%, accepted throughput 89.9/s and mean residence 33.6 ms. Larger K can reduce rejection and keep the server busier while increasing admitted-job waiting. The model has no timeout, service failure or retry: every admitted job eventually completes. Production successful throughput needs separate accounting.
Admission, backpressure and timeouts change different things
Admission rejects or defers work before consuming protected resources. Backpressure communicates downstream limits upstream, potentially moving the wait to a client or another queue. A timeout limits how long the caller waits. None substitutes for the others.
A client timeout need not stop server work. Retrying can create concurrent copies. Propagate deadlines, check remaining budgets while queued and at dispatch, and provide cancellation or deduplication that the operation can actually honor. These mechanisms are absent from M/M/1/K, so its mean does not predict deadline success.
A 20 ms waiting budget and mean throughput 1000/s do not imply K=20 guarantees a p99 target. Work ahead, residual service, parallelism and variability matter. Reliable bounds on individual job cost can support conservative waiting bounds; otherwise use distributions or measurements.
Effective successful work is the objective
Distinguish offered, admitted, completed and useful successful throughput, often called goodput. Fast rejections can make an aggregate latency percentile look better. Report rejection rate beside successful-request latency and state how timeouts, cancellations and unfinished requests enter the SLO.
Check your understanding
With λ=μ and K=1, what are rejection and admitted mean residence?
Reasoning
π0=π1=1/2, so half of arrivals are rejected, accepted throughput is λ/2 and L=1/2. Residence is 1/μ. There is no waiting slot: K=1 is not one waiting position plus one service position.
Does changing a timeout from 1 s to 100 ms necessarily reduce server work?
Reasoning
No. Without canceling original work, more frequent retries can increase it. Observe attempts, completions, cancellations, duplicates and resource occupancy rather than only shorter client waits.
Further reading
See the MIT lecture for finite-capacity accounting, Google SRE for admission and rejection costs, and message semantics for duplicate delivery and business identity.