Pular para o conteúdo principal

Replication — CDC Postgres → Mongo/MySQL

A partir da v4.5, o CipherVault suporta Change Data Capture (CDC) replication do Postgres primário para sinks externos (MongoDB / MySQL). Use case: dashboards analíticos / data lakes / sistemas legados que precisam dos eventos do CV em tempo quase-real sem expor Postgres diretamente.

Replication ≠ Multi-region. Multi-region usa Postgres logical replication entre instâncias CV. Replication aqui é para sinks externos (não-CV).

Architecture (Opção B — External CDC)

Após análise de 3 opções (A: logical decoding, B: external CDC, C: WAL-shipping), foi escolhida B:

┌─────────────┐ poll ┌─────────────────┐ write ┌──────────┐
│ Postgres CV │ ───────────▶ │ CDC connector │ ─────────────▶ │ MongoDB │
│ (primary) │ `cdc_state` │ background │ │ (sink) │
│ │ table │ job no CV │ └──────────┘
└─────────────┘ └─────────────────┘ ┌──────────┐
│ MySQL │
│ (sink) │
└──────────┘

Trade-off escolhido: simplicidade operacional (zero binding C nativo) ao custo de latência ~5-30s (vs. ms de logical decoding).

Phase 1 — CDC core (entregue)

Background job poll cdc_state (cursor por sink) → busca rows em 4 tabelas whitelistadas:

TabelaReplicadaComentário
secrets✅ (redacted)Sem value (apenas metadata + hash)
vaultsCompleta
audit_logsCompleta
risk_scoresCompleta

Não replicadas: AppConnections (cert privado), dynamic_leases (region-local), fortress_* (segregação), eaas_*/pki_*/ssh_* (chaves privadas).

Phase 2 — Failover state machine (entregue)

Sinks podem ser promotidos se primary cair (DR scenario):

┌────────┐ promote ┌──────────┐
│ mirror │ ─────────▶ │ promoted │
└────────┘ └────┬─────┘
▲ │ demote (após primary recovery)
│ ▼
└─────────────── ┌──────────┐
│ demoted │
└──────────┘

Split-brain auto-detect: se 2 sinks reportam promoted simultaneamente, flag de alerta crítico + audit log severity=critical.

5 endpoints admin:

POST /replication/sinks Criar sink
GET /replication/sinks Listar
PATCH /replication/sinks/:id Update config
POST /replication/sinks/:id/promote Promote (dual-control)
POST /replication/sinks/:id/demote Demote (dual-control)

Promote/demote gated por dual-control (Approvals action replication_state_change).

Phase 3 — Storage abstraction (PoC, deferred)

Foundation only. AuditLogRepository interface + Postgres/MongoDB impls. Refactor full (200 call sites no backend) requer 6-10 sprints — postergado para v5+.

Por enquanto, replication funciona com Postgres como source of truth universal; sinks são read-only mirrors.

Configuração

Criar sink MongoDB

curl -X POST https://cv.acme.com.br/replication/sinks \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{
"name": "analytics-mongo",
"type": "mongodb",
"connection": {
"uri": "mongodb://cv_replicator:***@mongo-analytics.acme.com.br:27017/ciphervault",
"database": "ciphervault",
"tls": true
},
"tables": ["secrets", "vaults", "audit_logs", "risk_scores"],
"poll_interval_seconds": 30,
"batch_size": 1000,
"state": "mirror"
}'

Sink MySQL

{
"type": "mysql",
"connection": {
"host": "mysql-analytics.acme.com.br",
"port": 3306,
"user": "cv_replicator",
"password_secret": "internal/db/mysql-replicator",
"database": "ciphervault",
"ssl_ca": "<PEM>"
},
...
}

Monitoring

MétricaTipoLabels
cv_replication_lag_secondsgaugesink_id, sink_name, table
cv_replication_rows_replicated_totalcountersink_id, table, status
cv_replication_stategaugesink_id (0=mirror, 1=promoted, -1=demoted)
cv_replication_split_brain_detectedcountersink_id

Alerta crítico: cv_replication_lag_seconds > 300 (5min).

Recovery após falha primary

# 1. Confirmar primary realmente down (não bug de monitoramento)
curl --max-time 5 https://cv.acme.com.br/health

# 2. Promote sink
curl -X POST https://cv.acme.com.br/replication/sinks/12/promote \
-d '{ "reason": "Primary down 2026-05-12T14:00 — DR drill / incidente real" }'
# → 202, aguardando dual-control

# 3. Após approvals, sink vira source of truth read+write
# Apps continuam apontando para o endpoint normal (CV-frontend)
# que agora roteia via sink promoted

# 4. Quando primary volta:
curl -X POST https://cv.acme.com.br/replication/sinks/12/demote \
-d '{ "reason": "Primary recovered" }'

Limitações conhecidas

  • Latência 5-30s entre escrita no primary e disponibilidade no sink (poll-based, não streaming)
  • Sinks são read-only em estado mirror — writes vão direto para primary
  • Sem suporte a schema migration cross-version — sink precisa ter mesma estrutura de tabelas do primary (esquema versionado)
  • Phase 3 storage abstraction — sem ela, apps que querem usar sink como primary precisam adaptar (não há driver "magicamente" usando sink)

Boas práticas

  • Comece com audit_logs apenas — tabela mais útil para analytics, sem PII relevante
  • TLS obrigatório para conexão CV → sink
  • Credenciais de replicator com permissão mínima (INSERT, UPDATE, DELETE apenas)
  • Backup do sink também — não substitui backup do primary mas adiciona camada
  • Alerta em split_brain_detected — incidente que exige resposta imediata

Referências

  • docs/REPLICATION.md no repo do produto — design doc completo
  • Postgres logical replication para Multi-region (caso diferente)