Errore 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.
Questo errore esiste solo sui replica in hot standby. Il replica deve applicare il WAL dal primario per restare in sincronia; la tua query aveva bisogno di versioni di riga che il WAL in arrivo (tipicamente pulizia del vacuum) rimuove. Dopo aver stallato il replay per max_standby_streaming_delay, il replica ha scelto la replica invece della tua query e l'ha annullata.
Cosa significa questo errore
Il replica serve letture mentre riproduce continuamente il WAL del primario — due lavori che entrano in conflitto quando il replay vuole cambiare o rimuovere dati che una query in esecuzione può ancora vedere. PostgreSQL prima ritarda il replay per far finire la query (fino a max_standby_streaming_delay, default 30 s), poi annulla la query. È un compromesso deliberato tra freschezza del replica e sopravvivenza della query, e ogni manopola si limita a spostarsi lungo quell'asse.
Cause comuni
- Query di lunga durata sul replica (report, analytics) in gara contro l'attività di vacuum sul primario — di gran lunga il caso tipico.
- DDL sul primario: riprodurre un DROP o ALTER richiede un lock esclusivo sul replica, in conflitto con qualsiasi query che usi l'oggetto.
Come diagnosticarlo
-- Sul replica: contatori dei conflitti per tipo e database
SELECT * FROM pg_stat_database_conflicts;
confl_snapshot è il caso della pulizia del vacuum; confl_lock punta al DDL sul primario. Correla con quali carichi girano sul replica in quei momenti.
Come risolverlo
Scegli il compromesso esplicitamente:
hot_standby_feedback = on(sul replica): il replica dice al primario quali versioni di riga gli servono ancora, e il vacuum sul primario le risparmia. Le query smettono di morire; il costo è bloat sul primario trattenuto dalle query lunghe del replica — lo stesso effetto di una transazione lunga in esecuzione localmente.- Alza
max_standby_streaming_delaysu un replica dedicato al reporting: le query vincono, il replica va in lag durante di esse. Va bene quando il replica non è usato per una freschezza critica al failover. - Riprova su SQLSTATE 40001 per le query brevi — l'HINT lo intende: un attimo dopo la stessa query di solito riesce.
- Separa i carichi: un replica veloce e fresco per le letture dell'app; un replica separato con delay generoso per le analytics.
