Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

02 - Point-in-Time Recovery (PITR)

#pitr#recuperacao#redo-log#rpo#wal
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Point-in-Time Recovery (PITR)

Point-in-Time Recovery é a capacidade de restaurar um banco de dados para um segundo específico do passado, combinando um backup de base com a aplicação sequencial do registro de transações gerado a partir daquele backup. Em vez de aceitar a granularidade de horas de um snapshot, o PITR reduz o RPO para minutos e permite desfazer exatamente o incidente: um UPDATE sem WHERE executado às 14h07 é revertido restaurando o banco às 14h06, um segundo antes da catástrofe. Em produção, o PITR é a diferença entre perder um dia de negócio e perder cinco minutos de escritas, e é o mecanismo subjacente ao backup automatizado de serviços gerenciados como RDS, Cloud SQL e Azure Database. Seu custo não é o backup, mas a retenção contínua de logs de transação e a disciplina operacional de mantê-los arquivados, testados e recuperáveis. Dominar o ciclo de base backup, arquivamento de WAL e replay é competência central de quem opera bancos críticos.

Conceito e problema que resolve

Todo banco de dados transacional mantém um log de transações append-only: o WAL no PostgreSQL, o redo log e o binlog no MySQL, o transaction log no SQL Server. Esse log registra cada mudança física e lógica necessária para reconstruir o estado do banco, e é a matéria-prima do PITR.

O problema que o PITR resolve é a granularidade. Snapshots e dumps capturam instantes discretos, e o espaço entre eles é área de perda: qualquer escrita que aconteceu depois do último backup some no desastre. Com o log de transações arquivado continuamente, essa área se reduz ao intervalo entre a última transação commitada e o instante alvo, tipicamente segundos.

O segundo problema é o erro lógico. Um snapshot restaura o banco exatamente como ele estava, inclusive o erro. O PITR permite parar o replay um segundo antes da transação destrutiva, descartando apenas o incidente e preservando tudo o que aconteceu antes dele.

A janela de recuperação é limitada pela retenção: recuperar para ontem exige que o log de ontem esteja arquivado; recuperar para 40 dias atrás exige 40 dias de logs contínuos e um backup de base anterior ao instante alvo. O tamanho da janela é uma decisão de custo explícita, não um detalhe de configuração.

Vale distinguir o PITR físico do PITR lógico. O físico, tratado aqui, reaplica registro de transações sobre uma cópia física do diretório de dados, e é o mecanismo dos bancos gerenciados. O lógico, disponível em ferramentas de binlog do MySQL ou em replay de streams de eventos, reaplica declarações SQL capturadas, é mais flexível para filtrar transações específicas, porém mais lento e sensível a diferenças de schema e de contexto. Em produção, o físico é o padrão; o lógico é ferramenta de cirurgia em incidentes pontuais.

Funcionamento interno: base backup mais replay de WAL

O ciclo do PITR tem três peças: um backup de base físico (ou snapshot) que serve de ponto de partida, o arquivamento contínuo dos segmentos de log gerados a partir daquele backup e um processo de replay que reaplica esses segmentos em ordem até o instante alvo.

O PostgreSQL documenta esse ciclo com precisão. Em modo contínuo de arquivamento, cada segmento de WAL de 16 MB é copiado para um repositório remoto assim que é fechado, via archive_command ou archive_library. Na recuperação, o servidor parte do backup de base, baixa os segmentos arquivados e os reaplica, parando no recovery_target_time, no recovery_target_xid ou no recovery_target_lsn configurado.

O replay é idempotente em relação ao estado físico do backup: aplicá-lo duas vezes sobre o mesmo ponto de partida produz o mesmo resultado. Por isso a prática padrão é restaurar o backup de base em um diretório novo e reproduzir o log nele, sem nunca sobrescrever os dados originais até confirmar que a recuperação funcionou.

A granularidade final é o próprio limite do log. O WAL registra transações completas no momento do commit, então o PITR restaura o estado visível em um instante, transação por transação. Não existe recuperação para o meio de uma transação não commitada, e transações longas podem adiar o checkpoint de consistência durante o replay.

-- PostgreSQL: ciclo básico de PITR
-- 1. Habilita o arquivamento contínuo de WAL
ALTER SYSTEM SET archive_mode = 'on';
ALTER SYSTEM SET archive_command =
  'aws s3 cp %p s3://meu-bucket-wal/%f';   -- cada segmento fechado vai para o S3
ALTER SYSTEM SET wal_level = 'replica';     -- necessário para backup físico

-- 2. Tira o backup de base (com pg_basebackup, guarda o label gerado)
--    pg_basebackup -D /backup/base --wal-method=stream --checkpoint=fast

-- 3. No dia do desastre: restaura a base, cria o sinal de recuperação
--    e define o alvo, um segundo antes do incidente
--    Em restore_conf: restore_command = 'aws s3 cp s3://meu-bucket-wal/%f %p'
--    recovery_target_time = '2024-05-10 14:06:30-03'
--    recovery_target_action = 'pause'   -- pausa o replay para validação

Janela de retenção e custo contínuo

A retenção de logs contínuos é o custo estrutural do PITR, e ele cobra todos os dias, não apenas no desastre. Cada segmento arquivado ocupa espaço no repositório pelo período inteiro da janela configurada, e o volume gerado cresce com a taxa de escritas do banco, não com o tamanho dos dados.

Um banco de 200 GB pode gerar 5 GB ou 500 GB de WAL por dia, dependendo da intensidade de escritas e de operations como vacuum e index rebuild. Dimensionar a janela sem medir o volume real de WAL é a receita para faturas surpresa ou para janelas truncadas sem aviso.

A prática de dimensionamento mede três números: taxa média e de pico de geração de WAL por hora, tamanho do backup de base e janela de retenção desejada. O custo total é aproximadamente o backup de base mais a soma do WAL da janela, com compressão aplicada quando o repositório suporta.

Janela configurada Custo relativo de storage RPO efetivo Capacidade de desfazer erro lógico
24 horas Baixa Minutos Último dia de operação
7 dias Moderada Minutos Uma semana de histórico
35 dias Alta Minutos Padrão de serviços gerenciados
90 dias ou mais Muito alta, considerar comprimir Minutos Compliance e casos legais

A camada de storage do repositório também é decisão arquitetural: logs antigos raramente são acessados, e transição automática para armazenamento mais barato (classe de acesso infrequente) corta o custo sem mudar o contrato de recuperação.

Granularidade e escolha do alvo

O alvo de recuperação pode ser expresso de três formas, cada uma com um caso de uso dominante. Escolher o alvo correto no meio do incidente exige que essas opções já estejam ensaiadas.

  • Timestamp (recovery_target_time): recupera para um instante de relógio, útil quando se sabe a hora aproximada do incidente; cuidado com fusos e com relógios de servidor dessincronizados.
  • Transação (recovery_target_xid ou nome de transação): recupera até uma transação específica, útil quando o log de auditoria identificou a operação destrutiva exata.
  • Posição no log (recovery_target_lsn ou nome de posição no binlog): recupera para uma marca física, a forma mais precisa e a usada por ferramentas de replay assistido.

A prática de incidente recupera primeiro em modo pause (o replay para no alvo e o banco fica em leitura somente leitura ou espera decisão), valida o estado restaurado contra a expectativa do negócio e só então promove a instância a primária. recovery_target_inclusive controla se a transação do alvo entra ou não no resultado.

Exercício de recuperação: o runbook mínimo

PITR não configurado para teste é teoria. O exercício a seguir é o roteiro que valida a capacidade real, e executá-lo periodicamente em ambiente isolado é o que transforma arquivos arquivados em garantia de recuperação.

# Runbook de teste de PITR em PostgreSQL, ambiente isolado
# 1. Provisiona volume novo e descompacta o backup de base escolhido
pg_ctl -D /restore/pgdata stop 2>/dev/null || true
rm -rf /restore/pgdata && mkdir -p /restore/pgdata
tar -xzf /backup/base/base_20240510_0400.tar.gz -C /restore/pgdata

# 2. Limpa artefatos que não devem ser reaproveitados da base
rm -f /restore/pgdata/postmaster.pid /restore/pgdata/postmaster.opts

# 3. Configura a recuperação: de onde buscar WAL e onde parar
cat > /restore/pgdata/restore_conf <<'EOF'
restore_command = 'aws s3 cp s3://meu-bucket-wal/%f %p'
recovery_target_time = '2024-05-10 14:06:30-03'
recovery_target_action = 'pause'
EOF

# 4. Inicia o servidor e observa o log até o replay pausar no alvo
pg_ctl -D /restore/pgdata start
tail -f /restore/pgdata/log/postgresql.log   # aguarda "pausado no alvo"

# 5. Valida dados críticos, promove e redireciona a aplicação
SELECT count(*), max(updated_at) FROM pedidos;   -- validação de negócio
SELECT pg_wal_replay_resume();                   -- retoma e conclui
SELECT pg_promote();                             -- torna-se primária

Cada execução do exercício deve registrar o tempo total do início ao primário em pé, porque esse número, e não a suposição de arquiteto, é o RTO real do PITR. Replay de horas de WAL acumulado é o componente dominante do tempo, e bases com alto volume de escritas podem exigir paralelismo ou alvos mais recentes para caber no RTO.

Comparações e posição na estratégia de backup

O PITR não substitui as outras camadas, ele as completa. A tabela abaixo posiciona cada mecanismo pela granularidade e pelo custo, e a combinação de produção típica usa os três com papéis distintos.

Mecanismo Granularidade de recuperação Custo contínuo Tempo de recuperação típico
Snapshot Instante discreto, horas entre capturas Baixo, incremental Minutos a horas
Dump lógico Instante discreto, granular por tabela Médio, proporcional aos dados Horas em bases grandes
PITR Segundo específico dentro da janela Alto, WAL contínuo + base Base + replay, horas possíveis

A divisão clássica de papéis: o snapshot diário é a âncora de cadeia que limita o volume de replay; o PITR é a camada de granularidade fina para erros lógicos e para o RPO de minutos; o dump lógico semanal é a apólice para corrupção de longo prazo e migrações de versão. O arquivo seguinte mostra como o RDS empacota exatamente essa combinação como serviço gerenciado.

Erros comuns e boas práticas

  • Configurar archive_command sem monitorar se ele falha; um bucket cheio ou credencial expirada quebra o arquivamento em silêncio e só aparece no desastre, alertar sobre filas de arquivamento pendentes é obrigatório.
  • Manter a janela de retenção no repositório, mas deletar backups de base antigos; sem base anterior ao instante alvo, o WAL daquele período é inútil, alinhar expirações de base e de log.
  • Usar horário local em recovery_target_time sem registrar o fuso; documentar o alvo sempre em UTC e registrar o relógio do servidor nos runbooks.
  • Testar apenas a captura e nunca a recuperação; o teste mensal de PITR em ambiente isolado é o único indicador honesto de RPO e RTO.
  • Arquivar WAL no mesmo domínio de falha do banco; o repositório precisa estar em outra conta, região ou storage com política de imutabilidade, senão o desastre leva os dois.
  • Deixar transações longas inflarem o WAL e os atrasos de arquivamento; monitorar lag de archiving e tamanho de transações como parte da observabilidade de backup.
  • Ignorar a diferença entre RPO prometido e RPO real; medir o intervalo máximo observado entre transações commitadas e segmento arquivado, e reportá-lo junto com o SLO de backup.
  • Promover a instância recuperada a primária sem revogar acessos que existiam no instante restaurado; se o incidente envolveu credencial comprometida, restaurar o banco para 14h06 restaura também o acesso do atacante, rodar rotação de credenciais como passo do runbook.