Session State — Client × Server × Database

TL;DR

HTTP não lembra de você entre uma requisição e outra, mas a conversa dura várias — um carrinho, um wizard, uma edição longa. Esse estado precisa morar em algum lugar, e há exatamente três: no cliente (cookie, campo oculto, token), no servidor (memória do processo) ou no banco (tabela ou store dedicado). Em 2002, Fowler trata o cliente como a opção cheia de ressalvas e o servidor como a natural. A nuvem inverteu isso. Autoescala e serverless mataram a sessão pegajosa; a assinatura criptográfica resolveu a objeção de segurança; e o resultado é que hoje o default é cliente (JWT) ou banco (Redis) — as duas opções que o livro tratava como exceção.

O segundo servidor que quebrou o sistema

O sistema está lento, e a solução é óbvia: subir um segundo servidor atrás de um balanceador. Fazem isso numa sexta.

Na segunda começam os chamados. Usuários perdem o carrinho no meio da compra. Um wizard de cadastro pede login de novo na terceira tela. As reclamações são intermitentes e ninguém reproduz — porque funcionam quando a requisição cai na mesma máquina, e falham quando cai na outra.

A causa é que o estado da conversa vive na memória do processo de um dos dois servidores. A primeira reação é ativar sessão pegajosa (sticky session) no balanceador, amarrando cada usuário a uma máquina. Isso apaga o incêndio — e cria o problema estrutural: agora nenhum servidor pode ser derrubado sem descartar as sessões de quem estava nele. Adeus deploy sem interrupção, adeus autoescala, adeus contêineres efêmeros.

Essa é a razão pela qual esta nota importa mais do que parece. Numa migração de legado para nuvem, onde a sessão mora costuma ser o bloqueio principal — e é quase sempre descoberto tarde.

Os três lugares


graph TD
    R["Requisição"] --> Q{"Onde mora<br/>o estado da conversa?"}
    Q -->|"Client"| C["No próprio cliente<br/>cookie · campo oculto · token<br/>viaja em toda requisição"]
    Q -->|"Server"| S["Na memória do processo<br/>HttpSession<br/>exige afinidade ou replicação"]
    Q -->|"Database"| D["Fora do processo<br/>tabela · Redis · DynamoDB<br/>qualquer servidor atende"]

    style C fill:#4A90D9,color:#fff
    style S fill:#F5A623,color:#000
    style D fill:#4A90D9,color:#fff

Client Session State — o estado viaja junto de cada requisição. Nenhum servidor precisa lembrar de nada, o que torna o servidor genuinamente sem estado: qualquer instância atende qualquer requisição. Os custos são o tamanho (tudo trafega toda vez), a confiança (o cliente pode adulterar) e a revogação (o servidor não tem o que apagar para encerrar a sessão).

Server Session State — o estado fica na memória do processo, referenciado por um identificador no cookie. É o mais simples de programar e o que Fowler descreve como default. O custo é o do incidente acima: o processo vira parte do estado do sistema.

Database Session State — o estado sai do processo e vai para um armazenamento compartilhado. Qualquer servidor atende qualquer requisição, e a sessão sobrevive à queda de um deles. O custo é uma leitura e uma escrita por requisição, e mais uma peça de infraestrutura.

ClientServerDatabase
Servidor sem estadosimnãosim
Sobrevive à queda do servidorsimnãosim
Custo por requisiçãotráfego (paga sempre)nenhumida ao store
Tamanho do estadolimitado (KB)livrelivre
Adulteração pelo clientepossível → exige assinaturaimpossívelimpossível
Revogação imediatadifíciltrivialtrivial
Veredito de 2002opção com ressalvasnaturalpesada
Prática de 2026default (JWT)evitadadefault (Redis)

Como a era encarnava

Server Session State era o caminho pavimentado: HttpSession no Java, Session no ASP, $_SESSION no PHP. Escrever session.setAttribute("carrinho", carrinho) era a coisa mais natural do mundo — e é por isso que tantos sistemas daquela geração são difíceis de escalar horizontalmente. Quando o único servidor deixava de bastar, havia duas saídas, ambas ruins: sessão pegajosa no balanceador, ou replicação de sessão entre os servidores do cluster (o mecanismo do WebLogic, WebSphere e Tomcat), que funcionava até o cluster crescer e o tráfego de replicação virar o gargalo.

Client Session State aparecia em doses pequenas e bem delimitadas: campos ocultos em formulários multi-etapa, dados de identificação no cookie. O ViewState do ASP.NET WebForms foi a versão mais ambiciosa — serializava o estado da tela inteira num campo oculto — e virou o exemplo canônico do custo do padrão, com páginas carregando centenas de KB de estado opaco.

Database Session State era a escolha dos sistemas que precisavam mesmo escalar ou sobreviver a falhas — bancos, e-commerces grandes. A TB_SESSAO do sistema legado da nota 01 é isso: parecia estranha, e era a decisão que permitia rodar em vários servidores.

A ressurreição

O JWT é Client Session State — e a inversão mais limpa desta família. Um token assinado, guardado no cliente, enviado a cada requisição, contendo a identidade e o que mais couber. É exatamente o padrão que Fowler cercou de ressalvas, hoje adotado como default. Estatuto: correspondência reconhecida.

O que mudou foram as três objeções, uma a uma. Adulteração deixou de ser problema: a assinatura é verificada pelo servidor e as bibliotecas são maduras. Tráfego deixou de doer no mesmo grau — TLS é barato, banda é abundante, e um token de alguns KB é ruído perto de uma imagem. E, sobretudo, a premissa arquitetural se inverteu: em 2002 o servidor lembrar era grátis; com autoescala, serverless e contêineres efêmeros, lembrar virou o caro. O Client Session State ganhou porque tornou o servidor sem estado, que passou a ser o requisito.

A objeção que sobreviveu é a revogação, e ela é séria. Um token assinado vale até expirar; não há o que apagar para encerrar a sessão de imediato. A prática — access token de vida curta mais refresh token revogável, ou uma lista de negação consultada no servidor — resolve na prática à custa de reintroduzir estado do lado do servidor, o que é uma ironia honesta de reconhecer.

O Database Session State virou o session store. Redis, Memcached, DynamoDB: exatamente o padrão, com o armazenamento otimizado para o caso (chave-valor em memória, expiração nativa). É a resposta padrão quando se quer revogação imediata e servidor sem estado. Reconhecida.

O Server Session State voltou pela porta dos fundos. Durable Objects da Cloudflare, Durable Entities do Azure, sistemas de atores: estado em memória com identidade endereçável, roteado pela infraestrutura para a instância certa e persistido de forma transparente. É a virtude do Server Session State (estado na memória, sem ida ao store) sem o defeito (morrer com o processo). Estatuto: leitura deste catálogo.

Fronteira com o galho de Auth

Esta nota trata do padrão de onde o estado da conversa mora — a decisão de arquitetura de aplicação. As consequências de segurança e identidade (armazenar token em cookie ou localStorage, HttpOnly/SameSite, rotação de refresh token, revogação, o que nunca colocar dentro de um JWT) têm casa própria e muito mais profunda em Sessões e cookies e JWT e a família de tokens. Aqui é o padrão; lá é a prática de segurança.

Armadilhas comuns

Sessão em memória atrás de balanceador

O que acontece: o segundo servidor entra em produção e usuários passam a perder estado de forma intermitente e irreproduzível. Por quê: o estado vive no processo, e o balanceador não sabe disso. A correção rápida — sessão pegajosa — amarra o usuário à máquina e, com isso, impede deploy sem interrupção e autoescala. Como evitar: trate sessão pegajosa como dívida declarada, não como solução. Antes de escalar horizontalmente, mova o estado para fora do processo — store compartilhado ou cliente.

JWT gordo

O que acontece: o token acumula perfil, permissões e preferências, chega a vários KB e passa a trafegar em toda requisição, inclusive nas de recursos estáticos. Alguns servidores rejeitam por tamanho de cabeçalho. Por quê: como o token já vai junto, colocar mais um campo parece grátis. O custo é distribuído por todas as requisições e nunca aparece num ponto só. Como evitar: no token, o mínimo para identificar e autorizar; o resto vem do banco quando for preciso. E lembre que dado no token fica congelado até a expiração — permissão revogada continua valendo.

Confundir sessão com cache

O que acontece: resultados de consulta pesada são guardados na sessão “para não recalcular”. A sessão incha, a memória do servidor ou do store estoura, e usuários veem dados velhos porque nada invalida aquilo. Por quê: os dois são “guardar para depois”, e a sessão é o lugar mais fácil de escrever. Como evitar: sessão é estado da conversa daquele usuário, com ciclo de vida ligado a ele. Cache é cópia de algo derivável, com invalidação própria e compartilhável entre usuários. Se o dado pode ser recalculado e serve a mais de um usuário, é cache.

Como explicar em inglês

“HTTP doesn’t remember you between requests, but the conversation does — a cart, a wizard, a long edit. That state has to live somewhere, and there are exactly three options: on the client, in server memory, or in a database. What I find fascinating is that the cloud inverted Fowler’s 2002 advice. He treats client session state as the option with the most caveats and server memory as the natural default. Today it’s the reverse: autoscaling and serverless made a stateful server the expensive thing, so JWT — which is client session state — and Redis session stores — which is database session state — became the defaults. The three original objections mostly dissolved: signing handles tampering, TLS and bandwidth handle the size, and cheap storage handles the rest. The one that survived is revocation: a signed token is valid until it expires, and you can’t delete it. That’s why you end up with short-lived access tokens plus a revocable refresh token — which quietly puts some server-side state back.”

PTEN
estado de sessãosession state
sessão pegajosa / afinidadesticky session / session affinity
servidor sem estadostateless server
replicação de sessãosession replication
revogaçãorevocation
lista de negaçãodenylist / blocklist
campo ocultohidden field
implantação sem interrupçãozero-downtime deployment

O que vem a seguir

Guardado o estado da conversa, aparece o problema seguinte: se a edição de um usuário dura várias requisições, dois usuários podem estar editando o mesmo dado ao mesmo tempo — e a transação de banco não ajuda, porque ela não pode ficar aberta durante o café de quem está preenchendo o formulário.

Veja também

Fontes

  • Martin FowlerPatterns of Enterprise Application Architecture (2002), Session State Patterns — as três variantes e a avaliação de cada uma no contexto da época.
  • Martin FowlerPoEAA — catálogo online — as fichas resumidas de Client, Server e Database Session State.
  • Martin FowlerPresentation Domain Data Layering — o enquadramento de camadas em que a sessão é estado de apresentação, não de domínio.