Skip to content

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.

LugarInícioPapel
VagasVAGAS fichasuma 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} * 100

P{ } 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:

VagasTempo cheioVazãoNo sistema
133,4%0,0670,33
214,3%0,0860,57
36,7%0,0930,73
51,6%0,0980,91
100,1%0,1001,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çãoFichas em VagasConsome aoDevolve ao
Sala de esperacadeirasentrarsair do sistema
Pool de conexõesconexõesabrir conexãofechar conexão
Buffer de mensagensposiçõespublicarconsumir
Estacionamentovagaschegarir 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.