Solução de Problemas Comuns de Failover e Erros de Conexão em Clusters PostgreSQL HA

Navegue e resolva problemas comuns de failover e conexão em clusters PostgreSQL de alta disponibilidade. Este guia abrangente aborda desafios como falhas de reconexão de aplicativos através de poolers de conexão, lag excessivo de réplicas e transições de primário travadas. Aprenda técnicas práticas de depuração usando `pg_stat_replication`, `patronictl` e ferramentas de rede. Descubra soluções acionáveis, melhores práticas de configuração e estratégias essenciais de monitoramento para garantir transições primárias automatizadas suaves e conectividade contínua de aplicativos em seu cluster PostgreSQL HA.

Solução de Problemas Comuns de Failover e Erros de Conexão em Clusters PostgreSQL HA

Clusters PostgreSQL de alta disponibilidade (HA) falham de duas maneiras diferentes. Às vezes, o próprio failover do banco de dados quebra: nenhuma réplica é promovida, dois nós discordam sobre o primário, ou o novo primário não consegue aceitar gravações. Outras vezes, o banco de dados está funcionando, mas o aplicativo ainda não consegue conectar porque um pooler, registro DNS, IP virtual, regra de firewall ou loop de repetição do cliente não acompanhou a promoção.

Quando você está no incidente, separe essas camadas rapidamente. Primeiro pergunte: existe exatamente um primário gravável? Depois pergunte: o pooler consegue alcançá-lo? Então pergunte: o aplicativo consegue alcançar o pooler ou o endpoint de serviço? Essa ordem evita muito tempo perdido olhando logs de aplicativos enquanto o gerenciador de cluster ainda está travado na eleição de líder.

Entendendo os Fundamentos do PostgreSQL HA

Antes de mergulhar na solução de problemas, é essencial recapitular brevemente os componentes principais de um cluster PostgreSQL HA:

  • Arquitetura Primário/Réplica: Um banco de dados primário lida com todas as operações de gravação, enquanto uma ou mais réplicas recebem alterações de forma assíncrona ou síncrona via replicação de streaming. As réplicas são somente leitura, mas servem como candidatas para promoção durante um failover.
  • Gerenciador de Failover: Ferramentas como Patroni, pg_auto_failover ou Corosync/Pacemaker monitoram a saúde do primário, detectam falhas, elegem um novo primário entre as réplicas disponíveis e gerenciam o processo de promoção. Eles também lidam com a reconfiguração de outras réplicas para seguir o novo primário.
  • Pooling de Conexão: Os aplicativos geralmente se conectam a um pooler de conexão PostgreSQL (ex.: PgBouncer, Odyssey) em vez de diretamente ao banco de dados. O pooler então roteia as consultas para o primário atual, fornecendo multiplexação de conexão, balanceamento de carga e, potencialmente, abstraindo o endereço de rede real do primário dos aplicativos. Essa abstração é crucial durante o failover.

Problemas Comuns de Failover e Conexão e Suas Soluções

1. Falhas no Pooling de Conexão Durante o Failover

Um dos problemas pós-failover mais frequentes é a falha dos aplicativos em se reconectar ao primário recém-promovido, apesar do banco de dados em si estar operacional. Isso geralmente aponta para problemas com o pooler de conexão ou cache do lado do cliente.

Sintomas do Problema:

  • Os aplicativos relatam erros de conexão com o banco de dados (FATAL: database "mydb" does not exist, connection refused, server closed the connection unexpectedly).
  • Conexões existentes através do pooler parecem travadas ou tentam conectar ao IP do primário antigo.
  • Novas conexões também falham, mesmo após o failover estar completo.

Causas Subjacentes:

  • Conexões Obsoletas no Pooler: O pooler de conexão pode manter conexões abertas para o antigo primário e tentar reutilizá-las, levando a erros quando o primário antigo está inativo ou agora é uma réplica.
  • Configuração Incorreta do Pooler: O pooler pode não estar configurado para detectar e alternar corretamente para o novo primário, ou seu server_reset_query pode estar ausente/incorreto.
  • Cache DNS: Se seus aplicativos ou pooler usam uma entrada DNS para resolver o endereço do primário, entradas de cache DNS obsoletas (localmente ou no nível do resolvedor DNS) podem fazê-los continuar tentando conectar ao IP antigo.
  • Falta de Lógica de Repetição no Lado do Cliente: Os aplicativos podem não ser construídos com mecanismos robustos de repetição para lidar com problemas transitórios de conexão durante um failover.

Etapas de Depuração:

  1. Verifique o Status do Pooler: Acesse o console do seu pooler (ex.: psql -p 6432 pgbouncer -U pgbouncer) e verifique a saída de SHOW SERVERS, SHOW CLIENTS, SHOW DATABASES para ver se ele está ciente do novo primário e se tem conexões ativas para o endereço correto.
  2. Verifique a Conectividade de Rede: Do host do pooler, execute ping e telnet para a porta PostgreSQL do novo primário (telnet new_primary_ip 5432).
  3. Inspecione os Logs do Pooler: Revise os logs do pooler em busca de mensagens de erro relacionadas à conexão com o banco de dados ou tentativas de resolver nomes de host.
  4. Verifique a Resolução DNS: Use dig ou nslookup no host do pooler para garantir que o registro DNS para o endpoint do seu serviço primário (ex.: primary.mydomain.com) resolva para o endereço IP do novo primário.

Soluções:

  • Configure server_reset_query: Certifique-se de que seu pooler tenha um server_reset_query (ex.: DISCARD ALL;) para limpar o estado da sessão quando uma conexão é retornada ao pool e antes de ser reutilizada por outro cliente. Isso é crucial para ambientes que usam objetos temporários ou configurações específicas de sessão.
  • max_db_connections e max_user_connections: Defina limites apropriados para evitar que o pooler monopolize todas as conexões para o novo primário, potencialmente privando outros serviços.
  • Recarregar/Reiniciar Pooler: Em alguns casos, um recarregamento ou reinicialização graciosa do pooler de conexão pode ser necessário para forçá-lo a pegar novas configurações ou resolver novamente o DNS. Isso deve ser um último recurso e, de preferência, automatizado pelo seu gerenciador de failover.
  • TTL de DNS Mais Curto: Se estiver usando descoberta de serviço baseada em DNS, configure um Time-To-Live (TTL) muito curto para o registro DNS do primário (ex.: 30-60 segundos) para minimizar o impacto do cache DNS.
  • Repetições no Lado do Cliente: Implemente lógica de repetição com backoff exponencial no código do seu aplicativo. Isso torna os aplicativos mais resilientes a problemas transitórios de conexão durante o failover.
  • IP Virtual (VIP): Considere usar um IP Virtual gerenciado pela sua solução de HA. O VIP se move com o primário, então os aplicativos se conectam a um IP estático, e o servidor de banco de dados subjacente muda de forma transparente.
# Exemplo de Trecho de Configuração do PgBouncer
[databases]
mydb = host=primary_cluster_service_ip port=5432 dbname=mydb
# Ou usando um hostname que é atualizado pelo seu gerenciador de failover
# mydb = host=primary.mydomain.com port=5432 dbname=mydb

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = session
server_reset_query = DISCARD ALL;
server_fast_close = 1 # Fecha conexões rapidamente se o servidor não responder
server_check_delay = 10 # Verifica a saúde do servidor a cada 10 segundos

2. Lag Excessivo de Réplica Dificultando o Failover

O lag de réplica ocorre quando um banco de dados standby fica atrás do primário, significando que ele não reproduziu todos os registros WAL (Write-Ahead Log) enviados pelo primário. Durante um failover, promover uma réplica com muito lag pode levar à perda de dados ou atrasar significativamente o processo de failover se o gerenciador de HA esperar que ela alcance o primário.

Sintomas do Problema:

  • Alertas de monitoramento indicam alto lag de réplica (ex.: em bytes ou tempo).
  • Gerenciadores de failover se recusam a promover uma réplica devido ao excesso de um limite de lag configurado.
  • Após o failover, os aplicativos observam dados ausentes que estavam presentes no primário antigo.

Causas Subjacentes:

  • Alta Carga de Gravação no Primário: Um volume sustentado alto de operações de gravação no primário pode sobrecarregar a capacidade da réplica de acompanhar, especialmente se o hardware da réplica (E/S, CPU) for inferior.
  • Latência/Largura de Banda de Rede: Links de rede lentos ou congestionados entre o primário e a réplica podem atrasar o envio de WAL.
  • E/S Lenta da Réplica: O subsistema de disco da réplica pode não ser rápido o suficiente para escrever e reproduzir registros WAL de forma eficiente.
  • Configuração wal_level: Se wal_level não estiver definido como replica ou superior, as informações necessárias para a replicação de streaming não serão geradas.
  • max_wal_senders: max_wal_senders insuficiente no primário pode limitar o número de slots de replicação ativos ou conexões de replicação concorrentes, impactando a taxa de transferência.
  • Problemas com archive_command / restore_command: Se estiver usando arquivamento e recuperação WAL, problemas com esses comandos (ex.: armazenamento de arquivo lento) podem causar atrasos.

Etapas de Depuração:

  1. Monitore pg_stat_replication: Esta visão fornece informações em tempo real sobre o status da replicação, incluindo write_lag, flush_lag e replay_lag.
  2. Compare LSNs: Compare manualmente o LSN WAL atual no primário com o último LSN reproduzido na réplica.
  3. Verifique Recursos do Sistema: Use iostat, vmstat, top tanto no primário quanto na réplica para identificar gargalos de E/S, saturação de CPU ou pressão de memória.
  4. Diagnósticos de Rede: Teste o desempenho da rede entre o primário e a réplica usando iperf.

Soluções:

  • Aumente max_wal_senders: No primário, aumente max_wal_senders (ex.: max_wal_senders = 10) para permitir mais conexões de replicação concorrentes. Reinicialização necessária.
  • Melhore o Hardware da Réplica: Se E/S ou CPU for um gargalo, considere atualizar o hardware da réplica ou otimizar sua configuração de armazenamento (ex.: SSDs mais rápidos, disco WAL separado).
  • Ajuste wal_compression: No primário, definir wal_compression = on pode reduzir o volume de WAL, potencialmente melhorando a velocidade de replicação em links com restrição de rede, mas ao custo de CPU do primário.
  • Ajuste wal_keep_size ou wal_keep_segments: Certifique-se de que arquivos WAL suficientes sejam retidos no primário para evitar que as réplicas fiquem fora de sincronia e exijam um backup completo.
  • synchronous_commit: Embora synchronous_commit = on forneça garantias mais fortes de durabilidade de dados, ele introduz latência para gravações no primário. Use remote_write ou remote_apply para tabelas ou transações específicas se a replicação síncrona estrita for necessária, mas avalie cuidadosamente o impacto no desempenho.
  • Monitoramento e Alertas: Implemente monitoramento robusto para pg_stat_replication e configure alertas para quando o lag exceder limites aceitáveis.
-- No Primário: Verificar LSN WAL atual
SELECT pg_current_wal_lsn();

-- No Primário: Verificar standbys conectados e lag
SELECT
    usename, application_name, client_addr, state, sync_state,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes,
    write_lag, flush_lag, replay_lag
FROM pg_stat_replication;

Em um standby, use:

SELECT
    pg_is_in_recovery() AS is_standby,
    pg_last_wal_receive_lsn(),
    pg_last_wal_replay_lsn(),
    now() - pg_last_xact_replay_timestamp() AS time_since_last_replay;

A consulta de timestamp pode ser enganosa em um sistema ocioso porque nenhuma transação recente pode ter sido reproduzida. Use-a com comparações de LSN e contexto de carga de trabalho.

3. Transição de Primário Falha ou Travada

Um failover automatizado deve promover uma réplica de forma rápida e confiável. Quando esse processo trava ou falha completamente, pode levar a um tempo de inatividade prolongado e exigir intervenção manual.

Sintomas do Problema:

  • Nenhum novo primário é eleito ou promovido após o primário antigo cair.
  • O cluster entra em um estado de split-brain onde dois nós acreditam ser o primário.
  • Logs do gerenciador de failover mostram erros relacionados a quórum, eleição de líder ou promoção de banco de dados.
  • Os aplicativos permanecem inativos porque nenhum primário está disponível.

Causas Subjacentes:

  • Split-Brain: Ocorre quando partições de rede isolam nós, levando a múltiplos primários ou eleição ambígua de primário. Este é o cenário mais perigoso, arriscando divergência de dados.
  • Problemas de Quórum: O gerenciador de failover pode não conseguir atingir um quórum (maioria dos votos) entre seus nós, impedindo-o de tomar uma decisão sobre a promoção. Isso é comum em clusters com um número par de nós ou muito poucos nós.
  • Isolamento de Rede: Os nós do gerenciador de failover não conseguem se comunicar entre si ou com as instâncias PostgreSQL, impedindo verificações de saúde ou execução de comandos.
  • Privilégios Insuficientes: O usuário do gerenciador de failover pode não ter as permissões PostgreSQL necessárias (ex.: pg_promote()) ou permissões de nível de sistema (ex.: para gerenciar VIPs).
  • Erros de Configuração: restore_command, primary_conninfo ou outras configurações incorretas dentro da configuração do gerenciador de failover.

Etapas de Depuração:

  1. Verifique os Logs do Gerenciador de Failover: Esta é a principal fonte de informação. Para o Patroni, procure em seus logs específicos (geralmente journalctl -u patroni ou o arquivo de log configurado em patroni.yml). Para pg_auto_failover, verifique journalctl -u pgautofailover_monitor e logs do agente.
  2. Verifique o Status do Quórum: Para o Patroni, use patronictl list para ver o estado de todos os membros do cluster e confirmar o líder eleito. Para pg_auto_failover, verifique pg_autoctl show state.
  3. Conectividade de Rede: Execute verificações de ping, traceroute e telnet entre todos os nós de HA e o armazenamento de consenso distribuído (ex.: Etcd, Consul, ZooKeeper para Patroni).
  4. Logs do Sistema: Verifique journalctl -xe ou /var/log/syslog em todos os nós em busca de erros de nível de sistema que possam interferir no gerenciador de failover (ex.: disco cheio, problemas de memória).
  5. Logs do PostgreSQL: Examine os logs do PostgreSQL na réplica candidata para promoção para ver se ela relata algum problema durante a tentativa de promoção.

Soluções:

  • Implemente Fencing/STONITH: O Fencing é crucial para evitar split-brain, garantindo que um primário com falha não possa continuar aceitando gravações antes que um novo seja promovido. Isso é normalmente tratado pelo gerenciador de failover ou pela camada de infraestrutura.
  • Número Ímpar de Nós para Quórum: Sempre implante um número ímpar de nós votantes (ex.: 3, 5) para o armazenamento de consenso distribuído do seu gerenciador de failover (Etcd, Consul, ZooKeeper) para garantir que o quórum possa ser sempre alcançado, mesmo se um ou dois nós falharem.
  • Configuração de Rede Robusta: Garanta caminhos de rede redundantes, regras de firewall adequadas permitindo comunicação nas portas necessárias (PostgreSQL, armazenamento de consenso, API do Patroni) e resolução de nomes de host consistente.
  • Verificação de Permissões: Verifique se a conta de usuário que executa o gerenciador de failover tem todos os privilégios PostgreSQL necessários e permissões de sistema para realizar tarefas de promoção e reconfiguração.
  • Revise a Configuração do Gerenciador de Failover: Verifique novamente as configurações de patroni.yml ou pg_auto_failover em busca de erros de digitação, caminhos incorretos ou restore_command mal configurado.
  • Intervenção Manual (Com Cautela): Em um failover travado grave, a promoção manual ou a rejunção de nós pode ser necessária. Proceda com extrema cautela, garantindo que o primário antigo seja completamente desligado antes de promover um novo para evitar divergência de dados.
# Exemplo: Verificando o status do cluster Patroni
patronictl -c /etc/patroni/patroni.yml list

# Saída esperada (exemplo):
# + Cluster: my_ha_cluster (6979219803154942080) ------+----+-----------+----+-----------+
# | Member  | Host         | Role    | State    | TL | Lag |
# +---------+--------------+---------+----------+----+-----+
# | node1   | 192.168.1.10 | Leader  | running  | 2  |     |
# | node2   | 192.168.1.11 | Replica | running  | 2  | 0   |
# | node3   | 192.168.1.12 | Replica | running  | 2  | 0   |
# +---------+--------------+---------+----------+----+-----+

4. Problemas de Conectividade de Rede e Resolução DNS

Na raiz de muitos problemas de HA estão problemas fundamentais de rede, impedindo que os nós se comuniquem ou que os aplicativos encontrem o endpoint correto do banco de dados.

Sintomas do Problema:

  • Erros de connection refused ou no route to host de aplicativos ou entre nós do cluster.
  • O gerenciador de failover relata nós como inacessíveis.
  • Serviços que dependem de DNS não conseguem resolver o nome do host do primário corretamente.

Causas Subjacentes:

  • Regras de Firewall: Regras de firewall configuradas incorretamente (ex.: iptables, grupos de segurança) bloqueando a porta PostgreSQL (5432), portas do gerenciador de failover ou portas do armazenamento de consenso.
  • Partição de Rede: Divisão de rede física ou lógica impedindo a comunicação entre um subconjunto de nós.
  • Roteamento Incorreto: Rotas de rede mal configuradas em um ou mais nós.
  • Cache/Configuração Incorreta de DNS: Conforme discutido na Seção 1, registros DNS obsoletos ou configuração incorreta do servidor DNS podem direcionar o tráfego incorretamente.
  • Falha na Migração de IP Virtual (VIP): Se estiver usando um VIP, ele pode falhar ao migrar para o novo primário, deixando o serviço inacessível.

Etapas de Depuração:

  1. Conectividade Básica: Use ping <target_ip> entre todos os nós.
  2. Conectividade de Porta: Use telnet <target_ip> <port> (ex.: telnet 192.168.1.10 5432) para verificar se a porta PostgreSQL está aberta e ouvindo.
  3. Verificação de Firewall: Em cada nó, verifique as regras de firewall ativas (sudo iptables -L, sudo ufw status ou configurações de grupo de segurança do provedor de nuvem).
  4. Status da Interface de Rede: Use ip addr show ou ifconfig para garantir que as interfaces de rede estejam ativas e configuradas corretamente.
  5. Resolução DNS: Use dig <hostname> ou nslookup <hostname> para verificar a resolução de nomes de host a partir de nós relevantes (servidores de aplicativos, pooler, nós de HA).

Soluções:

  • Revise as Regras de Firewall: Certifique-se de que as portas necessárias estejam abertas para PostgreSQL (5432), plano de controle do gerenciador de failover (ex.: 8008 para API do Patroni, 8000/8001 para pg_auto_failover) e o armazenamento de consenso distribuído (ex.: Etcd: 2379/2380, Consul: 8300/8301/8302).
  • Rede Consistente: Certifique-se de que todos os nós estejam na mesma sub-rede ou tenham o roteamento correto configurado se abrangerem várias sub-redes.
  • Atualizações de DNS: Automatize as atualizações de DNS como parte do processo de failover ou use um TTL mais curto. VIPs são frequentemente preferidos por esse motivo.
  • Gerenciamento de VIP: Se estiver usando um VIP, certifique-se de que a ferramenta de gerenciamento de VIP (ex.: Keepalived, gerenciamento de IP do provedor de nuvem) esteja configurada e funcionando corretamente. Teste a migração de VIP explicitamente.
  • Acesso Baseado em Host: Para simplificar em clusters menores, certifique-se de que pg_hba.conf permita conexões de todos os endereços IP potenciais de primário/réplica e do IP do pooler de conexão.

Ferramentas Essenciais para Solução de Problemas

  • psql: Para executar consultas SQL como pg_stat_replication, pg_current_wal_lsn(), comandos SHOW *.
  • CLI do Gerenciador de Failover: patronictl, pg_autoctl para consultar o estado do cluster, logs e iniciar ações.
  • Monitoramento do Sistema: Ferramentas como Prometheus + Grafana, Zabbix, Nagios ou monitoramento do provedor de nuvem para insights em tempo real sobre utilização de recursos, lag de replicação e status do serviço.
  • journalctl / tail -f: Para visualizar logs do sistema e de aplicativos.
  • Utilitários de Rede: ping, traceroute, telnet, iperf, netstat, dig, nslookup para diagnosticar conectividade.
  • dmesg: Para erros de nível de kernel, especialmente relacionados a E/S de disco ou OOM (Out Of Memory) killer.

Uma Lista de Verificação Rápida para Incidentes

Quando um alerta de failover de HA dispara, use uma lista de verificação curta antes de mudar qualquer coisa:

  1. Confirme a visão do cluster:
patronictl -c /etc/patroni/patroni.yml list
  1. Confirme a função do próprio PostgreSQL em cada nó acessível:
SELECT pg_is_in_recovery();

false significa que o nó é o primário gravável. true significa que é um standby. Se mais de um nó relatar false, pare e trate o incidente como possível split-brain.

  1. Confirme o endpoint do aplicativo:
dig primary-db.internal.example.com
nc -vz primary-db.internal.example.com 5432
  1. Confirme o alvo do pooler, se você usa PgBouncer:
SHOW SERVERS;
SHOW POOLS;
  1. Verifique se os erros são antigos ou atuais. Os logs do aplicativo geralmente mantêm mensagens de repetição dos primeiros segundos do failover. Compare os timestamps antes de assumir que a falha ainda está ocorrendo.

Esta lista de verificação é intencionalmente simples. Durante um failover real, o melhor comando é aquele que todos na equipe entendem e podem executar sem adivinhar.

Melhores Práticas para Prevenir Problemas de Failover

  • Testes Regulares de Failover: Simule regularmente falhas do primário e observe o processo de failover. Isso constrói confiança e expõe configurações incorretas.
  • Monitoramento e Alertas Robusto: Monitore métricas-chave como lag de réplica, status do primário, saúde do pooler de conexão e recursos do sistema. Configure alertas para qualquer desvio.
  • Configuração Adequada do Pooler de Conexão: Certifique-se de que server_reset_query esteja configurado, pool_mode seja apropriado para seu aplicativo e as verificações de saúde estejam habilitadas.
  • Ajuste os Parâmetros de Replicação: Configure wal_level, max_wal_senders, wal_keep_size e synchronous_commit cuidadosamente com base em seus requisitos de desempenho e durabilidade.
  • Documente Sua Configuração de HA: Documente claramente sua arquitetura de HA, configuração do gerenciador de failover, configurações de rede e procedimentos de recuperação.
  • Use um Gerenciador de Failover Dedicado: Confie em soluções comprovadas como Patroni ou pg_auto_failover em vez de scripts personalizados para lógica crítica de HA.
  • Armazenamento de Consenso Dedicado: Se estiver usando um gerenciador como Patroni, implante um cluster separado e altamente disponível para seu armazenamento de consenso distribuído (Etcd, Consul) para evitar um ponto único de falha.

Os testes de HA mais úteis não são demonstrações limpas onde o primário é educadamente parado. Teste também os casos feios: o primário antigo perde a rede, mas continua funcionando, o DNS atualiza lentamente, o PgBouncer mantém conexões de servidor obsoletas, uma réplica está 30 segundos atrasada ou o armazenamento de consenso perde um membro. Esses testes mostram se seus runbooks correspondem ao sistema que você realmente opera.