Pular para conteúdo

Redis, caches e estado operacional

Redis reúne caches reconstruíveis, contadores de quota/concorrência e locks. A perda desses dados tem efeitos diferentes: uma política pode ser recarregada, mas zerar um contador altera a admissão de requisições. Os padrões abaixo vêm dos produtores e consumidores; não descrevem instâncias corporativas.

Modelos visuais

Cache e invalidação

Caixas Redis mostram chave, tipo do valor e TTL; não são campos BSON nem novas collections.

Cache e invalidação; visão simplificada. Detalhes no visualizador abaixo. Cache e invalidação; visão simplificada em tema escuro.

Explorar diagramas detalhados com zoom

Legenda e convenções dos modelos. Os dicionários e evidências desta página permanecem a referência dos campos.

Políticas e configuração

%%{init: {"theme":"base","themeVariables":{"fontFamily":"Arial, sans-serif","fontSize":"16px","lineColor":"#64748b","primaryTextColor":"#172b4d"},"flowchart":{"htmlLabels":true,"useMaxWidth":true,"nodeSpacing":28,"rankSpacing":38,"padding":14,"curve":"linear","subGraphTitleMargin":{"top":12,"bottom":16}},"sequence":{"useMaxWidth":true,"actorMargin":28,"width":145,"height":45,"diagramMarginX":32,"diagramMarginY":12,"messageMargin":24,"wrap":true,"mirrorActors":false},"themeCSS":".edgeLabel, .edgeLabel p {background-color:var(--diagram-surface)!important;color:var(--diagram-text)!important}\n.cluster rect {fill:var(--diagram-group)!important;stroke:var(--diagram-line)!important}\n.cluster-label span, .cluster-label text {color:var(--diagram-text)!important;fill:var(--diagram-text)!important}\n.flowchart-link, .actor-line {stroke:var(--diagram-line)!important}\nmarker path {fill:var(--diagram-line)!important;stroke:var(--diagram-line)!important}\n.actor {fill:var(--diagram-domain)!important;stroke:var(--diagram-domain-border)!important}\ntext.actor, .messageText, .loopText, .labelText {fill:var(--diagram-text)!important}\n.messageLine0, .messageLine1, .loopLine {stroke:var(--diagram-line)!important}\n.labelBox {fill:var(--diagram-surface)!important;stroke:var(--diagram-line)!important}\n.note {fill:var(--diagram-async)!important;stroke:var(--diagram-async-border)!important}\n.noteText {fill:var(--diagram-text)!important}\n.nodeLabel, .nodeLabel p, .nodeLabel span {color:var(--diagram-text)!important}\n.node .nodeLabel, .node .nodeLabel p {font-size:17px;font-weight:600;line-height:1.4;text-align:center}\n.nodeLabel small {display:inline-block;margin-top:4px;font-size:15px;font-weight:400;line-height:1.4;color:var(--diagram-description)!important}\ntext.actor, text.actor tspan {font-size:17px;font-weight:600}\n.node.entry rect, .node.entry polygon, .node.entry path, .node.entry circle {fill:var(--diagram-entry)!important;stroke:var(--diagram-entry-border)!important}\n.node.domain rect, .node.domain polygon, .node.domain path, .node.domain circle {fill:var(--diagram-domain)!important;stroke:var(--diagram-domain-border)!important}\n.node.security rect, .node.security polygon, .node.security path, .node.security circle {fill:var(--diagram-security)!important;stroke:var(--diagram-security-border)!important}\n.node.store rect, .node.store polygon, .node.store path, .node.store circle {fill:var(--diagram-store)!important;stroke:var(--diagram-store-border)!important}\n.node.async rect, .node.async polygon, .node.async path, .node.async circle {fill:var(--diagram-async)!important;stroke:var(--diagram-async-border)!important}\n.node.external rect, .node.external polygon, .node.external path, .node.external circle {fill:var(--diagram-external)!important;stroke:var(--diagram-external-border)!important}\n text.actor tspan, .loopText tspan, .labelText tspan, .messageText tspan, .noteText tspan {fill:var(--diagram-text)!important}\n\n"}}%%
flowchart TB
    builder["Builder<br/><small>Rotas e recursos iPaaS</small>"]:::domain
    checker["Security Checker<br/><small>Autorizações e IPs</small>"]:::security
    redis[("Redis<br/><small>Valores de configuração</small>")]:::store
    invalidator["Cache Invalidator<br/><small>Remove chaves por entidade</small>"]:::async
    builder -->|CacheUtil| redis
    checker -->|JSON| redis
    invalidator -->|KEYS / UNLINK| redis
    classDef domain stroke-width:1.5px;
    classDef security stroke-width:1.5px;
    classDef store stroke-width:1.5px;
    classDef async stroke-width:1.5px;

Laranja identifica armazenamento, verde processamento, roxo segurança e amarelo invalidação. As setas representam acesso ao cache, não referências entre documentos MongoDB.

Padrão de chave Escritor / leitor Valor TTL e invalidação
{tenant}:route:{routeId} Builder Configuração normalizada de client.core.Route CacheUtil da biblioteca; TTL não definido na chamada. Invalidação por entidade route
{tenant}:resource:{resourceId} Builder Recurso iPaaS normalizado CacheUtil; invalidação por entidade
{tenant}:environment:{environmentId} Builder Ambiente iPaaS normalizado CacheUtil; invalidação por entidade
{tenant}:account:{accountId} Builder Conta iPaaS normalizada; tipo CUSTOM não é armazenado nesse caminho CacheUtil; invalidação por entidade
{tenant}:integration:{integrationId} Builder Diagrama iPaaS normalizado 120 s explícitos. projectId pertence à consulta, mas não à chave
{targetTenant}:authorization:{id} Security Checker JSON com id, clientId, type, openIdUrl, header, validations conforme tipo Default 300 s; modo Redis ou memória. Invalidação authorization
{targetTenant}:ip_policy:{id} Security Checker JSON id, cidr[], kind Default 300 s; Redis ou memória. Invalidação ip_policy
tenant-alias:{tenant} Global Registry / BFF Alias textual Escrita sem TTL explícito no chamador; delete e reconciliação periódica (default 5 min). BFF lê com timeout de 30 ms e omite alias na falha
lock:tenant-alias-initial-load Global Registry Token de lock pela biblioteca CacheStore 30 s; liberação com token após carga inicial

validations contém iss, issVal, aud, audVal, iat, exp, nbf. Os valores do Builder são modelos de cache/cliente, não collections de domínio. A serialização/TTL padrão do CacheUtil está na biblioteca compartilhada; não se deve inferir seu valor a partir do chamador.

Fontes: chaves Builder, adaptador iPaaS, modelo de rota, authorization cache, IP cache, aliases, BFF.

Identidade e tokens

Serviço / chave Valor e duração
Auth Provider: token:{tenant}:{realm}:{app}:{group} Access token; segmentos vazios são omitidos pelo BuildKey. TTL expiresIn − 120 s; não grava quando o resultado não é positivo
Auth Provider: {tenant}:token:{clientId}:{hash} Access token para client credentials; mesmo TTL. Hash: primeiros 16 bytes do SHA-256 de clientId + clientSecret, em hexadecimal
Auth Provider: {tenant}:oidc:{hash(url)} JSON {issuer,jwksUrl}; default 5 min. Hash da URL em lowercase, SHA-256 truncado a 16 bytes
Auth Provider: {tenant}:jwks:{hash(url)}:kid:{kid} JWK: kty, kid, use, alg, crv, n, e, x, y, x5c; default 5 min
Security Checker: {tenant}:authorization:{authorizationId}:token:{hash} Token do caminho IssueToken; TTL expiresIn − 15 s
Security Checker: chave de token por app/group ou client Padrões equivalentes ao Auth Provider; TTL expiresIn − 120 s no caminho geral

O cliente de cache Go serializa valores em JSON, inclusive strings de token, e permite memória/Redis conforme configuração. Tokens e material de identidade não devem aparecer em exemplos com valores reais. A expiração é a principal renovação desses caches; invalidar uma autorização não equivale a revogar todos os tokens emitidos.

Fontes: Auth Provider tokens, OIDC/JWK, serialização, configuração, Checker tokens.

Contadores e exclusão mútua

%%{init: {"theme":"base","themeVariables":{"fontFamily":"Arial, sans-serif","fontSize":"16px","lineColor":"#64748b","primaryTextColor":"#172b4d"},"flowchart":{"htmlLabels":true,"useMaxWidth":true,"nodeSpacing":28,"rankSpacing":38,"padding":14,"curve":"linear","subGraphTitleMargin":{"top":12,"bottom":16}},"sequence":{"useMaxWidth":true,"actorMargin":28,"width":145,"height":45,"diagramMarginX":32,"diagramMarginY":12,"messageMargin":24,"wrap":true,"mirrorActors":false},"themeCSS":".edgeLabel, .edgeLabel p {background-color:var(--diagram-surface)!important;color:var(--diagram-text)!important}\n.cluster rect {fill:var(--diagram-group)!important;stroke:var(--diagram-line)!important}\n.cluster-label span, .cluster-label text {color:var(--diagram-text)!important;fill:var(--diagram-text)!important}\n.flowchart-link, .actor-line {stroke:var(--diagram-line)!important}\nmarker path {fill:var(--diagram-line)!important;stroke:var(--diagram-line)!important}\n.actor {fill:var(--diagram-domain)!important;stroke:var(--diagram-domain-border)!important}\ntext.actor, .messageText, .loopText, .labelText {fill:var(--diagram-text)!important}\n.messageLine0, .messageLine1, .loopLine {stroke:var(--diagram-line)!important}\n.labelBox {fill:var(--diagram-surface)!important;stroke:var(--diagram-line)!important}\n.note {fill:var(--diagram-async)!important;stroke:var(--diagram-async-border)!important}\n.noteText {fill:var(--diagram-text)!important}\n.nodeLabel, .nodeLabel p, .nodeLabel span {color:var(--diagram-text)!important}\n.node .nodeLabel, .node .nodeLabel p {font-size:17px;font-weight:600;line-height:1.4;text-align:center}\n.nodeLabel small {display:inline-block;margin-top:4px;font-size:15px;font-weight:400;line-height:1.4;color:var(--diagram-description)!important}\ntext.actor, text.actor tspan {font-size:17px;font-weight:600}\n.node.entry rect, .node.entry polygon, .node.entry path, .node.entry circle {fill:var(--diagram-entry)!important;stroke:var(--diagram-entry-border)!important}\n.node.domain rect, .node.domain polygon, .node.domain path, .node.domain circle {fill:var(--diagram-domain)!important;stroke:var(--diagram-domain-border)!important}\n.node.security rect, .node.security polygon, .node.security path, .node.security circle {fill:var(--diagram-security)!important;stroke:var(--diagram-security-border)!important}\n.node.store rect, .node.store polygon, .node.store path, .node.store circle {fill:var(--diagram-store)!important;stroke:var(--diagram-store-border)!important}\n.node.async rect, .node.async polygon, .node.async path, .node.async circle {fill:var(--diagram-async)!important;stroke:var(--diagram-async-border)!important}\n.node.external rect, .node.external polygon, .node.external path, .node.external circle {fill:var(--diagram-external)!important;stroke:var(--diagram-external-border)!important}\n text.actor tspan, .loopText tspan, .labelText tspan, .messageText tspan, .noteText tspan {fill:var(--diagram-text)!important}\n\n"}}%%
flowchart TB
    scheduler["Scheduler<br/><small>Inicializa e repõe quotas</small>"]:::domain
    quota[("Redis · ratelimit<br/><small>Saldo inteiro por atividade</small>")]:::store
    checker["Security Checker<br/><small>Consulta e decrementa saldo</small>"]:::security
    scheduler -->|SETNX / SET / DEL| quota
    checker -->|KEYS / GET / DECR| quota
    classDef domain stroke-width:1.5px;
    classDef security stroke-width:1.5px;
    classDef store stroke-width:1.5px;
Chave Dono / operações Expiração e atomicidade
{tenant}:ratelimit:{activityId} Scheduler inicializa com SETNX, repõe com SET e remove com DEL. Valor inteiro maxRequests. Checker procura *:ratelimit:{id}, escolhe primeiro resultado, GET e DECR Sem TTL declarado. GET/decisão/DECR são comandos separados; busca wildcard não seleciona tenant explicitamente
{tenant}:{concurrencyKey} (default concurrent_requests) Broker lê saldo concorrente, verifica limite e INCR; na conclusão DECR, corrigindo valor negativo com INCR Sem TTL declarado; admissão não é uma operação atômica conjunta. Falhas Redis permitem prosseguir
rate-limit:{tenant}:{bucket} BFF: contador inteiro, Lua INCR e EXPIRE na primeira ocorrência Janela default 60 s, limite default 5. Script agrupa incremento/expiração; falha retorna contagem zero
scheduler:http-call:lock:{id} Scheduler: token UUID, SET NX PX; liberação Lua compare-and-delete Timeout da chamada + padding (default 5 s); usado para política CRON/SKIP

O Rate Limit Mongo descreve a política; a atividade Scheduler controla reposição; a chave Redis representa saldo. IDs são correlacionados por contratos HTTP, não por relacionamento físico. Exclusão de cache indiscriminada também remove estado operacional.

Fontes: quota Scheduler, operações de saldo, consumo Checker, Broker, bucket BFF, lock HTTP.

Invalidação e caches em memória

Cache Invalidator constrói {tenant}:{entity}:{id}; segmentos não informados podem se tornar curingas. Usa KEYS e UNLINK. Eventos iPaaS sem tenant podem alcançar múltiplos tenants. A invalidação HTTP com tenant também publica evento para consumidores. O script Cache Monitor possui recuperação ampla com FLUSHALL e limpeza do Discovery; esse comportamento não representa invalidação seletiva.

Serviço Cache em memória Renovação / limite
Discovery Mapa de rotas agrupado por tenant Invalida tenant por mensagem; limpa tudo na desconexão do consumidor; sem TTL de rota
Auth Provider JWKS por realm, separado do cache OIDC/JWK anterior TTL 5 min, max stale 1 h; limpeza periódica 5 min
Broker JWKS TTL 1 h
Web Pages Caffeine openapi-doc-cache Expiração após escrita de 3 min
Auth Provider / Security Checker Backend alternativo de cache em memória TTL dos respectivos clientes; estado não compartilhado entre processos

Fontes: invalidador, monitor, Discovery, JWKS Auth Provider, Web Pages.

Riscos de concorrência, abrangência de chaves e retenção estão separados em Análises de dados.