Relacionamento Digitalcom o Cidadão
Parte V — Tecnologia
Parte V — TecnologiaCapítulo 35

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:

  1. 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.

  2. Cache tem TTL e invalidação — entradas têm tempo de vida finito e são invalidadas ativamente quando a informação subjacente muda.

  3. 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:

TipoTTL de ReferênciaJustificativa
Catálogo de serviços5–15 minPublicação é evento raro
Configuração de tenant10–30 minMudanças raras
Permissões calculadas1–5 minMudanças moderadas
Sessão HTTP30 minSessão natural
Resultados de API1–5 minTolerância a staleness
Contadores dashboard5 minPrecisão próxima do real
Modelo IA inferido24 hSimilar 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égiaQuando UsarVantagemRisco
TTL puroDados tolerantes a staleness, baixo risco de inconsistênciaSimplicidade operacionalDados obsoletos até expirar
Invalidação ativaDados sensíveis a freshnessConsistência imediataComplexidade operacional
TTL + invalidaçãoCombinaçãoDefesa em profundidadeCusto 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íticaComportamento
noevictionRejeita novas writes; nunca remove itens
allkeys-lruRemove chaves menos recentemente usadas
volatile-lruRemove chaves com TTL (LRU entre as que expiram)
allkeys-randomRemove chaves aleatoriamente
volatile-ttlRemove 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-IIIAtendimento
1.1Cache de catálogos e configurações acelera integração entre canais
3.1Cache de permissões reduz latência de autorização
4.5Cache de inferência LLM reduz latência de IA
6.2Cache de identidade e tokens revogados suporta controle de acesso
6.3Cache 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

RiscoConsequênciaMitigação
Cache fora de sincronia com bancoDados inconsistentes apresentadosInvalidação por evento + TTL curto + versão + consulta banco periodicamente
Cache stampede — múltiplas queries simultâneas na origemSobrecarga temporária no bancoJitter no TTL, single-flight (apenas uma requisição recomputa), stale-while-revalidate
Dado sensível em cache sem proteçãoRoubo de informação via inspeção do RedisCriptografia em trânsito (TLS), em repouso, sem PII completo (apenas ID)
Redis com muita memória em manutençãoDowntime percebidoHA (Sentinel/Cluster), replicação, política de eviction, monitoramento de memória
Tenants "vendo" dados de outros em cacheVazamento de dadosTenant no namespace, segregação estrutural, teste de penetração específico para multi-tenant
Cache nunca invalida (bug de lógica)Dados antigos indefinidamenteValidação de invalidação em testes, TTL como "hard limit", alerta de staleness
Ausência de cache em falha críticaQueda do banco quando Redis caiPolítica clara: alguns caches são fail-open (usar banco se Redis falhar); outros fail-closed (bloquear)

35.24 Decisões Arquiteturais

ADRTema
ADR-415Redis Sentinel vs. Cluster — topologia HA
ADR-416Política de eviction — LRU, LFU ou custom
ADR-417Estratégia de cache keys — namespace, versioning, format
ADR-418TTL por tipo de dado — valores de referência
ADR-419Invalidação — por evento vs. TTL vs. ambos
ADR-420Criptografia de dados sensíveis em cache
ADR-421Circuit breaker — quando falhar cache, chamar banco?
ADR-422Cache local (in-process) vs. distribuído (Redis) — decisão por serviço
ADR-423Monitoramento e alerta de cache hit rate, churn, memória
ADR-424Polí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

CampoValor
DocumentoDocumento Mestre — Plataforma de Relacionamento Digital com o Cidadão
Capítulo35 — Cache Distribuído
Versão1.0
SituaçãoConcluído
Última atualização16/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.

Nesta página

35.1 Objetivo do Capítulo35.2 Papel do Cache na Plataforma35.3 Tipos de Cache e Casos de Uso35.3.1 Cache de Identidade e Permissões35.3.2 Cache de Configuração de Tenant35.3.3 Cache de Catálogo de Serviços35.3.4 Cache de Resposta de API35.3.5 Cache de Sessão de Atendimento35.3.6 Cache de Permissões Calculadas35.3.7 Cache de Modelo de IA e Predições35.3.8 Cache de Contadores e Agregados35.3.9 Cache de Bloqueio Distribuído35.4 Arquitetura Redis35.4.1 Redis como Tecnologia de Cache35.4.2 Cluster Redis para Alta Disponibilidade35.4.3 Isolamento Multi-Tenant via Namespace35.4.4 Versionamento de Schema de Cache35.4.5 Separação por Cluster de Cache35.5 Política de TTL e Invalidação35.5.1 TTL Explícito por Tipo de Dado35.5.2 Invalidação Ativa por Evento35.5.3 TTL vs. Invalidação Ativa35.5.4 Invalidação por Tag35.5.5 Cache Refresh Pattern35.6 Prevenção de Cache Stampede35.6.1 O Problema35.6.2 Mitigação 1: Jitter no TTL35.6.3 Mitigação 2: Single-Flight35.6.4 Mitigação 3: Stale-While-Revalidate35.6.5 Mitigação 4: Probabilistic Early Expiration (XFetch)35.7 Camada de Acesso ao Cache35.7.1 Interface de Cache Abstraída35.7.2 Serialização Eficiente35.7.3 Pipeline e Batch35.7.4 Connection Pool35.8 Cache de Resposta HTTP35.8.1 Cache-Control Headers35.8.2 Validação com ETag35.8.3 Cache de CDN35.8.4 Não-Cache de Dados Sensíveis35.9 Monitoramento e Observabilidade de Cache35.9.1 Métricas Críticas35.9.2 Alertas35.9.3 Logs de Invalidação35.9.4 Rastrear Cache Poisoning35.10 Política de Falha e Resiliência35.10.1 Redis Indisponível: Fail-Open vs. Fail-Closed35.10.2 Circuit Breaker no Acesso ao Cache35.10.3 Cache Replication e Failover35.10.4 Cache Warming em Startup35.11 Estratégia de Limpeza e Retenção35.11.1 Memory Eviction Policy35.11.2 Limpeza Manual35.11.3 TTL Persistente35.12 Segurança de Cache35.12.1 Dados Sensíveis em Cache35.12.2 Autenticação de Cluster Redis35.12.3 Isolamento de Rede35.12.4 Auditoria de Acesso35.13 Custo Operacional e Sizing35.13.1 Dimensionamento de Memória35.13.2 Monitoramento de Capacity35.13.3 Custo vs. Benefício35.14 Observabilidade do Cache em Produção35.14.1 Dashboard de Cache35.14.2 Queries de Debug Úteis35.14.3 Trace de Cache Miss35.15 Padrões Avançados35.15.1 Cache Multilevel35.15.2 Bloom Filters para Negação35.15.3 Pub/Sub para Sinais Operacionais35.15.4 Redisearch (Índices em Redis)35.16 Cache de Identidade e Autorização35.16.1 Cache de Perfis de Usuário35.16.2 Cache de Permissões35.16.3 Cache de Tokens Revogados35.16.4 Cache de Sessão Ativa35.17 Cache de Modelo e Embeddings de IA35.17.1 Cache de Inferência LLM35.17.2 Cache Semântico (Embeddings)35.17.3 Cache de Classificação35.17.4 Invalidação de Cache IA35.18 Cache de Queries e Resultados Pesados35.18.1 Queries Analíticas Agregadas35.18.2 Relatórios Periódicos35.18.3 Geocoding e Reverse Geocoding35.19 Casos Especiais35.19.1 Cache de Plano de Execução PostgreSQL35.19.2 Cache de Schema Metadata35.19.3 Cache de Tokens de Integração35.19.4 Cache de Rotas e Configuração de Roteamento35.20 Migração e Evolução de Cache35.20.1 Versionamento de Schema35.20.2 Migração de Chaves35.20.3 Descontinuação de Cache Antigo35.21 Rastreabilidade com o Anexo III35.22 Benefícios do Cache Distribuído35.23 Riscos e Mitigações35.24 Decisões Arquiteturais35.25 Integração com Observabilidade35.25.1 Métricas Essenciais35.25.2 Alertas35.26 Considerações Finais35.27 Controle de Versão35.28 Rastreabilidade PRODEMGE