Skip to content

Worker v3 + Upstash Redis — Plano de Implementação

Status: Em implementação — Fase 0 pendente (provisionamento Upstash) Última revisão: 2026-04-19 Documentos relacionados: WORKER_TELEMETRY · CLOUDFLARE_WORKER_GATEWAY · UPSTASH_REDIS_MIGRATION · RATE_LIMITS


1. Contexto e limitações do Worker v2

O Cloudflare Worker v2 atual (ver CLOUDFLARE_WORKER_GATEWAY.md) cumpre três funções: roteamento de domínio, encaminhamento de cookies e burst rate limit em /portal-escola. Está em produção desde fevereiro de 2026 e atende ao tráfego atual com folga, mas acumula limitações estruturais que bloqueiam a próxima fase de proteção:

Limitação v2Impacto
Burst rate limit via Map in-memoryContador é fragmentado por PoP da Cloudflare. Um atacante distribuído contorna o limite trivialmente.
Burst só em /portal-escolaDemais endpoints (auth, dashboards, CRUD, broadcast) não têm camada anti-burst no Worker.
Sem telemetria estruturadaNão temos baseline de tráfego por endpoint, escola, IP ou usuário. Impossível calibrar limites com dados reais.
Sem headers RFC 6585Respostas 429 não informam X-RateLimit-Limit, Retry-After, Reset. Frontend não sabe quando tentar de novo.
Sem rate limit por usuário autenticadoTudo que passa do /me é proxy puro. Operações autenticadas (importação, broadcast) não têm proteção no edge.
Arquivo único index.js ~600 linhasMistura roteamento, cookies, CORS, burst, OAuth — bloqueia evolução incremental.

A migração para v3 ataca todas essas limitações simultaneamente, com Upstash Redis como backing store global e telemetria fire-and-forget como pré-requisito de qualquer bloqueio novo.


2. Estrutura de arquivos do Worker v3

O Worker v3 adota arquitetura multi-módulo. Cada arquivo tem uma responsabilidade única e é importado pelo orquestrador:

text
cloudflare-worker/
├── src/
│   ├── index.js          # Orquestrador: routing, fluxo principal, fail-open
│   ├── redis.js          # Cliente Upstash REST + helpers (INCR, EXPIRE, SCAN)
│   ├── telemetry.js      # Coleta fire-and-forget (ctx.waitUntil)
│   ├── ratelimit.js      # Lógica de bloqueio (burst Redis + janelas)
│   ├── cookies.js        # Parsing/forwarding de olp_auth e olp_mural
│   └── cors.js           # Headers CORS unificados
└── wrangler.toml

Critério de aceite da estrutura: nenhum arquivo passa de 250 linhas; cada módulo é testável isoladamente.


3. Key schema Redis completo

Toda chave usa prefixo de ambiente: olp: em produção, olp-stg: em staging. Mesmo database Upstash, isolamento por prefixo.

ChaveTTLPropósitoFase
olp:rl:burst:{ip}5sBurst global por IP, substitui Map in-memory2
olp:m:{endpoint}:{minuto}2hContador de requisições por endpoint/minuto1
olp:m:escola:{escola_id}:{minuto}2hContador por escola/minuto1
olp:m:blocked:{endpoint}:{minuto}2hContador de 429 por endpoint/minuto1
olp:m:status:{code}:{minuto}2hDistribuição de status HTTP por minuto1
olp:rl:api:{usuario_id}:{minuto}2minRate limit autenticado por usuário/minuto3
olp:bl:{jti}até expiração do tokenToken blacklist (revogação)futura

Regras absolutas: toda chave DEVE ter TTL. Nenhuma exceção. Chave sem TTL = memory leak no Redis.


4. Fases de implementação

Fase 0 — Setup Upstash + wrangler local

Pré-requisito manual, sem código.

  • Provisionar database Upstash (região gru1).
  • Adicionar secrets UPSTASH_REDIS_REST_URL e UPSTASH_REDIS_REST_TOKEN no Cloudflare (produção e staging).
  • Validar wrangler dev com Redis remoto.

Critério de saída: wrangler dev consegue executar INCR olp-stg:test contra Upstash.

Fase 1 — Telemetria fire-and-forget

Worker registra contadores em Redis via ctx.waitUntil() para todo request, em produção e staging. Zero bloqueio novo. O Worker v2 continua aplicando burst em /portal-escola exatamente como hoje.

Critério de entrada: Fase 0 concluída. Critério de saída: 7 dias contínuos com contadores olp:m:* populando consistentemente; cron horário de agregação para gateway_metrics operando (ver WORKER_TELEMETRY.md).

Fase 2 — Burst Redis global

Substitui o Map in-memory por INCR olp:rl:burst:{ip} EX 5. Aplicado em staging e produção simultaneamente — burst de 5 segundos é o limite menos arriscado de mover, não afeta usuários legítimos.

Critério de entrada: Fase 1 estável por ≥7 dias. Critério de saída: zero regressão em /portal-escola (taxa de 429 igual ou menor que v2) por ≥3 dias.

Fase 3a — Rate limit autenticado em modo observação (produção)

Worker calcula o limite por usuário/escola e adiciona o header X-RateLimit-Would-Block: true em respostas que seriam bloqueadas, mas deixa a requisição passar normalmente. Telemetria registra os "would-block" em olp:m:blocked:* para análise.

Critério de entrada: Fase 2 estável. Critério de saída: 4 semanas contínuas de coleta com baseline de tráfego real por usuário/escola/endpoint.

Fase 3b — Bloqueio real (staging primeiro, produção depois)

Em staging, bloqueio real ativo desde a Fase 1 com os limites propostos (entram como teste de funcionamento, não como guarda-rail real porque staging não tem usuários). Scripts de stress test validam que o código bloqueia, retorna headers RFC 6585 corretos e mensagens contextuais PT-BR.

Em produção, bloqueio real só ativa após Fase 3a completa (4 semanas) e calibração dos limites com dados reais. Os limites finais podem ser diferentes dos propostos.

Critério de entrada (produção): Fase 3a completa + calibração aprovada. Critério de saída: zero ticket de "fui bloqueado indevidamente" por 14 dias.


5. Regras obrigatórias de código

#RegraMotivo
1Toda chave Redis DEVE ter TTLSem TTL = memory leak
2redis.keys() PROIBIDO — usar SCAN com cursorkeys bloqueia o Redis em produção
3Telemetria sempre via ctx.waitUntil() — nunca no caminho críticoLatência do gateway não pode depender de Redis
4Fallback obrigatório: se Redis falhar, Worker continua sem rate limit (fail-open)Backend tem RLS e lockout próprios; nunca derrubar o site por falha de Redis
5Prefixo de ambiente obrigatório: olp: produção, olp-stg: stagingIsolamento de dados sem segundo database
6Respostas 429 incluem headers RFC 6585 completosFrontend precisa saber quando retentar
7Mensagens 429 sempre em PT-BR e contextuais ao escopoUX consistente com o resto do sistema

Detalhes dos headers RFC 6585 e mensagens contextuais: ver RATE_LIMITS.md seção "Worker Gateway — Rate Limits (v3)".


6. Documentos a atualizar após cada fase

FaseDocumentoMudança esperada
0CLOUDFLARE_WORKER_GATEWAY.mdAdicionar variáveis Upstash, nota de pré-requisito
1WORKER_TELEMETRY.mdConfirmar schema gateway_metrics, exemplos reais de leitura
1CRON_JOBS.mdRegistrar cron horário de agregação Redis → Postgres
2CLOUDFLARE_WORKER_CODE.mdSubstituir trecho de burst Map pelo trecho Redis
2RATE_LIMITS.mdMarcar burst como "global via Redis" em vez de "in-memory por PoP"
3aRATE_LIMITS.mdTabela de baseline real (substitui valores propostos por medidos)
3bRATE_LIMITS.mdMarcar Grupo B como "bloqueio ativo"
3bINCIDENT_POLICY.mdProcedimento de relax temporário de limite via Cloudflare KV

7. Riscos e mitigações

RiscoImpactoMitigação
Upstash indisponívelWorker perde rate limitFail-open obrigatório (Regra 4); backend mantém RLS e lockout; alerta ntfy crítico
Custo Upstash escala inesperadaBilling surpresaAlerta no Upstash dashboard > 500k cmd/dia; TTL natural limita acúmulo
Latência Upstash > 50msDegrada gatewayTelemetria sempre via waitUntil; rate limit autenticado tem timeout de 30ms com fail-open
Vazamento de prefixo (olp: em staging ou vice-versa)Contadores misturam ambientesHelper redisKey() único, prefixo lido de env.ENVIRONMENT; lint check no CI
Falha no cron de agregaçãoPerda de série temporal pós-2hCron tem retry automático; Upstash mantém 2h de buffer; perda máxima = 1h
Bloqueio falso-positivo em produçãoUsuário legítimo travadoFase 3a (observação 4 semanas) antes de qualquer bloqueio real; mensagens 429 com Retry-After claro

8. Fora de escopo desta migração

  • Token blacklist via Redis (olp:bl:{jti}) — entra em fase futura, depois que Worker v3 estiver estável.
  • Sessões ativas em Redis — depende de blacklist.
  • Drop das tabelas portal_rate_metrics e token_blacklist no Postgres — só após Worker v3 absorver totalmente as funções equivalentes.
  • Interface de observabilidade dedicada — só construída quando uma das três condições objetivas for verdadeira (ver WORKER_TELEMETRY.md §7).