Erreur 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.
Cette erreur n'existe que sur les réplicas hot standby. Le réplica doit appliquer le WAL du primaire pour rester synchronisé ; votre requête avait besoin de versions de ligne que le WAL entrant (typiquement le nettoyage par vacuum) supprime. Après avoir suspendu le rejeu pendant max_standby_streaming_delay, le réplica a choisi la réplication plutôt que votre requête et l'a annulée.
Ce que signifie cette erreur
Le réplica sert des lectures tout en rejouant continuellement le WAL du primaire — deux tâches qui entrent en conflit quand le rejeu veut modifier ou supprimer des données qu'une requête en cours peut encore voir. PostgreSQL retarde d'abord le rejeu pour laisser la requête se terminer (jusqu'à max_standby_streaming_delay, par défaut 30 s), puis annule la requête. C'est un compromis délibéré entre la fraîcheur du réplica et la survie de la requête, et chaque bouton ne fait que se déplacer le long de cet axe.
Causes fréquentes
- Requêtes de longue durée sur le réplica (rapports, analytics) en course contre l'activité de vacuum sur le primaire — de loin le cas habituel.
- DDL sur le primaire : rejouer un DROP ou un ALTER nécessite un verrou exclusif sur le réplica, en conflit avec toute requête utilisant l'objet.
Comment la diagnostiquer
-- Sur le réplica : compteurs de conflits par type et par base
SELECT * FROM pg_stat_database_conflicts;
confl_snapshot est le cas du nettoyage par vacuum ; confl_lock pointe vers du DDL sur le primaire. Corrélez avec les charges de travail qui s'exécutent sur le réplica à ces moments-là.
Comment la corriger
Choisissez le compromis explicitement :
hot_standby_feedback = on(sur le réplica) : le réplica indique au primaire quelles versions de ligne il utilise encore, et le vacuum sur le primaire les épargne. Les requêtes cessent de mourir ; le coût est du bloat sur le primaire retenu par les requêtes longues du réplica — le même effet qu'une transaction longue s'exécutant localement.- Augmentez
max_standby_streaming_delaysur un réplica de reporting dédié : les requêtes gagnent, le réplica prend du retard pendant leur exécution. Bien quand le réplica n'est pas utilisé pour une fraîcheur critique en cas de bascule. - Réessayez sur le SQLSTATE 40001 pour les requêtes courtes — le HINT le dit : un instant plus tard, la même requête réussit généralement.
- Séparez les charges de travail : un réplica rapide et frais pour les lectures applicatives ; un réplica distinct avec un délai généreux pour l'analytics.
