Ripristino fallito: impossibile creare indice univoco

Ciao, il ripristino del backup fallisce con l’errore:

CREATE INDEX
ERROR:  non è stato possibile creare l'indice unico "index_incoming_referers_on_path_and_incoming_domain_id"
DETTAGLIO:  La chiave (path, incoming_domain_id)=(/m/search, 25) è duplicata.
ECCEZIONE:  psql fallito: DETTAGLIO:  La chiave (path, incoming_domain_id)=(/m/search, 25) è duplicata.

Ho trovato un argomento simile, ma non sono riuscito a capire cosa sia necessario fare: https://meta.discourse.org/t/getting-this-error-during-restore-could-not-create-unique-index/

Apprezzo qualsiasi aiuto passo dopo passo per principianti per risolvere il problema.

Questo problema con la tabella incoming_referers si è presentato diverse volte di recente. Non sono sicuro del motivo per cui quella tabella in particolare stia causando problemi, ma sembra probabile che le problematiche siano correlate. Forse qualcun altro del team di Discourse avrà idee su cosa potrebbe causare la creazione di record duplicati.

Hai ancora accesso al sito su cui hai creato il file di backup? Se sì, la soluzione è eliminare il record duplicato dal database e poi creare un nuovo file di backup. Per farlo, dovresti accedere via SSH al vecchio server e spostarti (cd) nella directory /var/discourse:

 cd /var/discourse

Poi esegui

./launcher enter app

Quindi accedi alla console Rails con

rails c

Dovresti quindi vedere un prompt simile a questo:

[1] pry(main)>

Prova a eseguire il seguente comando dalla console Rails e facci sapere cosa restituisce:

IncomingReferer.where(path: "/m/search")

Dovrebbe restituire un array con due o più record.

Grazie.
Lo eseguirò domani mattina e farò rapporto.

Questo proviene dalla vecchia installazione: sembra esserci solo un record?

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x00005638d834b130
  id: 5153,
  path: "/m/search",
  incoming_domain_id: 25>]
[2] pry(main)>

Modifica: provato anche sul nuovo server. Mostra:

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> []
[2] pry(main)>

Grazie per averlo verificato! Il risultato che hai ottenuto è effettivamente lo stesso che ho visto io su un altro sito più tardi oggi. È un problema risolvibile, ma cercherò di far controllare a uno dei nostri ingegneri cosa sta succedendo.

Il mio obiettivo principale nello spostare i server era dovuto al fatto che stavo utilizzando Debian 8, che sta per perdere il supporto.
Di fronte a questo problema di ripristino, ho optato per l’aggiornamento a Debian 9 sullo stesso server. L’operazione è andata a buon fine, quindi per il momento c’è un po’ di sollievo.
Grazie per il vostro supporto.

Sostituisci questa riga

È necessario eseguire una ricerca fuzzy in modo che non dia per scontato che l’indice funzioni. Un solo segno di percentuale è probabilmente sufficiente se si trova all’inizio, credo.

Puoi semplicemente eliminare il record extra. Per farlo correttamente, però, devi aggiornare anche l’altra tabella che fa riferimento a questa. Devo cercarlo ogni volta, dato che ci sono diverse tabelle collegate che coinvolgono questo caso.

Questo problema viene attribuito alle estensioni di terze parti, il che non ha molto senso. Sembra che debba essere colpa di PostgreSQL, ma non ne sono sicuro. Mi capita di imbatterci in questo problema un paio di volte al mese (score) su diversi siti.

Ho anch’io un problema di chiave duplicata, esiste una soluzione documentata?

discourse=# REINDEX SCHEMA CONCURRENTLY public;
    ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x0000557176d3f210 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557176d925c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

Anche se ho appena aggiornato il mio server in-place e quindi non ripristinerò più su un nuovo server, l’ho provato per curiosità e non ho trovato alcun record con la ricerca fuzzy:

[1] pry(main)> IncomingReferer.where(path: "%/m/search%")
=> []
[2] pry(main)> IncomingReferer.where(path: "%/m/search")
=> []
[3] pry(main)> IncomingReferer.where(path: "/m/search%")
=> []

Devi usare LIKE affinché i caratteri jolly funzionino:

IncomingReferer.where("path LIKE '%/m/search%'")

Ciò ha portato a molte altre chiavi duplicate.

[1] pry(main)> IncomingReferer.where("path LIKE '%/m/search%'")
=> [#<IncomingReferer:0x0000557eaa7ed488 id: 408, path: "/m/search", incoming_domain_id: 26>,
 #<IncomingReferer:0x0000557eaabd80c0 id: 1508, path: "/m/search", incoming_domain_id: 45>,
 #<IncomingReferer:0x0000557eaabe3268 id: 2216, path: "/m/search", incoming_domain_id: 420>,
 #<IncomingReferer:0x0000557eaabe2f20 id: 3081, path: "/m/search", incoming_domain_id: 230>,
 #<IncomingReferer:0x0000557eaabe2c00 id: 33210, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe2908 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe27c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

Eliminerei semplicemente tutte le righe duplicate… c’è poco valore in queste informazioni.

Felice di farlo, puoi fornire il comando corretto? Non sono pratico di PostgreSQL in particolare, ma conosco SQL.

È una buona notizia. Ho aggiornato con fatica l’altra tabella che fa riferimento a queste. È un vero incubo, dato che non riesco mai a ricordarmi di cosa si tratti, quindi devo ripeterlo continuamente.

IncomingReferer.find(44231).destroy
IncomingReferer.find(42228).destroy

Rimuovere quei due duplicati è andato a buon fine, ma la successiva ricostruzione degli indici ha generato nuovi errori. Si tratta di un problema grave? Come possiamo risolvere, cancellando quella riga con search 3433?

[1] pry(main)> IncomingReferer.find(44231).destroy
=> #<IncomingReferer:0x000055734c65d8e8 id: 44231, path: "/m/search", incoming_domain_id: 4>
[2] pry(main)> IncomingReferer.find(42228).destroy
=> #<IncomingReferer:0x000055734cd81a70 id: 42228, path: "/m/search", incoming_domain_id: 26>
postgres=# \connect discourse
Ora sei connesso al database "discourse" come utente "postgres".
discourse=# REINDEX SCHEMA CONCURRENTLY public;
WARNING:  impossibile reindicizzare l'indice non valido "public.incoming_referers_pkey_ccnew" in modo concorrente, salto
WARNING:  impossibile reindicizzare l'indice non valido "public.index_incoming_referers_on_path_and_incoming_domain_id_ccnew" in modo concorrente, salto
WARNING:  impossibile reindicizzare l'indice non valido "pg_toast.pg_toast_2782645_index_ccnew" in modo concorrente, salto
ERROR:  impossibile creare l'indice univoco "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1"
DETAIL:  La chiave (path, incoming_domain_id)=(/search/, 3433) è duplicata.
CONTEXT:  worker parallelo

Ecco il codice che gestisce la creazione… dovrebbe gestirlo correttamente, ma potremmo aggiornarlo a un inserimento ON CONFLICT se necessario?

Ho provato a ricostruire manualmente quei 4 indici. Due sono riusciti, due sono falliti. Dovrei eliminare quelle due righe duplicate?

discourse=# REINDEX INDEX CONCURRENTLY "public"."incoming_referers_pkey_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "public"."index_incoming_referers_on_path_and_incoming_domain_id_ccnew";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_cc_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.
discourse=# REINDEX INDEX CONCURRENTLY "pg_toast"."pg_toast_2782645_index_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_c_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

Sì, per favore elimina le righe duplicate.

@riking la corruzione degli indici in PostgreSQL è un bug di PostgreSQL, non di Discourse. Possiamo certamente migliorare le prestazioni di quell’inserimento, ma il bug di PostgreSQL deve essere risolto in PostgreSQL stesso.

Il mio sospetto è che sia legato a una chiusura brusca del motore del database, forse dovuta a un’interruzione di corrente.

È un’ipotesi plausibile. Il comando ./launcher shutdown app (o rebuild) esegue una chiusura pulita di Postgres? Aspetta, ma scommetto che un aggiornamento automatico non sa come chiudere correttamente i container Docker, vero?