Capítulo 35 — Cache Distribuído
Este capítulo detalha a estratégia de cache distribuído da Plataforma de Relacionamento Digital com o Cidadão, descrevendo como diferentes tipos de dados são acelerados por cache distribuído, como invalidação é coordenad…
35.1 Objetivo do Capítulo
Este capítulo detalha a estratégia de cache distribuído da Plataforma de Relacionamento Digital com o Cidadão, descrevendo como diferentes tipos de dados são acelerados por cache distribuído, como invalidação é coordenada, como o cache é segregado por tenant, e como alta disponibilidade é mantida em face de falhas parciais.
O cache distribuído é uma das decisões arquiteturais com maior impacto em performance e custo operacional. Uma estratégia bem calibrada reduz a carga no banco de dados em 80% ou mais, reduz latência de resposta de centenas de milissegundos a poucos milissegundos, e permite que a plataforma escale horizontalmente sem pressão sobre o banco.
Uma estratégia mal calibrada — cache stale, cache stampede, dados quentes não cacheados, TTL mal dimensionado — degrada experiência do usuário, viola consistência, e pode até derrubar o banco em pico de carga.
35.2 Papel do Cache na Plataforma
O cache distribuído é uma camada intermediária entre os serviços de aplicação e o banco de dados. Sua função é responder consultas frequentes com latência baixa, sem invocar a fonte primária de dados.
Cliente / Serviço A
│
▼
┌──────┐ ┌─────────────┐ ┌────────┐
│ App │ ───▶ │ Cache Hit? │ ───▶ │ Backend│
└──────┘ └──────┬──────┘ └────┬───┘
│ Sim │
▼ ▼
Resposta Persistência
rápida em banco
│
▼
Fonte primária
consultada apenas
em cache miss
Três regras fundamentais:
-
Cache é otimização, não fonte de verdade — o banco de dados permanece como fonte canônica de cada informação. Cache serve para reduzir latência e carga.
-
Cache tem TTL e invalidação — entradas têm tempo de vida finito e são invalidadas ativamente quando a informação subjacente muda.
-
Falha de cache degrada de forma controlada — se o cache está indisponível, a aplicação continua funcionando consultando a fonte primária, mesmo com latência maior.
35.3 Tipos de Cache e Casos de Uso
A plataforma utiliza diferentes padrões de cache conforme a natureza do dado:
35.3.1 Cache de Identidade e Permissões
Dados de identidade, perfis e permissões são consultados em cada requisição para autorização. Cache reduz drasticamente a carga no IAM e acelera autorização:
Chave: platform:v1:tenant:{tenantId}:subject:{subjectId}:permissions
TTL: 5–15 minutos
Invalidação: evento `identity.subject-updated.v1`
Quando o IAM publica um evento de alteração de perfil, todos os caches de permissões afetados são invalidados.
35.3.2 Cache de Configuração de Tenant
Configurações que mudam raramente mas são lidas em cada requisição (políticas de senha, timeout de sessão, lista de canais habilitados):
Chave: platform:v1:tenant:{tenantId}:config:{config_key}
TTL: 10–30 minutos
Invalidação: evento `tenant.config-changed.v1`
35.3.3 Cache de Catálogo de Serviços
Serviços publicados no catálogo mudam com pouca frequência, mas são acessados constantemente:
Chave: platform:v1:tenant:{tenantId}:catalog:service:{serviceId}
TTL: 5–15 minutos
Invalidação: evento `catalog.service-published.v1`
35.3.4 Cache de Resposta de API
Resultados de queries de leitura frequente em endpoints REST são cacheados no servidor HTTP ou na camada de aplicação:
Chave: platform:v1:tenant:{tenantId}:endpoint:{path_hash}:params_hash
TTL: 1–5 minutos
Invalidação: TTL puro (sem invalidação ativa)
35.3.5 Cache de Sessão de Atendimento
Estado da sessão de um atendimento em andamento (atendente + cidadão + ticket):
Chave: platform:v1:tenant:{tenantId}:conversation:{conversationId}
TTL: 30 minutos
Invalidação: evento `conversation.closed.v1`
35.3.6 Cache de Permissões Calculadas
Resultado de cálculo composto de permissões (RBAC + ABAC + unidade organizacional) é cacheado:
Chave: platform:v1:tenant:{tenantId}:effective-permissions:{subjectId}:{resourceId}
TTL: 1–5 minutos
Invalidação: evento `subject.permission-changed.v1`
35.3.7 Cache de Modelo de IA e Predições
Resultados de inferência LLM e predições de modelos podem ser cacheados para queries idênticas ou semanticamente similares:
Chave: platform:v1:tenant:{tenantId}:llm-cache:{query_hash}
TTL: 24 horas (operações classificadas)
Invalidação: TTL puro (operações sazonais)
Cache de LLM reduz custo de inferência quando múltiplos cidadãos fazem perguntas similares.
35.3.8 Cache de Contadores e Agregados
Dashboards executam queries de agregação pesadas. Contadores pré-calculados são cacheados:
Chave: platform:v1:tenant:{tenantId}:dashboard:{dashboardId}:aggregated
TTL: 5 minutos
Invalidação: TTL puro + refresh assíncrono
35.3.9 Cache de Bloqueio Distribuído
Coordenação entre instâncias do mesmo serviço (ex.: reconciliação periódica por tenant):
Chave: platform:v1:lock:{lockName}:{resourceId}
TTL: 5 minutos (com renovação)
Invalidação: release explícito via owner token
35.4 Arquitetura Redis
35.4.1 Redis como Tecnologia de Cache
A plataforma utiliza Redis na versão 7 ou superior. A escolha é justificada por: latência submilissegundo, estruturas de dados versáteis (string, hash, set, sorted set, list, stream, pub/sub), replicação nativa, persistência opcional, suporte a cluster, e comunidade ativa.
Redis é utilizado como cache, coordenação (locks, rate limiting), e pub/sub para casos simples. Para filas complexas, RabbitMQ é utilizado (ver Capítulo 13).
35.4.2 Cluster Redis para Alta Disponibilidade
Redis é implantado em cluster (modo Cluster do Redis ou Redis Sentinel) com:
┌─────────────────────────────────────────┐
│ Redis Cluster │
│ ┌──────────┐ ┌──────────┐ ┌──────┐ │
│ │ Master 1 │ │ Master 2 │ │Master│ │
│ │ Replica │ │ Replica │ │ Repl │ │
│ └──────────┘ └──────────┘ └──────┘ │
│ (shard 1) (shard 2) (shard 3) │
└─────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
Cluster client com hash slot awareness
Sharding por hash slot permite que o cluster escale além do limite de memória de uma única instância. Replicação síncrona para réplicas garante failover automático em caso de falha de master.
35.4.3 Isolamento Multi-Tenant via Namespace
Toda chave de cache inclui o tenant na hierarquia:
platform:v1:tenant:{tenantId}:{domain}:{entityType}:{entityId}:{attribute}
O prefixo platform:v1:tenant:{tenantId} garante que:
- nenhum cache key entre tenants conflita — cada tenant tem seu próprio namespace;
- uma consulta de cache por tenant nunca retorna dados de outro — mesmo por erro de aplicação, a chave inclui tenant_id explícito;
- invalidação por tenant é simples — basta pattern matching com
platform:v1:tenant:{tenantId}:*.
35.4.4 Versionamento de Schema de Cache
O prefixo v1 no início permite evolução do schema de cache. Quando a estrutura de dados cacheada muda (ex.: inclusão de novo campo), incrementa-se para v2. A versão antiga permanece válida até expiração natural das chaves existentes — sem invalidação em massa.
35.4.5 Separação por Cluster de Cache
Caches com características diferentes podem ser isolados em clusters separados:
Redis Cluster Cache-Session (dados quentes, baixa latência)
Redis Cluster Cache-Catalog (dados semi-estáticos, alta vazão)
Redis Cluster Cache-Analytics (contadores e agregados, TTL longo)
Redis Cluster Lock-Distributed (locks distribuídos, baixa cardinalidade)
Separação evita que carga de uma aplicação afete performance de outra. Decisão registrada como ADR-503.
35.5 Política de TTL e Invalidação
35.5.1 TTL Explícito por Tipo de Dado
Cada tipo de dado tem TTL explícito conforme volatilidade:
| Tipo | TTL de Referência | Justificativa |
|---|---|---|
| Catálogo de serviços | 5–15 min | Publicação é evento raro |
| Configuração de tenant | 10–30 min | Mudanças raras |
| Permissões calculadas | 1–5 min | Mudanças moderadas |
| Sessão HTTP | 30 min | Sessão natural |
| Resultados de API | 1–5 min | Tolerância a staleness |
| Contadores dashboard | 5 min | Precisão próxima do real |
| Modelo IA inferido | 24 h | Similar queries |
TTL nunca é zero (cache eterno sem invalidação) nem extremamente longo sem justificativa. Jitter é aplicado ao TTL de chaves muito populares para evitar expiração sincronizada.
35.5.2 Invalidação Ativa por Evento
Quando dados subjacentes mudam, o produtor do evento publica um evento que dispara invalidação de caches afetados:
Workflow Service publica workflow.updated
│
▼
RabbitMQ routing: cache.invalidate.v1
│
▼
CacheInvalidationListener recebe evento
│
├─ Extrai tenant_id, entity_type, entity_id
├─ Calcula chaves a invalidar:
│ platform:v1:tenant:{tenantId}:{domain}:{entity_type}:{entity_id}:*
├─ DEL das chaves via pipeline
└─ Log: invalidação executada com {num_chaves} chaves removidas
35.5.3 TTL vs. Invalidação Ativa
| Estratégia | Quando Usar | Vantagem | Risco |
|---|---|---|---|
| TTL puro | Dados tolerantes a staleness, baixo risco de inconsistência | Simplicidade operacional | Dados obsoletos até expirar |
| Invalidação ativa | Dados sensíveis a freshness | Consistência imediata | Complexidade operacional |
| TTL + invalidação | Combinação | Defesa em profundidade | Custo de processamento |
A plataforma combina TTL (defesa em profundidade) com invalidação ativa (consistência imediata). Quando o evento de invalidação chega, a chave é removida imediatamente — antes do TTL expirar. O TTL serve como rede de segurança caso o evento falhe.
35.5.4 Invalidação por Tag
Em alguns cenários, é necessário invalidar todas as chaves relacionadas a uma tag (ex.: todas as chaves de um tenant quando há reset administrativo):
SET platform:v1:tenant:{tenantId}:tag:{tag_name} = "1"
EXPIRE platform:v1:tenant:{tenantId}:tag:{tag_name} 86400
-- Chave indexada por tag:
SET platform:v1:tenant:{tenantId}:data:{entityId} = "..."
EXPIRE platform:v1:tenant:{tenantId}:data:{entityId} 3600
SADD platform:v1:tenant:{tenantId}:tag:{tag_name}:members {entityId}
Para invalidar por tag, basta ler membros do set e deletar as chaves individuais.
35.5.5 Cache Refresh Pattern
Para dados críticos com alta disponibilidade, cache refresh em background é utilizado:
Cache Refresh Job (a cada 5 min)
│
├─ Identifica chaves quentes (top 100 acessadas)
│
├─ Recarrega da fonte primária antes da expiração
│
└─ Atualiza cache atomicamente (SET XX ou Lua script)
O cache nunca fica indisponível para o cliente durante refresh — chave antiga permanece válida até a nova ser escrita.
35.6 Prevenção de Cache Stampede
35.6.1 O Problema
Cache stampede ocorre quando uma chave muito acessada expira e múltiplas requisições consultam a fonte primária simultaneamente:
10.000 requests → mesmo cache miss → 10.000 queries ao banco
Em pico, isso derruba o banco.
35.6.2 Mitigação 1: Jitter no TTL
Adicionar variação aleatória ao TTL evita expiração sincronizada:
TTL base: 300 segundos
Jitter: ±60 segundos
TTL efetivo: 240 a 360 segundos
Chaves quentes não expiram todas no mesmo segundo.
35.6.3 Mitigação 2: Single-Flight
Apenas uma requisição consulta a fonte primária; outras aguardam:
GET cache:user:123
│
├─ Hit → retorna
│
└─ Miss
│
├─ SET cache:user:123:lock = "{owner_id}" NX EX 30
│
├─ Se lock adquirido:
│ └─ query ao banco
│ SET cache:user:123 = "{value}" EX 300
│ DEL cache:user:123:lock
│ retorna
│
└─ Se lock não adquirido:
├─ WAIT (50ms polling)
├─ GET cache:user:123
├─ Se hit → retorna
└─ Se miss → retry (timeout 1s, depois retorna stale ou erro)
Implementado com Redis Lua script para atomicidade.
35.6.4 Mitigação 3: Stale-While-Revalidate
Cache retorna valor anterior enquanto nova consulta ocorre em background:
GET cache:user:123
│
├─ Hit + valor fresco (TTL > 50% do tempo) → retorna
│
├─ Hit + valor próximo de expirar (TTL < 50%) → retorna valor stale + dispara refresh em background
│
└─ Miss → consulta banco + popula cache
Cliente não percebe inconsistência de curta duração. Banco recebe uma única query de refresh.
35.6.5 Mitigação 4: Probabilistic Early Expiration (XFetch)
Cada requisição tem probabilidade de disparar refresh antecipado, baseado em idade da chave:
delta = TTL atual
beta = 1 (constante)
delta * beta * (-log(rand()))
Se este valor calculado é maior que TTL, dispara refresh antes da expiração natural. Distribui refresh ao longo do tempo em vez de concentrar no momento da expiração.
35.7 Camada de Acesso ao Cache
35.7.1 Interface de Cache Abstraída
Aplicações acessam cache via interface que abstrai Redis:
public interface CacheService {
Optional<String> get(String key);
void set(String key, String value, Duration ttl);
void set(String key, Object value, Duration ttl); // serialização JSON
void delete(String key);
void deleteByPattern(String pattern);
boolean acquireLock(String lockKey, Duration ttl);
void releaseLock(String lockKey, String ownerToken);
}
Implementação com Lettuce (cliente Redis reativo assíncrono) ou Jedis (cliente síncrono) — escolha registrada como ADR-504.
35.7.2 Serialização Eficiente
Valores serializados em formato eficiente:
- Strings simples: UTF-8 nativo do Redis;
- Objetos complexos: JSON com Jackson (legibilidade, interoperabilidade);
- Dados binários (imagens, PDFs): binário nativo + compressão snappy/lz4 quando grande;
- Hashes de campos: HSET quando estrutura tem múltiplos campos atualizados independentemente.
35.7.3 Pipeline e Batch
Operações múltiplas são enviadas em pipeline para reduzir latência:
Pipeline:
GET cache:tenant:abc:permissions:user1
GET cache:tenant:abc:permissions:user2
GET cache:tenant:abc:permissions:user3
GET cache:tenant:abc:config:password-policy
Uma única ida-volta ao cluster Redis em vez de 4. Latência agregada cai drasticamente.
35.7.4 Connection Pool
Cliente Redis usa pool de conexões dimensionado conforme carga:
max-active: 50
max-idle: 20
min-idle: 5
max-wait: 100ms
Connection leak é monitorado (conexões abertas não retornam ao pool). Alerta em uso > 80% do pool.
35.8 Cache de Resposta HTTP
35.8.1 Cache-Control Headers
Endpoints REST retornam headers Cache-Control para instruir caches intermediários:
GET /api/v1/services/{id}
│
Cache-Control: private, max-age=300
ETag: "service-{id}-v{version}"
Last-Modified: {date}
Cliente (browser ou CDN) pode cachear por 5 minutos.
35.8.2 Validação com ETag
Cliente envia If-None-Match: "service-{id}-v{version}". Servidor retorna 304 Not Modified se versão não mudou — sem payload.
35.8.3 Cache de CDN
Páginas públicas do portal (catálogo sem personalização, ajuda, FAQ) podem ser servidas via CDN:
GET https://portal.example.com/help
│
CloudFlare/CDN cache (TTL 1h)
│
Origin: Backend
CDN reduz latência para usuários geograficamente distribuídos. Cache invalidation via purge quando conteúdo muda.
35.8.4 Não-Cache de Dados Sensíveis
Endpoints com dados pessoais, dinâmicos por tenant, ou alterados em mutação nunca são cacheados:
Cache-Control: private, no-store, no-cache, must-revalidate
Pragma: no-cache
Headers impedem cache em qualquer camada.
35.9 Monitoramento e Observabilidade de Cache
35.9.1 Métricas Críticas
A plataforma monitora:
cache.hits → requisições atendidas pelo cache
cache.misses → requisições que foram ao banco
cache.evictions → itens removidos por politique LRU
cache.size_bytes → bytes ocupados no cluster
cache.connections → conexões ativas ao Redis
cache.latency_ms → latência p50, p95, p99
cache.errors → falhas ao acessar cache
Taxa de hit rate esperada: 70–90% para dados bem cacheados. Hit rate < 50% indica cache misconfigured.
35.9.2 Alertas
Alert: cache.hit_rate < 50% for 10 minutes → possível cache misconfiguration
Alert: cache.evictions > 1000/min → memória insuficiente
Alert: cache.latency_p99 > 100ms → degradação de performance
Alert: redis.connections > 90% max → pool esgotado
Alert: redis.memory_used > 90% configured → risco de eviction agressiva
35.9.3 Logs de Invalidação
Cada invalidação é registrada:
{
"timestamp": "2026-07-15T10:30:45.123Z",
"event": "cache.invalidation.executed",
"tenant_id": "org-uuid",
"keys_pattern": "platform:v1:tenant:*:workflow:*",
"keys_count": 247,
"duration_ms": 18,
"source_event": "workflow.updated"
}
Observar ausência de invalidação — se evento chega mas invalidação não ocorre, há problema de consumer ou de conexão ao Redis.
35.9.4 Rastrear Cache Poisoning
Se um cliente recebe dados corrompidos ou inválidos do cache, rastreabilidade é crítica:
Cliente relata dados inválidos
│
├─ Procurar em logs: qual chave foi consultada?
│ GET cache:tenant:abc:user:123
│
├─ Verificar histórico do cache:
│ - Quando foi SET?
│ - De qual serviço veio o valor?
│ - Qual evento disparou invalidação?
│
└─ Investigar: bug no serializer? evento de invalidação não chegou?
Correlação com eventos de banco ajuda isolar causa raiz.
35.10 Política de Falha e Resiliência
35.10.1 Redis Indisponível: Fail-Open vs. Fail-Closed
Quando Redis está fora do ar, a decisão é aplicação-específica:
Fail-Open (permissivo): Cache indisponível → consulta banco direto.
try {
value = cacheService.get(key)
} catch (CacheException e) {
value = primaryService.fetch(key) // fallback
}
Aplicável a: dados não-críticos, leitura, comportamento defensivo.
Fail-Closed (restritivo): Cache indisponível → bloqueia operação.
try {
value = cacheService.get(key)
} catch (CacheException e) {
throw new ServiceUnavailableException("Cache unavailable")
}
Aplicável a: rate limiting (se cache falha, não confia em limites), distributed locks (se falha, não executa), operações financeiras (se falha, não processa).
35.10.2 Circuit Breaker no Acesso ao Cache
Padrão circuit breaker protege o backend:
Estado: CLOSED (operações normais)
│
├─ Contabilizar falhas ao acessar Redis
│
├─ Se falhas > threshold (ex: 5) em janela (ex: 10s)
│ └─ OPEN (falhar rápido, não tentar)
│
└─ Depois de timeout (ex: 30s)
└─ HALF_OPEN (testar uma requisição)
├─ Sucesso → CLOSED
└─ Falha → OPEN (esperar mais tempo)
Circuit breaker evita retry thundering quando cache está fora do ar.
35.10.3 Cache Replication e Failover
Redis replicado garante que cache sobrevive a falha de uma instância:
Master Replica1 Replica2
│ │ │
│─────────│─────────│ Replicação síncrona
│
Se Master falha:
Sentinel detecta → promove Replica1 a Master
│
Clientes redirecionados para novo Master
Failover automático é transparente para aplicação (cliente Redis reclui automaticamente).
35.10.4 Cache Warming em Startup
Ao iniciar ou ao restaurar cache, dados críticos são pré-carregados:
App startup
│
├─ Conectar ao Redis
│
├─ Verificar se cache está vazio ou corrupto
│
└─ Se vazio:
├─ Load catálogo de serviços publicados
├─ Load configurações globais de tenants
├─ Load tabelas de referência (países, UFs)
│
Processamento em background paralelo
│
└─ Aplicação inicia respondendo após cache warm (30s típico)
Cache warming reduz impacto de falha recuperada — não há avalanche de cache miss nos primeiros minutos.
35.11 Estratégia de Limpeza e Retenção
35.11.1 Memory Eviction Policy
Redis tem limite de memória. Quando atinge limite, política de despejo define o que remover:
| Política | Comportamento |
|---|---|
noeviction | Rejeita novas writes; nunca remove itens |
allkeys-lru | Remove chaves menos recentemente usadas |
volatile-lru | Remove chaves com TTL (LRU entre as que expiram) |
allkeys-random | Remove chaves aleatoriamente |
volatile-ttl | Remove chaves com TTL mais próximo de expirar |
Recomendação: volatile-lru para cache puro (dados com TTL são candidatos) ou noeviction + alertas para forçar operador a aumentar memória.
35.11.2 Limpeza Manual
Procedimento operacional periódico:
Semanal:
├─ Analisar chaves grandes ou suspeitas
│ SCAN 0 MATCH "platform:v1:tenant:*" COUNT 1000
│
├─ Deletar chaves órfãs (origem não mais existe)
│ DEL chaves_orfas
│
└─ Registrar em log operacional
Mensal:
├─ Flush de namespace de versão antiga (v0:*)
│ EVAL "return redis.call('del', unpack(redis.call('keys', KEYS[1])))" 1 "platform:v0:*"
│
└─ Relatório de uso de memória por tenant
35.11.3 TTL Persistente
Dados com TTL muito longo (ex: 24h) podem acumular. Revisar periodicamente:
Redis INFO stats → expired_keys: 1234567
Se expired_keys cresce linearmente, há chaves com TTL muito longo
que nunca são acessadas. Considerar reduzir TTL ou invalidar
ativamente chaves antigas.
35.12 Segurança de Cache
35.12.1 Dados Sensíveis em Cache
Dados pessoais (CPF, nome, e-mail) em cache exigem cuidado:
Armazenar: ID técnico (UUID), hash não-reversível
Não armazenar: documento completo, CPF, e-mail completo
Exemplo:
❌ cache:user:123 = { "cpf": "123.456.789-00", "email": "joao@example.com" }
✓ cache:user:123 = { "id": "user-uuid", "name_hash": "abc123" }
Dados sensíveis recuperados sob demanda do banco.
35.12.2 Autenticação de Cluster Redis
Redis pode exigir senha ou mTLS:
requirepass "senha-forte-gerada"
ou
tls-port 6380
tls-cert-file /path/to/cert
tls-key-file /path/to/key
Cliente conecta com credenciais:
redis-cli -u redis://:password@host:6379/0
Em Kubernetes, segredo é montado do Secret.
35.12.3 Isolamento de Rede
Redis roda em rede isolada, acessível apenas por aplicações da plataforma:
Redis Service
└─ Rede privada (ex: 10.0.1.0/24)
└─ Firewall: apenas fonte=AppServices, porta 6379/6380
└─ Sem acesso de cliente externo
35.12.4 Auditoria de Acesso
Comandos sensatos ao Redis são monitorados:
FLUSHDB, FLUSHALL, CONFIG, ACL, AUTH, SHUTDOWN, REPLCONF
Se alguém executa FLUSHALL em produção, deve haver alerta e registro investigável.
35.13 Custo Operacional e Sizing
35.13.1 Dimensionamento de Memória
Estimation inicial:
Dados em cache = Volume total * Taxa de cache hit desejada
Exemplo:
- Catálogo: 50MB (1000 serviços × 50KB)
- Configurações: 10MB (100 tenants × 100KB)
- Permissões ativas: 500MB (assume 1M subjects × 500B)
- Rate limiting: 100MB
- Sessões: 200MB
- Locks, contadores: 50MB
─────────────────────
Total: ~900MB
Com crescimento 20% ao ano + buffer 40%:
= 900MB × 1.2 × 1.4 = 1512MB ≈ 2GB alocados
Redis com data persistence (RDB/AOF): +30% para log de replicação.
35.13.2 Monitoramento de Capacity
Alerta: used_memory > 70% max_memory
Alerta: connected_clients > 80% max_clients
Alerta: evicted_keys > 0 (se policy = noeviction)
Reação:
- Se hit_rate cai, aumentar memória;
- Se conexões maxam, aumentar pool ou verificar leaks;
- Se evictados crescem, avaliar política ou aumentar memória.
35.13.3 Custo vs. Benefício
Redis tem custo: infraestrutura, memória, operação. O benefício deve justificar:
Custo Redis: R$ 1000/mês
Benefício:
- Redução 50% de queries ao BD
- Redução 5s em latência média (P99)
- Capacidade de 50 rps extra sem upgrade do BD
- Redução 30% em hardware de BD necessário
Custo evitado: R$ 3000/mês (BD maior)
ROI: 3.0x
35.14 Observabilidade do Cache em Produção
35.14.1 Dashboard de Cache
Dashboard Grafana com painéis:
┌─ Hit Rate Trend
├─ Miss Rate Trend
├─ Evictions Rate
├─ Memory Usage (series por tenant)
├─ Latency (p50, p95, p99)
├─ Connections
├─ Key Count por Prefix (top 20)
├─ Top Cache Keys por Hitrate
├─ Replication Lag (se replicado)
└─ Invalidation Events Rate
Refresha cada 10 segundos. Histórico 7 dias.
35.14.2 Queries de Debug Úteis
# Tamanho total
INFO memory | grep used_memory_human
# Hit rate
INFO stats | grep -E "hits|misses"
# Chaves por padrão
SCAN 0 MATCH "platform:v1:tenant:org-uuid:*" COUNT 1000
# Maior chave
SCAN 0 TYPE string | xargs -I {} redis-cli STRLEN {}
# TTL de chave
TTL "platform:v1:tenant:org-uuid:config:password-policy"
# Política de memória
CONFIG GET maxmemory-policy
CONFIG GET maxmemory
35.14.3 Trace de Cache Miss
Quando hit rate cai, investigação sistemática:
1. Verificar se cache está conectado
PING → PONG?
2. Verificar se TTL está expirado
TTL key → deveria ser > 0
3. Verificar se invalidação foi muito agressiva
SCAN 0 MATCH "pattern:*"
4. Verificar se aplicação está cacheando
Logs: "GET cache:*"
5. Verificar se há serialização falhando
Logs: "Cache deserialize error"
35.15 Padrões Avançados
35.15.1 Cache Multilevel
Aplicações combinam cache local em memória + cache distribuído Redis:
GET user:123
│
├─ Local cache (app memory) → Hit!
│ Retorna em < 1ms
│
├─ Cache miss local → Redis
│ Retorna em 5ms
│ Popula local cache
│
└─ Cache miss Redis → Database
Retorna em 50ms
Popula Redis + local cache
Local cache reduz latência crítica de rota. Redis garante coerência entre instâncias.
35.15.2 Bloom Filters para Negação
Usar Bloom Filter em Redis para saber se uma chave definitivamente não existe:
SET tentative:user:123 0 [significa: definitivamente não existe]
vs
[sem SET = talvez exista]
Antes de consultar banco, verificar Bloom Filter:
if (bloomFilter.contains(key))
return null (definitivamente não existe)
else
query banco (pode ou não existir)
Economiza consulta ao banco para dados não-encontrados frequentes.
35.15.3 Pub/Sub para Sinais Operacionais
Cache usa Redis Pub/Sub para coordenação de invalidação:
Serviço A publica em "cache.invalidate.org-uuid":
{ "pattern": "platform:v1:tenant:org-uuid:catalog:*" }
Serviço B, C, D escutam o canal e executam DEL
Todos os consumidores invalidam simultaneamente — coerência distribuída sem polling.
35.15.4 Redisearch (Índices em Redis)
Redis Stack oferece Redisearch para busca full-text em cache:
FT.CREATE idx:catalog_services
ON HASH
SCHEMA
serviceId TAG
name TEXT WEIGHT 10
description TEXT WEIGHT 5
tenantId TAG
FT.SEARCH idx:catalog_services "@tenantId:org-uuid @name:habilitação"
Cache de busca sem indexador separado. Trade-off: indexação em memória usa mais CPU.
35.16 Cache de Identidade e Autorização
35.16.1 Cache de Perfis de Usuário
Perfis de usuários (internos e cidadãos) são consultados em cada requisição para autorização. Cache em Redis reduz latência de 50ms para 5ms:
Chave: platform:v1:tenant:{tenantId}:subject:{subjectId}:profile
TTL: 15 minutos
Invalidação: evento `identity.profile-updated.v1`
Quando o usuário atualiza perfil, evento dispara invalidação.
35.16.2 Cache de Permissões
Cache de permissões calculadas reduz chamadas ao IAM:
Chave: platform:v1:tenant:{tenantId}:effective-permissions:{subjectId}
TTL: 5 minutos
Invalidação:
- subject-updated
- role-updated
- unit-changed
Permissões são calculadas uma vez, cacheadas, e reutilizadas em múltiplas requisições.
35.16.3 Cache de Tokens Revogados
Quando um token é revogado (logout forçado, suspeita de comprometimento), deve ser rejeitado imediatamente. Lista de tokens revogados em cache:
Chave: platform:v1:tenant:{tenantId}:revoked-tokens:{tokenId}
TTL: até expiração natural do token
Operação: SADD revoked-tokens tokenId, SISMEMBER para consulta
Cada requisição verifica se token está na lista. Operação rápida (O(1)).
35.16.4 Cache de Sessão Ativa
Sessões ativas de atendimento cidadão:
Chave: platform:v1:tenant:{tenantId}:session:{sessionId}
TTL: 30 minutos (renovável)
Invalidação: logout, sessão expirada, troca de usuário
Sessão armazena contexto: tenant atual, último canal acessado, preferências.
35.17 Cache de Modelo e Embeddings de IA
35.17.1 Cache de Inferência LLM
LLM é custoso (latência de segundos, custo por token). Cache de resultados idênticos economiza:
Chave: platform:v1:tenant:{tenantId}:llm:{model}:{query_hash}
TTL: 24 horas
Operação: cache.get(query_hash) → se miss, invocar LLM, cachear
Query hash considera: prompt, modelo, parâmetros (temperatura, top_k). Queries com semântica similar mas texto diferente não compartilham cache.
35.17.2 Cache Semântico (Embeddings)
Cache semântico usa embeddings para encontrar respostas similares:
Chave: platform:v1:tenant:{tenantId}:llm-semantic:{embedding_hash}
TTL: 24 horas
Operação:
1. Gerar embedding da query
2. Buscar no vector store embeddings similares (cosine > 0.95)
3. Se encontrado, retornar resposta cacheada
4. Senão, invocar LLM
Respostas idênticas ou semanticamente similares são compartilhadas.
35.17.3 Cache de Classificação
Classificação automática de solicitações (sentimento, urgência, categoria):
Chave: platform:v1:tenant:{tenantId}:classification:{text_hash}
TTL: 12 horas
Operação:
1. Hash do texto
2. Cache.get(hash)
3. Se miss, modelo ML classifica e cacheia
Reduz uso de GPU/TPU em classificação repetida.
35.17.4 Invalidação de Cache IA
Quando modelo é retreinado ou corrigido, todo cache de inferência é invalidado:
Modelo v2 publicado
│
├─ Evento: ai.model-updated.v1 (model_name, version)
│
└─ Listener: cache invalidate pattern
platform:v1:tenant:*:llm:{model_name}:*
Versionamento de modelo no namespace evita conflitos.
35.18 Cache de Queries e Resultados Pesados
35.18.1 Queries Analíticas Agregadas
Dashboards executivos executam queries de agregação pesada. Cache de resultado:
Chave: platform:v1:tenant:{tenantId}:dashboard:{dashboardId}:{periodo}
TTL: 5 minutos
Operação:
- cache.get → retorna rápido
- cache miss → executa query agregada + cacheia
Dashboard executivo responde em 50ms em vez de 5 segundos.
35.18.2 Relatórios Periódicos
Relatórios mensais, semanais são pré-computados e cacheados:
Job noturno executa relatórios pesados
│
├─ Computa agregações do mês
│
├─ Cache: SET platform:v1:tenant:org:report:monthly:2026-06 value
│
└─ TTL: 31 dias (até próximo relatório)
Usuário abre dashboard e vê relatório pronto em milissegundos.
35.18.3 Geocoding e Reverse Geocoding
Cache de geocoding reduz chamadas a serviços externos:
Chave: platform:v1:tenant:{tenantId}:geocode:{address_hash}
TTL: 30 dias
Operação: cache.get(address_hash) → se miss, serviço externo
Endereços não mudam de coordenadas com frequência. Cache por 30 dias é seguro.
35.19 Casos Especiais
35.19.1 Cache de Plano de Execução PostgreSQL
PostgreSQL tem cache interno de plano de execução. Aplicações podem usar PREPARE para cachear em PreparedStatement:
PREPARE select_user (UUID) AS
SELECT * FROM users WHERE id = $1 AND tenant_id = current_setting('app.current_tenant');
Plano de execução fica cacheado na sessão. Reutilização em múltiplas execuções evita planner overhead.
35.19.2 Cache de Schema Metadata
Cache de metadata do banco evita consultas constantes a information_schema:
Chave: platform:v1:tenant:{tenantId}:schema:{tableName}:columns
TTL: 30 minutos
Operação: cache.get → retorna metadata cacheada
Invalidação quando migração altera schema (via evento).
35.19.3 Cache de Tokens de Integração
Tokens OAuth de sistemas externos (GOV.BR, SEI!MG) são cacheados:
Chave: platform:v1:tenant:{tenantId}:integration:{integrationName}:access-token
TTL: próximo a expiração do token (ex: 50 min para token de 60min)
Operação: cache.get → se expira, refresh e cachear
Reduz chamadas de autenticação desnecessárias.
35.19.4 Cache de Rotas e Configuração de Roteamento
Rotas internas e configuração de roteamento (gateway, API gateway) são cacheados:
Chave: platform:v1:tenant:{tenantId}:route:{service}:{version}
TTL: 10 minutos
Operação: cache.get → se rota muda, nova versão cacheada
Gateway descobre serviços atualizados rapidamente.
35.20 Migração e Evolução de Cache
35.20.1 Versionamento de Schema
Schema de cache evolui com o tempo. Sem versionamento, mudança quebra caches antigos:
Sem versionamento:
v1: SET cache:user:123 "{ name, email }"
v2: schema inclui novo campo "phone"
──────────
Aplicação v2 lê cache v1: falta campo phone
Aplicação v1 lê cache v2: campo phone ignorado (ok)
Com versionamento:
v1: SET cache:v1:user:123 "{ name, email }"
v2: SET cache:v2:user:123 "{ name, email, phone }"
──────────
Aplicação v1: lê apenas cache:v1
Aplicação v2: lê apenas cache:v2
Migração: ambas versões rodam até cache v1 expirar
Versionamento elimina inconsistências durante deploy.
35.20.2 Migração de Chaves
Quando nova versão é deployada, chaves antigas podem precisar ser migradas. Estratégia:
Estratégia A: Read-with-Fallback
GET cache:v2:user:123
│
├─ Hit → retorna
└─ Miss → GET cache:v1:user:123 (legacy)
│
├─ Hit → retorna + SET cache:v2:user:123 (atualiza)
└─ Miss → query banco + SET v1 e v2
Estratégia B: Background Migration
Job assíncrono durante transição:
SCAN cache:v1:user:* → para cada hit, lê banco, escreve v2
Aplicações leem apenas v2
Background popula v2 com chaves quentes
35.20.3 Descontinuação de Cache Antigo
Após período de transição (cache antigo TTL expirou), namespace é removido:
1. Aplicação não escreve mais em cache:v1:*
2. Cache v1:* ainda existe até TTL expirar
3. Monitorar contagem de chaves v1:*
4. Quando v1:* chega a zero: cleanup manual
5. Remover lógica de fallback
35.21 Rastreabilidade com o Anexo III
| Item ANX-III | Atendimento |
|---|---|
| 1.1 | Cache de catálogos e configurações acelera integração entre canais |
| 3.1 | Cache de permissões reduz latência de autorização |
| 4.5 | Cache de inferência LLM reduz latência de IA |
| 6.2 | Cache de identidade e tokens revogados suporta controle de acesso |
| 6.3 | Cache de auditoria de alterações acelera consultas |
35.22 Benefícios do Cache Distribuído
- Performance: redução de 10-100x em latência para dados frequentemente acessados;
- Escalabilidade: diminui carga no banco de dados, permitindo maior throughput;
- Resiliência: cache funciona como fallback quando banco está lento (circuit breaker);
- Economia: menos requisições de I/O = menos CPU/memória no servidor de banco;
- Experiência do usuário: respostas rápidas geram percepção de plataforma ágil;
- Redução de custos: menos servidores de banco necessários com cache eficaz.
35.23 Riscos e Mitigações
| Risco | Consequência | Mitigação |
|---|---|---|
| Cache fora de sincronia com banco | Dados inconsistentes apresentados | Invalidação por evento + TTL curto + versão + consulta banco periodicamente |
| Cache stampede — múltiplas queries simultâneas na origem | Sobrecarga temporária no banco | Jitter no TTL, single-flight (apenas uma requisição recomputa), stale-while-revalidate |
| Dado sensível em cache sem proteção | Roubo de informação via inspeção do Redis | Criptografia em trânsito (TLS), em repouso, sem PII completo (apenas ID) |
| Redis com muita memória em manutenção | Downtime percebido | HA (Sentinel/Cluster), replicação, política de eviction, monitoramento de memória |
| Tenants "vendo" dados de outros em cache | Vazamento de dados | Tenant no namespace, segregação estrutural, teste de penetração específico para multi-tenant |
| Cache nunca invalida (bug de lógica) | Dados antigos indefinidamente | Validação de invalidação em testes, TTL como "hard limit", alerta de staleness |
| Ausência de cache em falha crítica | Queda do banco quando Redis cai | Política clara: alguns caches são fail-open (usar banco se Redis falhar); outros fail-closed (bloquear) |
35.24 Decisões Arquiteturais
| ADR | Tema |
|---|---|
| ADR-415 | Redis Sentinel vs. Cluster — topologia HA |
| ADR-416 | Política de eviction — LRU, LFU ou custom |
| ADR-417 | Estratégia de cache keys — namespace, versioning, format |
| ADR-418 | TTL por tipo de dado — valores de referência |
| ADR-419 | Invalidação — por evento vs. TTL vs. ambos |
| ADR-420 | Criptografia de dados sensíveis em cache |
| ADR-421 | Circuit breaker — quando falhar cache, chamar banco? |
| ADR-422 | Cache local (in-process) vs. distribuído (Redis) — decisão por serviço |
| ADR-423 | Monitoramento e alerta de cache hit rate, churn, memória |
| ADR-424 | Política de fallback — quais componentes toleram cache miss vs. bloqueiam |
35.25 Integração com Observabilidade
35.25.1 Métricas Essenciais
redis_cache_hits_total
redis_cache_misses_total
redis_cache_hit_rate = hits / (hits + misses)
redis_cache_evictions_total
redis_memory_used_bytes
redis_connected_clients
redis_commands_processed_total
redis_latency_p99_ms
Hit rate >80% indica cache bem configurado. Taxa <50% sugere TTL curto ou padrão de acesso ruim.
35.25.2 Alertas
alert: CacheHitRateLow
if: redis_cache_hit_rate < 0.5
for: 5m
action: Investigar padrão de acesso e TTL
alert: RedisMemoryHigh
if: redis_memory_used_bytes > 0.9 * max_memory
for: 5m
action: Aumentar memória ou revisar política de eviction
alert: RedisLatencyHigh
if: redis_latency_p99_ms > 100
for: 5m
action: Investigar contention, GC, network latency
35.26 Considerações Finais
O cache distribuído é como um acelerador silencioso — quando bem configurado, a plataforma sente significativamente mais rápida sem que o usuário perceba mudança visual. Quando mal configurado, dados desatualizados ou indisponíveis transformam experiência em frustração.
A solidez do cache distribído não reside apenas em escolher Redis, mas em compreender profundamente cada padrão de acesso, definir TTL apropriado, implementar invalidação confiável, e operar com visibilidade total dos hit rates e latências.
O Capítulo 36 detalha a Busca, Vetores e Indexação Semântica.
35.27 Controle de Versão
| Campo | Valor |
|---|---|
| Documento | Documento Mestre — Plataforma de Relacionamento Digital com o Cidadão |
| Capítulo | 35 — Cache Distribuído |
| Versão | 1.0 |
| Situação | Concluído |
| Última atualização | 16/07/2026 |
35.28 Rastreabilidade PRODEMGE
- [ANX-III] Bloco 1, 3, 4, 6 — performance de integração, IA e segurança via cache otimizado.
- [ANX-IV] — Capacidades técnicas de escalabilidade (alta vazão) e resiliência (degradação graciosa).
- [ANX-V] Item 2.1 — Manutenibilidade com estratégia de cache bem documentada, TTL e invalidação confiáveis.
- [PNR] — Plano de Negócio Referencial: plataforma com performance em escala via cache distribuído.
- [EDITAL] — Edital CP001/2026: subsistema de cache como componente crítico de escalabilidade.
Capítulo 34 — Banco de Dados e Persistência
Este capítulo detalha a estratégia de persistência e armazenamento de dados da Plataforma de Relacionamento Digital com o Cidadão, descrevendo como dados transacionais, documentais, analíticos e de auditoria são armazena…
Capítulo 36 — Busca, Vetores e Indexação Semântica
Este capítulo detalha a arquitetura de busca textual, busca vetorial (semântica) e indexação para a Plataforma de Relacionamento Digital com o Cidadão. Descreve como dados estruturados e não estruturados são indexados, c…