Pular para o conteúdo principal

Multi-region active-active

A partir da v4.2, o CipherVault suporta deploy active-active em múltiplas regiões com Postgres logical replication, region-aware routing e CRDTs para metadata cross-region.

Topologia

┌──────────────────────────────────────┐
│ DNS / GeoDNS │
│ cv.acme.com.br → região mais próxima│
└─────────────┬────────────────────────┘

┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ sa-east-1│ │ us-east-1│ │ eu-west-1│
│ CV+PG │ │ CV+PG │ │ CV+PG │
└────┬─────┘ └────┬─────┘ └────┬─────┘
└───────── PG logical replication ──┘
└────────── CRDT sync (counters) ───┘

Cada região tem seu próprio backend CipherVault + Postgres. Replicação é bidirecional via logical replication (pub/sub).

Componentes

1. tenant_regions — fonte da verdade

Tabela com tenant_id PK, primary_region, replica_regions[]. Cada operação consulta para decidir se redireciona ou processa local.

# Configurar tenant
curl -X PUT https://cv.acme.com.br/admin/tenant-region/42 \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{
"primary_region": "sa-east-1",
"replica_regions": ["us-east-1", "eu-west-1"]
}'

2. requireRegionForwarding middleware

Aplicado em app.js após auth. Comportamento:

  • tenant_id do JWT/AppConnection
  • Consulta tenant_regions (cache 60s TTL)
  • Se região atual ≠ primary, retorna 307 com Location apontando para a região primária
  • Loop detection via header X-CV-Region-Origin — se já bate com região atual, processa local mesmo em mismatch (failover scenario)

Endpoints excluídos (region-local, processados independente):

  • /auth — login pode acontecer em qualquer região
  • /admin/tenant-region — config global
  • /attestation — local
  • /crdt/sync — peer-to-peer
  • /clusters — federation, region-local
  • /health, /metrics — operacional

3. Postgres logical replication

infrastructure/multi-region/setup-replication.sql configura pub/sub para 24 tabelas. Excluídas:

TabelaMotivo
audit_logsCRDT counter cobre métrica agregada cross-region
dynamic_leasesRegion-local (lease só faz sentido na região onde DB upstream está)
crdt_statesSync próprio via syncer (não via PG replication)

4. CRDTs para metadata cross-region

5 tipos completos em lib/crdt/index.js:

TipoQuando usar
GCounterContadores monotônicos (audit_count, leases_issued_total)
PNCounterContadores que sobem e descem (active_sessions)
GSetConjuntos que só crescem (regions_seen)
LWWRegisterÚltima escrita ganha (config flags)
ORSetConjuntos com add+remove concurrent (tags)

Background syncer push 5s para peers via CRDT_PEERS env. Cada peer expõe POST /crdt/sync (auth via X-CRDT-Sync-Token).

Convergência: total cross-region em < 10s.

Failover

Failover de region primária é gated por dual-control (Approvals) porque é mudança de alta criticidade:

# Tenant 42 — primary atual: sa-east-1, replicas: [us-east-1, eu-west-1]
curl -X POST https://cv.acme.com.br/admin/tenant-region/42/promote \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{
"new_primary_region": "us-east-1",
"reason": "DR drill 2026-Q2 + sa-east-1 outage simulada"
}'

# Retorna 202 — aguardando 2 aprovações em /Approvals

Action: tenant_region_promote (ver Approvals).

Após N aprovações (default 2), o promote acontece atomicamente:

  1. UPDATE tenant_regions SET primary_region = 'us-east-1' WHERE tenant_id = 42
  2. Cache regional invalidado em todas regiões via broadcast
  3. Próximas requests redirecionam para nova primary
  4. Audit log: tenant_region_promoted severity high

Runbook completo: docs/runbooks/REGIONAL_FAILOVER.md no produto.

Métricas

MétricaTipoLabels
cv_cross_region_forwards_totalcounterfrom, to, status
cv_cross_region_forward_duration_secondshistogramfrom, to
cv_replication_lag_secondsgaugereplica_application_name
cv_crdt_sync_totalcounterpeer, result
cv_crdt_state_size_bytesgaugetenant_id, name, type

cv_replication_lag_seconds é populada via pg_stat_replication. Alerta crítico se > 60s.

Configuração

Backend env vars

# Region atual (obrigatório em multi-region)
CV_REGION=sa-east-1

# Endpoints dos peers para CRDT sync (uma URL por peer, separados por vírgula)
CRDT_PEERS=https://cv-us.acme.com.br,https://cv-eu.acme.com.br

# Token de auth entre peers (compartilhado, igual em todas regiões)
CRDT_SYNC_TOKEN=<token aleatório>

# Loop detection — não modificar (header padrão)
# X-CV-Region-Origin é setado automaticamente em forwards

DNS / GeoDNS

Recomendado: Route 53 latency-based routing ou GeoDNS aponta o hostname cv.acme.com.br para a região mais próxima do client. O middleware redireciona quando o tenant tem primary diferente.

Alternativa simples: subdomínios por região (cv-sa., cv-us., cv-eu.) sem GeoDNS, app escolhe explicitamente qual usar.

Boas práticas

  • Postgres logical replication exige wal_level=logical — ajuste em postgresql.conf antes do setup
  • Monitor cv_replication_lag_seconds — alerta crítico em > 60s
  • DR drill trimestral — promote para region replica, valide RPO/RTO, depois reverta
  • CRDT_SYNC_TOKEN rotacionar a cada 6 meses
  • Excluir SIEM forwarding multi-region — configure SIEM para coletar de todas regiões diretamente, evita duplicação de events
  • Tenants region-pinned para LGPD — clientes BR ficam em sa-east-1 apenas se exigência legal de dado em território nacional