Tema
Limitando capacidade
Sistemas reais têm limite: a sala de espera tem 20 cadeiras, o buffer tem 500 posições, o pool tem 8 conexões. Modelar esse limite muda o resultado de forma importante — e é o que permite medir quanto trabalho o sistema recusa.
A ideia: uma ficha por vaga
Um lugar de capacidade guarda uma ficha para cada vaga disponível. Quem entra no sistema consome uma ficha; quem sai devolve.
Quando o lugar zera, não há vaga, e a entrada não acontece até alguém sair.
| Lugar | Início | Papel |
|---|---|---|
Vagas | VAGAS fichas | uma ficha por lugar livre |
Duas ligações fazem o mecanismo funcionar:
- a transição de entrada consome uma ficha de
Vagas - a transição de saída devolve a ficha para
Vagas
A regra que evita a maioria dos erros
A ficha de capacidade tem que ser devolvida quando o item sai de verdade, não antes. Se você devolver logo na entrada, o lugar nunca zera e o limite não existe na prática.
O modelo
A fila do primeiro modelo, agora com sala de espera limitada:
json
{
"modelName": "Fila com sala de espera limitada",
"definitions": [
{ "name": "TEMPO_ENTRE_CHEGADAS", "type": "DOUBLE", "value": "10" },
{ "name": "TEMPO_DE_ATENDIMENTO", "type": "DOUBLE", "value": "5" },
{ "name": "VAGAS", "type": "INTEGER", "value": "3" }
],
"places": [
{ "name": "Vagas", "initialMarking": 3, "stringMarking": "VAGAS", "initialStringMarking": "VAGAS" },
{ "name": "Fila", "initialMarking": 0, "stringMarking": "0", "initialStringMarking": "0" },
{ "name": "Atendente", "initialMarking": 1, "stringMarking": "1", "initialStringMarking": "1" },
{ "name": "EmAtendimento", "initialMarking": 0, "stringMarking": "0", "initialStringMarking": "0" }
],
"rewardMeasures": [
{ "name": "cheio_pct", "expression": "P{#Vagas = 0} * 100" },
{ "name": "vazao", "expression": "E{#EmAtendimento} / TEMPO_DE_ATENDIMENTO" },
{ "name": "no_sistema", "expression": "E{#Fila} + E{#EmAtendimento}" },
{ "name": "vagas_livres","expression": "E{#Vagas}" }
],
"transitions": [
{ "name": "Chegada", "type": "TIMED", "firingPolicy": "SINGLE_SERVER",
"raceType": "RACE_WITH_ENABLING_MEMORY", "priority": 1,
"distribution": { "type": "exponential", "parameters": { "Mean delay": "TEMPO_ENTRE_CHEGADAS" } },
"inputArcs": [{ "type": "INPUT", "place": "Vagas", "multiplicity": 1 }],
"outputArcs": [{ "type": "OUTPUT", "place": "Fila", "multiplicity": 1 }],
"inhibitorArcs": [] },
{ "name": "Inicio", "type": "IMMEDIATE", "firingPolicy": "SINGLE_SERVER",
"raceType": "RACE_WITH_ENABLING_MEMORY", "priority": 1, "weight": 1,
"inputArcs": [{ "type": "INPUT", "place": "Fila", "multiplicity": 1 },
{ "type": "INPUT", "place": "Atendente", "multiplicity": 1 }],
"outputArcs": [{ "type": "OUTPUT", "place": "EmAtendimento", "multiplicity": 1 }],
"inhibitorArcs": [] },
{ "name": "Fim", "type": "TIMED", "firingPolicy": "INFINITY_SERVER",
"raceType": "RACE_WITH_ENABLING_MEMORY", "priority": 1,
"distribution": { "type": "exponential", "parameters": { "Mean delay": "TEMPO_DE_ATENDIMENTO" } },
"inputArcs": [{ "type": "INPUT", "place": "EmAtendimento", "multiplicity": 1 }],
"outputArcs": [{ "type": "OUTPUT", "place": "Atendente", "multiplicity": 1 },
{ "type": "OUTPUT", "place": "Vagas", "multiplicity": 1 }],
"inhibitorArcs": [] }
]
}Repare que Fim devolve duas fichas: uma para Atendente (libera quem atendeu) e outra para Vagas (libera o lugar na sala). São coisas diferentes e ambas precisam voltar.
Medindo quanto o sistema recusa
Com o limite no modelo, aparece uma pergunta nova: com que frequência o sistema fica cheio?
P{#Vagas = 0} * 100P{ } mede a fração de tempo em que a condição vale, então isso é a porcentagem do tempo com o sistema lotado. Enquanto isso durar, quem chegar não entra.
O tamanho da sala muda tudo
Variando VAGAS no mesmo sistema — chegadas a cada 10 minutos, atendimento de 5:
| Vagas | Tempo cheio | Vazão | No sistema |
|---|---|---|---|
| 1 | 33,4% | 0,067 | 0,33 |
| 2 | 14,3% | 0,086 | 0,57 |
| 3 | 6,7% | 0,093 | 0,73 |
| 5 | 1,6% | 0,098 | 0,91 |
| 10 | 0,1% | 0,100 | 1,00 |
Três leituras importantes:
Capacidade pequena derruba a vazão. Com uma vaga só, o sistema conclui 0,067 por minuto em vez de 0,1 — perde um terço do trabalho, porque passa um terço do tempo recusando.
O ganho satura. De 1 para 3 vagas, a recusa cai de 33% para 6,7%. De 5 para 10, cai de 1,6% para 0,1%. Chega um ponto em que aumentar a sala não compra mais nada, porque ela já quase nunca enche.
Sem limite não existe recusa, mas existe espera. Com 10 vagas o comportamento já é praticamente o do modelo sem limite: vazão 0,1 e 1,0 no sistema. O trabalho não é recusado — mas alguém espera.
Limitar não é só proteção
Reduzir a capacidade parece defensivo, mas ela troca espera por recusa. Quem foi recusado não aparece no tempo médio de resposta, porque nem entrou. Um sistema com fila curta e ótimo tempo de resposta pode estar rejeitando um terço da demanda — sempre olhe as duas métricas juntas.
Onde mais isso aparece
O mesmo padrão serve para qualquer recurso limitado, não só sala de espera:
| Situação | Fichas em Vagas | Consome ao | Devolve ao |
|---|---|---|---|
| Sala de espera | cadeiras | entrar | sair do sistema |
| Pool de conexões | conexões | abrir conexão | fechar conexão |
| Buffer de mensagens | posições | publicar | consumir |
| Estacionamento | vagas | chegar | ir embora |
Cuidado com o paralelismo
Se o lugar de capacidade representa recursos que trabalham ao mesmo tempo — atendentes, servidores, threads —, a etapa de serviço precisa estar marcada como paralela, senão a capacidade não surte efeito. Veja Quantos em paralelo.
Já quando ele representa apenas espaço — cadeiras, posições de buffer —, esse cuidado não se aplica: espaço não processa nada, só segura.