Error de PostgreSQL: canceling statement due to conflict with recovery
FATAL: terminating connection due to conflict with recovery
DETAIL: User query might have needed to see row versions that must be removed.
HINT: In a moment you should be able to reconnect to the database and repeat your command.
Este error existe solo en réplicas hot standby. La réplica debe aplicar el WAL del primario para mantenerse sincronizada; tu consulta necesitaba versiones de fila que el WAL entrante (típicamente la limpieza del vacuum) elimina. Tras estancar el replay durante max_standby_streaming_delay, la réplica eligió la replicación por encima de tu consulta y la canceló.
Qué significa este error
La réplica sirve lecturas mientras reproduce continuamente el WAL del primario — dos trabajos que entran en conflicto cuando el replay quiere cambiar o eliminar datos que una consulta en marcha aún puede ver. PostgreSQL primero retrasa el replay para dejar terminar la consulta (hasta max_standby_streaming_delay, por defecto 30 s), y luego cancela la consulta. Es un compromiso deliberado entre la frescura de la réplica y la supervivencia de la consulta, y cada perilla solo se mueve a lo largo de ese eje.
Causas comunes
- Consultas de larga duración en la réplica (informes, analítica) compitiendo contra la actividad del vacuum en el primario — con diferencia el caso habitual.
- DDL en el primario: reproducir un DROP o ALTER necesita un bloqueo exclusivo en la réplica, en conflicto con cualquier consulta que use el objeto.
Cómo diagnosticarlo
-- En la réplica: contadores de conflictos por tipo y base de datos
SELECT * FROM pg_stat_database_conflicts;
confl_snapshot es el caso de la limpieza del vacuum; confl_lock apunta a DDL en el primario. Correlaciónalo con qué cargas de trabajo se ejecutan en la réplica en esos momentos.
Cómo solucionarlo
Elige el compromiso explícitamente:
hot_standby_feedback = on(en la réplica): la réplica le dice al primario qué versiones de fila aún necesita, y el vacuum en el primario las respeta. Las consultas dejan de morir; el coste es bloat en el primario retenido por las consultas largas de la réplica — el mismo efecto que una transacción larga ejecutándose localmente.- Sube
max_standby_streaming_delayen una réplica de informes dedicada: las consultas ganan, la réplica se retrasa durante ellas. Bien cuando la réplica no se usa para una frescura crítica de failover. - Reintenta ante el SQLSTATE 40001 para consultas cortas — el HINT lo dice en serio: un momento después la misma consulta normalmente tiene éxito.
- Separa las cargas de trabajo: una réplica rápida y fresca para las lecturas de la app; una réplica separada con retraso generoso para la analítica.
