01
De onde vem o atraso da réplica e por que ele tem picos
Na replicação por streaming do PostgreSQL, o primário confirma o COMMIT assim que grava o WAL no próprio disco e só depois envia esse WAL às réplicas. Cada réplica percorre quatro etapas: recebe os bytes, grava, sincroniza no disco e aplica as mudanças nas páginas de dados. Uma consulta na réplica só enxerga uma transação depois da última etapa, o replay. Em operação normal, tudo isso leva de dez a cinquenta milissegundos, e é por isso que o problema passa despercebido nos testes: o atraso médio é menor que o tempo de um redirecionamento. O que causa os chamados são os picos.
| Causa do pico | O que acontece | Como aparece |
|---|---|---|
| Rajada de escrita no primário | Uma importação ou um UPDATE em massa gera gigabytes de WAL em minutos, e o replay, que aplica o WAL em um único processo, não acompanha | Todas as réplicas atrasam juntas, por segundos ou minutos |
| Consulta longa na réplica | O replay precisa remover versões que a consulta ainda lê; ele pausa até max_standby_streaming_delay, trinta segundos por padrão, e depois cancela a consulta | Uma réplica atrasa sozinha, em degraus de até trinta segundos, enquanto a outra está em dia |
| Disco ou CPU da réplica saturados | Réplica menor que o primário, ou dividindo recurso com relatórios, aplica mais devagar do que o primário produz | Atraso que cresce continuamente no horário de pico e só zera de madrugada |
| Rede entre zonas ou regiões | Latência e banda limitam o envio do WAL, sobretudo em rajadas | Atraso proporcional ao volume escrito, pior na réplica mais distante |
No incidente do checkout, as duas primeiras causas se somavam. Às duas da tarde, a importação de catálogo gerava seis gigabytes de WAL e as duas réplicas chegavam a oito segundos de atraso. Ao longo do dia, um relatório de vendas de vinte e cinco segundos rodava na segunda réplica e pausava o replay dela a cada execução. As consultas abaixo mostram as duas coisas: o atraso por réplica visto do primário, a diferença entre o que a réplica recebeu e o que ela já aplicou, e quantas consultas foram canceladas por conflito com o replay.
-- No primario: quanto cada replica esta atrasada, em bytes e em tempo
SELECT application_name,
state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS bytes_atras,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication
ORDER BY bytes_atras DESC;
-- Na replica: posicao recebida, posicao aplicada e replay pausado por conflito
SELECT pg_last_wal_receive_lsn() AS recebido,
pg_last_wal_replay_lsn() AS aplicado,
pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS bytes_por_aplicar;
-- Na replica: consultas canceladas por conflito com o replay, por banco
SELECT datname, confl_snapshot, confl_lock, confl_bufferpin
FROM pg_stat_database_conflicts;Quando bytes_por_aplicar cresce na réplica enquanto o recebimento está em dia, o gargalo é o replay, e não a rede. Quando confl_snapshot sobe junto com os degraus de atraso, a causa são consultas longas disputando com o replay. Ligar hot_standby_feedback evita esses cancelamentos, mas transfere o custo para o primário, que passa a segurar versões mortas para proteger a consulta da réplica. É a troca entre atraso na réplica e tabela inchada no primário, e ela precisa ser feita de forma consciente.