Load Balancing, Cache e Redundância de Banco de Dados — Cloudflare Stack
Command Agendamento • Fevereiro 2026| Componente | Tecnologia | Plano Atual |
|---|---|---|
| API / Backend | Cloudflare Workers + Hono 4.x | Free / Paid ($5/mês) |
| Banco Principal (Grades/Slots) | Cloudflare D1 — command-agendamento-db |
Free / Paid |
| Banco Histórico (Agendamentos) | Cloudflare D1 — command-agendamento-historico |
Free / Paid |
| Banco Usuários (Pacientes/Admin) | Cloudflare D1 — command-agendamento-usuarios |
Free / Paid |
| Banco Parâmetros (Config) | Cloudflare D1 — command-agendamento-parametros |
Free / Paid |
| Storage de Arquivos | Cloudflare R2 — command-agenda |
Free (10GB) |
| Frontend ADM | Cloudflare Pages + React 18 + Vite | Free |
| Frontend Paciente | Cloudflare Pages + React 18 + Vite | Free |
| Documentação | Cloudflare Pages (HTML estático) | Free |
| WhatsApp (OTP/Notificações) | Whapi.cloud (API externa) | Variável |
| Mailjet (API externa) | Free / Paid |
Cloudflare Workers já funciona como um load balancer global por design:
O serviço de Load Balancing da Cloudflare seria relevante apenas em cenários onde:
| Recurso | Custo | Necessário Hoje? |
|---|---|---|
| Load Balancing (básico) | $5/mês + $0.50/500K health checks | NÃO |
| Load Balancing (multi-region) | $5/mês + $5/origin | NÃO |
| Health Checks | Inclusos no Workers | JÁ TEMOS |
API nativa do Cloudflare Workers que permite armazenar respostas HTTP no edge mais próximo do usuário.
| Característica | Detalhe |
|---|---|
| Custo | GRÁTIS — incluído no Workers |
| Escopo | Por data center (não replica globalmente) |
| TTL | Controlado via Cache-Control headers |
| Invalidação | Manual via cache.delete() |
| Restrição | Funciona apenas em domínios customizados (não *.workers.dev) |
Endpoints ideais para Cache API:
| Endpoint | TTL Sugerido | Motivo |
|---|---|---|
GET /api/especialidades |
1 hora | Raramente muda |
GET /api/unidades |
1 hora | Raramente muda |
GET /api/consultorios |
30 min | Baixa frequência de mudança |
GET /api/tipos-slot |
1 hora | Configuração estática |
GET /api/medicos |
15 min | Pode mudar com cadastros |
GET /api/medicos/:id |
10 min | Dados do médico individual |
GET /api/cotas |
30 min | Cotas por especialidade |
GET /api/slots/disponiveis |
2 min | Muda com agendamentos |
Exemplo de implementação:
Invalidação do cache ao modificar dados:
Key-Value store global com replicação eventual. Ideal para dados que precisam de cache consistente em todas as regiões.
| Característica | Detalhe |
|---|---|
| Custo (Free) | 100K leituras/dia, 1K escritas/dia, 1GB storage |
| Custo (Paid) | 10M leituras/mês incluídas, +$0.50/milhão |
| Escopo | Global — replica automaticamente em todos os edge nodes |
| Consistência | Eventual (até 60 segundos para propagar) |
| TTL | Configurável por chave (expirationTtl) |
| Tamanho máximo do valor | 25MB por chave |
Casos de uso recomendados para KV:
Configuração no wrangler.toml:
Exemplo de uso — Cache de Especialidades:
Os frontends em Cloudflare Pages já possuem cache automático para assets estáticos (JS, CSS, imagens). O CDN Cloudflare distribui automaticamente em todos os PoPs.
Fotos de médicos servidas via R2 público já se beneficiam do CDN Cloudflare. Para otimizar:
Cache-Control: public, max-age=86400 nos uploads para cache de 24hO Cloudflare D1 oferece Read Replication nativo, que cria cópias somente-leitura do banco em múltiplas regiões do mundo.
| Recurso | Detalhe |
|---|---|
| Custo | GRÁTIS — sem custo extra por réplicas |
| Regiões | ENAM, WNAM, WEUR, EEUR, APAC, OC (6 regiões globais) |
| Consistência | Sequencial (via Sessions API com bookmarks) |
| Escritas | Sempre redirecionadas ao primary |
| Leituras | Servidas pela réplica mais próxima |
| API | env.DB.withSession() |
Como habilitar (Dashboard):
Ir para dash.cloudflare.com → Workers & Pages → D1
Selecionar command-agendamento-db, ir em Settings →
Enable Read Replication
Habilitar em: DB, DB_HISTORICO, DB_USUARIOS, DB_PARAMETROS
Como habilitar (REST API):
Adaptação do código — Sessions API:
| Rota / Operação | Tipo de Session | Motivo |
|---|---|---|
GET /api/especialidades |
withSession() |
Dados estáticos, réplica OK |
GET /api/unidades |
withSession() |
Dados estáticos, réplica OK |
GET /api/medicos |
withSession() |
Lista geral, tolera lag |
GET /api/slots/disponiveis |
withSession("first-primary") |
Precisa estar atualizado |
POST /api/agendamentos |
Primary direto | Escrita, vai ao primary |
GET /api/agendamentos/meus |
withSession(bookmark) |
Após agendar, precisa ver imediato |
GET /api/backlog |
withSession(bookmark) |
Após alocar, precisa refletir |
POST /api/auth-paciente/login |
Primary direto | Escrita de sessão |
GET /api/dashboard/* |
withSession() |
Dados analíticos, tolera lag |
O D1 oferece Time Travel, permitindo restaurar o banco para qualquer ponto nos últimos 30 dias (Paid plan).
| Plano | Retenção | Custo |
|---|---|---|
| Free | Nenhum | Grátis |
| Paid ($5/mês Workers) | 30 dias de histórico | Incluído |
Criar um Worker com Cron Trigger que exporta os dados do D1 para R2 diariamente:
Custo ZERO, ganho imediato de latência. Ativar no dashboard em 5 minutos.
Proteção essencial contra abuse. Requer Workers KV.
Grátis, reduz 80% das queries D1 para endpoints de leitura.
Cache global para config e sessões. Custo baixo.
Cron Worker + R2 para dumps diários.
Observabilidade completa para troubleshooting.
Dashboard → D1 → Cada banco → Settings → Enable Read Replication
Tempo: 10 minutos | Custo: $0 | Risco: Nenhum
Atualizar todas as queries de leitura para usar withSession()
Tempo: 2-4 horas | Custo: $0 | Risco: Baixo (testar em staging)
Tempo: 5 minutos | Custo: Incluído no plan | Risco: Nenhum
Criar middleware rateLimit.js e aplicar nas rotas críticas (auth, OTP,
agendamentos)
Tempo: 2 horas | Custo: $0 (KV Free) | Risco: Baixo
Criar middleware cacheMiddleware e aplicar nos endpoints de leitura de
parâmetros
Tempo: 3-4 horas | Custo: $0 | Risco: Médio (requer domínio custom)
Nas rotas de escrita (POST/PUT/DELETE), invalidar cache das rotas de leitura correspondentes
Tempo: 2-3 horas | Custo: $0 | Risco: Médio (testar invalidação)
Implementar getWithCache() para especialidades, unidades, tipos de slot e
configurações
Tempo: 3 horas | Custo: ~$0 (dentro do Free) | Risco: Baixo
O Cloudflare D1 tem um limite de armazenamento (soft limit de 10GB no Beta/Paid atual). Caso o banco cresça além disso, temos 3 estratégias:
Mover dados históricos (agendamentos > 2 anos, logs antigos) para arquivos Parquet/JSON no R2. O R2 é infinitamente escalável e muito mais barato ($0.015/GB).
Custo: Baixíssimo. Esforço: Médio (Worker de arquivamento).
Criar múltiplos bancos D1 (ex: db_2026, db_2027) e rotear no
Worker. A conta Cloudflare suporta até 50.000 bancos D1.
Custo: $0 (pagamento por uso). Esforço: Alto (lógica de roteamento).
Migrar o banco principal para um serviço Postgres serverless especializado (Neon Tech ou Supabase) e manter o Worker como API Proxy via Hyperdrive (aceleração de conexão).
Custo: ~$19-50/mês. Esforço: Médio (driver Postgres no código).
Criar backup-worker com Cron Trigger (diário às 03:00 UTC) que exporta todos os 4 bancos para R2
Tempo: 3-4 horas | Custo: ~$0 (R2 Free tier cobre) | Risco: Nenhum
Configurar Workers Logs + notificações via email/WhatsApp para erros 5xx
Tempo: 2 horas | Custo: Incluído no Paid | Risco: Nenhum
Criar índices nas colunas mais consultadas para reduzir rows_read e custo
Tempo: 1 hora | Custo: Insignificante (mais writes no index) | Risco: Baixo
| Risco | Severidade | Probabilidade | Impacto |
|---|---|---|---|
| Denial-of-Wallet (sem rate limiting) | CRÍTICO | Média | Atacante gera milhões de requests, custo inesperado na fatura |
| Brute-force no OTP/Login | ALTO | Alta | Atacante tenta todas as combinações de OTP (6 dígitos = 1M tentativas) |
| Sem backup manual/automatizado | MÉDIO | Baixa | Perda de dados se D1 Time Travel falhar ou erro humano |
| Latência alta para usuários distantes | MÉDIO | Variável | Todas as queries vão ao primary (provavelmente US), latência alta para BR |
| Sem cache = mais rows_read = mais custo | MÉDIO | Alta | Cada refresh de página gera 5-10 queries ao D1 |
| Componente | Risco | Severidade | Mitigação |
|---|---|---|---|
| D1 Read Replication | Replica Lag — leitura pode retornar dados desatualizados | MÉDIO | Usar withSession("first-primary") para queries que exigem dados mais
recentes (ex: após criar agendamento). Sessions API garante consistência sequencial.
|
| Cache API | Stale data — cache não invalidado após mutação | MÉDIO | Implementar invalidação explícita em todas as rotas de escrita. Usar TTLs curtos (2-5 min) para dados voláteis. |
| Cache API | Requer domínio customizado (não funciona em *.workers.dev) |
BAIXO | Configurar domínio custom para a API (ex: api.commandagenda.com.br).
Cloudflare DNS é grátis. |
| Workers KV | Consistência eventual (até 60s de delay na propagação global) | BAIXO | Aceitável para cache de configurações. Para sessões, usar TTL alinhado ao JWT. |
| Rate Limiting (KV) | Contadores podem ficar inconsistentes entre regiões | BAIXO | Aceitável — se um IP fizer 5 requests em 2 PoPs diferentes, pior caso são 10 requests ao invés de 5. Suficiente para prevenir abuse. |
| Backup Worker | Export via SELECT * pode ser lento em tabelas grandes | BAIXO | Paginar queries (LIMIT/OFFSET) e exportar em chunks. Volume atual é pequeno. |
| Índices D1 | Aumenta marginalmente o custo de escritas | BAIXO | Benefício em leituras (80%+ das operações) supera largamente o custo extra em escritas. Cada write adiciona ~1 row_written por index. |
| Serviço | Free Tier | Plano Paid (Incluído) | Custo Excedente |
|---|---|---|---|
| Workers | 100K req/dia | 10M req/mês + 30M CPU-ms | $0.30/milhão req + $0.02/milhão CPU-ms |
| D1 Rows Read | 5M/dia | 25 bilhões/mês | $0.001/milhão rows |
| D1 Rows Written | 100K/dia | 50M/mês | $1.00/milhão rows |
| D1 Storage | 5 GB total | 5 GB incluído | $0.75/GB-mês |
| D1 Read Replication | ✅ GRÁTIS — sem custo extra por réplicas | ||
| Workers KV Reads | 100K/dia | 10M/mês | $0.50/milhão |
| Workers KV Writes | 1K/dia | 1M/mês | $5.00/milhão |
| Workers KV Storage | 1 GB | 1 GB | $0.50/GB-mês |
| R2 Storage | 10 GB | 10 GB | $0.015/GB-mês |
| R2 Class A Ops | 1M/mês | 1M/mês | $4.50/milhão |
| R2 Class B Ops | 10M/mês | 10M/mês | $0.36/milhão |
| Pages | ✅ GRÁTIS — unlimited bandwidth | ||
| Cache API | ✅ GRÁTIS — incluído no Workers runtime | ||
~100 pacientes ativos, 10 médicos, 3 unidades, ~5K req/dia
~1.000 pacientes ativos, 50 médicos, 10 unidades, ~50K req/dia
~10.000 pacientes ativos, 200 médicos, 30 unidades, ~500K req/dia
~50.000 pacientes ativos, 1.000 médicos, 150 unidades, ~2.5M req/dia
| Métrica | SEM Cache | COM Cache | Economia |
|---|---|---|---|
| D1 Rows Read/mês | ~300M (full table scans a cada request) | ~30M (80% servido do cache) | 90% redução |
| Custo D1 Read (excedente) | $0.275 (~275M excedente) | $0.00 (dentro do incluído) | $0.275/mês |
| Latência média | 100-300ms (D1 query) | 5-20ms (cache hit) | 80-95% mais rápido |
$5 - $65
Escala automática de 100 até 50.000 usuários mensais
80-95%
Redução com cache + read replication
~4 semanas
Implementação gradual em 4 fases
| Ferramenta | Finalidade | Custo Adicional | Status |
|---|---|---|---|
| Workers | API Runtime | Já em uso | JÁ TEMOS |
| D1 | Banco de Dados (4x) | Já em uso | JÁ TEMOS |
| D1 Read Replication | Redundância + Performance | $0 | ATIVAR |
| D1 Sessions API | Consistência com réplicas | $0 | IMPLEMENTAR |
| Cache API | Cache no Edge (por data center) | $0 | IMPLEMENTAR |
| Workers KV | Cache global + Rate Limiting + Sessões | $0 (dentro do Paid) | IMPLEMENTAR |
| R2 | Storage + Backups | Já em uso | JÁ TEMOS |
| Pages | Frontends (3 sites) | Já em uso | JÁ TEMOS |
| Cron Triggers | Backup automatizado | $0 | IMPLEMENTAR |
| D1 Time Travel | Point-in-time recovery | $0 (Paid plan) | JÁ TEMOS |