容量計画では、平常時の遅延、突発滞留の回復時間、失敗と再試行の追加仕事を同時に考えます。平均到着が平均能力より小さいことは出発点にすぎません。
滞留を解消するのは差分の余力
決定論的な流体近似では、仕事を連続に分割できる量として扱います。総処理能力 C、流入仕事率 λ なら:
Q˙=λ−C(Q>0),Q˙=max(0,λ−C)(Q=0).
既存滞留 Q0、以後の一定到着 λ<C なら解消時間は Q0/(C−λ)。Q0/C は新規到着がない場合だけです。全滞留の消失時間と、現在の末尾にいる一要求のFCFS待ち時間は違います。後から来る要求は通常その要求の後ろに並びます。
平常60/s、能力100/s、120/sの突発が30 s続くと600件増えます。平常へ戻ると余力40/sなので追加15 sで解消。能力70/sなら同じ突発で1500件、余力10/sなので150 s必要です。
再試行が余力を消費する
最大 m 回、各試行が独立に固定確率 p で失敗し、失敗後だけ次を試すなら:
E[A]=1+p+⋯+pm−1=1−p1−pm(p=1).
1/(1−p) は無限試行の場合だけです。現実の障害は相関し、失敗率も負荷で変わるので、固定 p は仕事量近似です。再試行の遅延は到着の時間構造を変え、期待増幅率は実際の逐時試行列ではありません。
実験は Q(0)=0 から240 sまで観測します。滞留は試行仕事量で数え、到着率には期待再試行増幅を既に含めています。
図を準備しています
容量計画と過負荷からの回復 · 実験決定論的流体近似。元の到着は平常60/s、20–50 sは120/s。最大3試行、独立で一定の失敗確率pから1+p+p²倍の即時負荷とします。バックオフや拒否はなく、実際の再試行過程や確率的テールモデルではありません。
{"id":"queueing-11","title":"容量計画と過負荷からの回復 · 実験","summary":"決定論的流体近似。元の到着は平常60/s、20–50 sは120/s。最大3試行、独立で一定の失敗確率pから1+p+p²倍の即時負荷とします。バックオフや拒否はなく、実際の再試行過程や確率的テールモデルではありません。","height":1000,"html":"<p class=\"intro\" data-i18n=\"scope\"></p><div class=\"controls\"><div class=\"control\"><label for=\"parameter-0\" data-i18n=\"capacityControl\"></label><output id=\"value-0\" for=\"parameter-0\"></output><input type=\"range\" id=\"parameter-0\" data-parameter=\"0\" min=\"70\" max=\"200\" step=\"10\" value=\"100\"/></div><div class=\"control\"><label for=\"parameter-1\" data-i18n=\"retryProbability\"></label><output id=\"value-1\" for=\"parameter-1\"></output><input type=\"range\" id=\"parameter-1\" data-parameter=\"1\" min=\"0\" max=\"0.5\" step=\"0.05\" value=\"0\"/></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=11, DEFAULTS=[100, 0];\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":{"attemptFactor":"元の要求あたりの期待試行回数","attemptRate":"試行の到着率","backlog":"滞留 / 試行数","burstBacklog":"50 s時点の滞留 / 試行数","canDrain":"平常時に正の滞留解消余力がある","capacity":"処理能力","capacityControl":"処理能力 / 試行·s⁻¹","drainSeconds":"50 s以後の解消所要時間 / s","endBacklog":"240 sでの残り滞留 / 試行数","no":"いいえ","normalAttempts":"平常時の試行率 / s⁻¹","queuedWork":"未処理の仕事","rateChart":"流量 / 試行·s⁻¹","reset":"実験をリセット","retryProbability":"各試行の失敗確率 p(独立と仮定)","scope":"決定論的流体近似。元の到着は平常60/s、20–50 sは120/s。最大3試行、独立で一定の失敗確率pから1+p+p²倍の即時負荷とします。バックオフや拒否はなく、実際の再試行過程や確率的テールモデルではありません。","timeAxis":"時間 / s","updated":"再計算しました。— はこの条件では適用できない値です。","vertical":"縦軸の範囲","yes":"はい"}}
実験は最大3試行です。再試行なしでは20–50 sの突発を65 sに解消します。能力100/s、p=0.3 なら増幅1.39倍、平常83.4試行/s、突発滞留2004件で、突発終了から約120.72 s必要です。p=0.5 では平常105/sとなり余力がなく、突発後も滞留が増えます。
理論を容量レビューにする
- 業務単位と成功条件を定義します。元要求、試行、受付、成功、取消、期限切れを分け、速いエラーを有効仕事に数えません。
- 制約資源のサービス需要と、種別ごとの平均、変動、裾を測ります。CPU、接続、ロック、遠隔割当が別々の段を制限し得ます。
- 適切なモデルで定常待ちを見積もり、現実の需要に合う開放・閉鎖負荷試験で目標を確かめます。モデル誤差と測定不確実性も余力に含めます。
- 突発、下流低速化、インスタンス故障、再試行を再生し、回復中の年齢、拒否、成功遅延と解消時間を見ます。
- 受付、テナント隔離、古い仕事、再試行予算、増設の有効化遅延を定義します。能力が実際に増えるまで滞留は生じ続けます。
AWSの滞留記事は隔離と回復の経験を、Google SREは仕事の費用差と資源信号を扱います。すべてのシステムに使える70%や80%の利用率閾値は導けません。
制御理論とメッセージングへ
待ち行列理論は流量と待ちを、制御理論は観測から操作を変える方法を扱います。列の年齢、滞留、利用率を帰還へ使えますが、標本化、平滑化、増設遅延、飽和も回復に影響します。計算機システムの制御で、本章の仕事モデルを動的な判断へ接続できます。
メッセージには永続化、重複、分割、再生もあり、FIFO図だけでは代替できません。メッセージの意味論と Kafkaアーキテクチャへ進みます。発展には更新過程、優先度列、閉鎖網の平均値解析、ネットワーク計算、重交通極限、状態依存処理があります。本シリーズは待ち行列理論全体の網羅を主張しません。
確認問題
- 滞留10000件、能力1000/s、新規900/sなら、なぜ解消は10 sでなく100 sですか。
考え方
毎秒1000件完了して900件増えるので、正味100件しか減りません。10000/100=100 s。到着停止時だけ10 sです。能力、流量、費用が変われば仕事量を再積分します。
- 列を大きくするか消費者を増やせば、必ず過負荷を解消できますか。
考え方
どちらも無条件には保証しません。列の拡大は待つ場所を増やすだけで、消費者追加は本当の制約能力を増やす必要があります。下流が飽和していれば競合を強める場合もあります。資源と業務締切を特定し、故障・回復実験で確認します。