Pular para o conteúdo

Estado e ausência de estado

Iniciante12 min de leiturafundamentos

Quase tudo que é difícil em infraestrutura é difícil por causa do estado. Escalar um serviço sem estado é acrescentar réplicas; escalar um banco exige decidir sobre replicação, consistência e failover. Substituir um servidor sem estado é destruir e criar; substituir um com estado envolve migrar dados, e migração de dados envolve risco.

A habilidade central não é “evitar estado” — dado é o motivo de o sistema existir. É saber onde ele mora e manter essa fronteira nítida.

Estado é qualquer informação que precisa sobreviver a uma requisição. Um serviço é sem estado quando cada requisição carrega tudo que é necessário para ser processada, e o processo não guarda nada entre uma e outra.

Repare que isso não significa “não usa banco”. Um serviço que lê e escreve num banco continua sem estado — o estado está no banco, não no processo. Essa distinção é o que permite matar qualquer réplica a qualquer momento.

Se as réplicas são intercambiáveis:

  • O balanceador manda a requisição para qualquer uma.
  • O autoscaler cria e destrói réplicas sem coordenação.
  • Um deploy é substituir instâncias, e o rollback é substituir de volta.
  • Uma instância spot interrompida não perde nada.
  • Diagnóstico não depende de saber qual réplica atendeu.

Cada uma dessas propriedades desaparece quando o processo guarda algo que ninguém mais tem.

O padrão que funciona é concentrar o estado em poucos lugares projetados para isso — banco, cache distribuído, armazenamento de objetos, fila — e manter a camada de aplicação descartável.

  • Sessão vai para Redis ou para um token assinado, não para a memória do processo.
  • Upload vai para o armazenamento de objetos, não para o disco local do container.
  • Trabalho em andamento vai para uma fila com confirmação, não para uma thread.
  • Cache local é aceitável quando é apenas otimização: perder o cache pode custar latência, nunca correção.

O ganho não é estético. É que os sistemas com estado passam a ser poucos, conhecidos, e recebem o cuidado que merecem: backup testado, replicação, plano de recuperação.

A lista de coisas que parecem sem estado e não são:

  • Sessão em memória — funciona com uma réplica, quebra na segunda, e é o motivo mais comum de alguém ligar sessão fixa no balanceador.
  • Arquivo escrito no disco do container — some no próximo deploy, e some em silêncio.
  • Trabalho agendado dentro do processo — com três réplicas, roda três vezes.
  • Contador ou rate limiter em memória — o limite passa a ser N vezes maior que o configurado.
  • Cache de configuração carregado no boot — a réplica nova tem um valor, a antiga tem outro.
  • Conexão de longa duração (WebSocket, SSE) — a réplica não é mais intercambiável para aquele cliente.

O sintoma típico é o mesmo em todos: funciona em desenvolvimento com uma instância, e apresenta comportamento intermitente em produção com várias.

“Torne tudo sem estado” é conselho incompleto. Alguns sistemas são inerentemente stateful — banco, fila, sistema de arquivos — e fingir o contrário só empurra a complexidade para um lugar pior.

Além disso, ausência de estado tem custo: toda requisição paga uma ida ao armazenamento externo, o que adiciona latência e cria uma dependência a mais no caminho crítico. Cache local existe justamente para isso, e é uma forma legítima de estado — desde que perdê-lo seja apenas mais lento, nunca incorreto.

A pergunta útil, então, não é “isto tem estado?”, e sim: se esta instância morrer agora, o que se perde? Se a resposta for “nada”, você está bem. Se for “as sessões de quem estava logado”, você tem uma decisão consciente a tomar — e não uma surpresa esperando o próximo deploy.