Pular para o conteúdo principal

Database Session State

Descrição​

O Database Session State armazena os dados da sessão como dados confirmados (committed) no banco de dados. Em vez de manter o estado em memória ou no cliente, cada interação grava as informações da sessão em tabelas, normalmente associadas a um identificador de sessão ou de processo.

A diferença central em relação aos outros padrões é a distinção entre dados da sessão e dados de negócio efetivados. Fowler discute duas abordagens: gravar os dados pendentes em tabelas separadas de sessão, ou gravá-los nas tabelas reais marcados como pendentes (com um indicador de status), o que torna as consultas mais complexas, pois precisam filtrar dados pendentes.

Como o estado vive no banco, o servidor fica sem estado e qualquer instância pode atender a próxima requisição. Isso também permite que a sessão sobreviva a falhas e a longos períodos de inatividade, como em fluxos que duram dias.

Problemas que resolve​

  • Sessões longas (processos de aprovação, cadastros em várias etapas) que precisam sobreviver a reinícios e a dias de inatividade.
  • Estado de sessão que deve ser durável, auditável e recuperável após falhas.
  • Necessidade de retomar o trabalho em outro dispositivo ou outra instância.
  • Estado grande demais para o cliente e crítico demais para ficar só em memória.

Quando usar​

  • Quando a durabilidade da sessão é mais importante que a velocidade de acesso.
  • Quando o fluxo de trabalho é longo e pode ser retomado depois, inclusive por outra pessoa ou dispositivo.
  • Quando já existe um banco confiável e se quer evitar um componente adicional como o Redis.
  • Quando o estado pendente precisa participar de transações com os dados de negócio.

Quando evitar​

  • Quando o estado é pequeno, de curta duração e de alto volume de leitura; o banco vira gargalo.
  • Quando a latência de cada requisição precisa ser mínima.
  • Quando a limpeza de sessões abandonadas não pode ser tratada (exige rotinas de expiração).
  • Quando dados pendentes misturados às tabelas reais complicariam consultas e relatórios.

Trade-offs​

GanhoCusto / Risco
Durabilidade e recuperação após falhasLeituras e escritas no banco a cada requisição aumentam a latência
Servidor sem estado, instâncias intercambiáveisCarga extra no banco, que costuma ser o recurso mais difícil de escalar
Sessão retomável por dias, em qualquer dispositivoNecessidade de limpar sessões abandonadas
Estado participa de transações e é auditávelDados pendentes nas tabelas reais complicam consultas e regras
  • Consistência: estado e dados de negócio podem ser atualizados na mesma transação.
  • Concorrência: requisições simultâneas da mesma sessão exigem controle (por exemplo, lock otimista).
  • Operação: rotinas de expiração são parte do padrão, não um detalhe.

Diagrama​

O Client mantém apenas o sessionId. Qualquer servidor lê e grava o estado no Database, que é a fonte única e durável da sessão.

Exemplo​

Um cadastro de seguro com várias etapas é salvo a cada passo em uma tabela session_state, com expiração em 7 dias. O usuário pode voltar de outro dispositivo e continuar.

import json
import sqlite3

db = sqlite3.connect("app.db")
db.execute("""CREATE TABLE IF NOT EXISTS session_state (
session_id TEXT PRIMARY KEY,
data TEXT NOT NULL,
expires_at TEXT NOT NULL)""")

def save(session_id: str, data: dict) -> None:
with db:
db.execute(
"INSERT OR REPLACE INTO session_state VALUES (?, ?, datetime('now', '+7 days'))",
(session_id, json.dumps(data)))

def load(session_id: str) -> dict | None:
row = db.execute(
"SELECT data FROM session_state WHERE session_id = ? AND expires_at > datetime('now')",
(session_id,)).fetchone()
return json.loads(row[0]) if row else None

Frameworks como Django (django.contrib.sessions.backends.db) e Laravel (driver database) oferecem esse armazenamento.

Padrões relacionados​

Referência​