Indici corrotti in PG12, come li risolvo?

Sto solo facendo delle supposizioni, ma ecco la mia teoria del ‘cappello di stagnola’:

Ho eseguito Reindex due volte in parallelo (quindi prima ccnew, poi ccnew1)
ed entrambe le volte ha segnalato errori.

Forse, quando fallisce, non effettua il ripristino; si limita a lasciare i residui e termina.

Sembra molto, molto, molto probabile.

Aggiungo solo il mio piccolo contributo,

Gli indici TOAST non possono essere eliminati in questo modo,
dovetti:

su postgres
psql
\connect discourse
drop index pg_toast.pg_toast_309322_index_ccnew;
drop index pg_toast.pg_toast_309322_index_ccnew1;

Quanto sopra si applica solo a pg_toast perché l’utente discourse non ha accesso a quell’indice.
PG::InsufficientPrivilege: ERRORE: permesso negato per lo schema pg_toast

eliminare gli indici toast fa molto paura. Immagino che non abbiate altra scelta in questo caso. Sì, dovete farlo da psql.

Ed ecco il felice reindicizzazione:

discourse=# REINDEX SCHEMA CONCURRENTLY public;
REINDEX

:partying_face:

Ottima individuazione! Ho trovato tutti gli indici con “ccnew” nel nome tramite questa query.

psql
\connect discourse
select tablename,indexname,indexdef from pg_indexes where indexname LIKE '%ccnew%';

incoming_referer.csv (7.2 KB)

Si è scoperto che ne avevo ben 30, tutti sulla tabella incoming_referers. Quindi ho verificato che tutti gli indici ccnew fossero effettivamente duplicati tramite la colonna indexdef in questa query.

select indexname,indexdef from pg_indexes where tablename = 'incoming_referers';

ccnew.csv (6.6 KB)

E poi li ho eliminati tutti con successo.

DROP INDEX incoming_referers_pkey_ccnew;
DROP INDEX incoming_referers_pkey_ccnew_ccnew;
DROP INDEX incoming_referers_pkey_ccnew1;
...e così via per tutti i 30

A quel punto ho ricreato gli indici dell’intero schema, ma ancora una volta non è stato possibile ricostruire due degli stessi indici ccnew di incoming_referers, e ho trovato anche tre indici pg_toast. Li ho eliminati e ho ricreato gli indici dell’intero schema ancora una volta; di nuovo ci sono stati errori, ho trovato un altro gruppo di indici ccnew nello schema discourse, ho ricreato gli indici per la terza volta…

Non riesco a completare un reindicizzazione completa senza errori: continua a creare e poi a non riuscire a ricostruire nuovi indici ccnew ogni volta. Dopo 4 ricostruzioni complete ho eliminato gli indici ccnew e ho desistito. Immagino che potrei provare a ricostruirli in modalità non concorrente, ma ciò causerebbe un notevole tempo di inattività.

Comunque, il mio sospetto è che la maggior parte degli utenti che aggiornano da PG10 a PG12 e che hanno tentato di ricreare gli indici successivamente abbiano questi indici ccnew aggiuntivi, che dovrebbero tutti essere eliminati. Occupano solo spazio e moltiplicano le operazioni di scrittura su disco senza alcun beneficio.

Ho eseguito l’equivalente in Data-Explorer. È stato leggermente più facile da gestire.

Quello che ho fatto è stato prendere tutti gli indici ccnew/ccnew1/ccnew2…ccnewn, eliminarli e reindicizzare. Questo ha risolto il problema per me.

cctime è una funzionalità di PostgreSQL per contrassegnare gli indici e penso che sia un’inefficienza nel processo che causa il loro abbandono se l’indicizzazione fallisce per qualche motivo.

Posso solo suggerirti di lavorare prima sulla causa principale del problema. Avevo tag duplicati che ho dovuto eliminare prima di procedere all’eliminazione degli indici. Se mi fosse rimasto anche un solo tag duplicato, non sarebbe stato indicizzato correttamente e avrebbe creato un altro indice ccnew'n' fallendo.

Hai ragione, ho ancora dei duplicati: non posso ricostruire gli indici non-ccnew in modo non concorrente. Devo rimuovere queste righe duplicate.

discourse=# reindex index index_incoming_referers_on_path_and_incoming_domain_id;
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 3433) is duplicated.
discourse=# reindex index index_incoming_referers_on_path_and_incoming_domain_id;
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.

La cosa davvero strana è che vedo solo una riga per ciascuno di quei valori di incoming_domain_id nella tabella incoming_referer. Allora perché sono considerati duplicati?

discourse=# select * from incoming_referers where path='/search/' AND incoming_domain_id IN (1861,3433);
  id   |   path   | incoming_domain_id 
-------+----------+--------------------
 42845 | /search/ |               1861
 40763 | /search/ |               3433
(2 rows)

@sam o @riking, dovrei eliminare queste due righe nel modo seguente:

DELETE FROM incoming_referers WHERE path='/search/' AND incoming_domain_id IN (1861,3433);

… Immagino che dopo tutto sto imparando PostgreSQL, eh.

No, questo cancellerà entrambi: vuoi usare WHERE per trovare i due id e selezionare solo uno degli id da eliminare.

La tua query sta utilizzando l’indice corrotto, ed è per questo che vedi solo una riga per ciascuno. Prova così:

... where path LIKE '%/search/' ...

OK, questo restituisce 43 righe.

discourse=# select * from incoming_referers where path LIKE '%/search/' ORDER BY incoming_domain_id;
  id   |    path     | incoming_domain_id 
-------+-------------+--------------------
   878 | /search/    |                 63
 33457 | /do/search/ |                567
  1580 | /search/    |                602
  1888 | /search/    |                663
 42983 | /search/    |               1259
  4896 | /search/    |               1788
 42845 | /search/    |               1861
  5162 | /search/    |               1861
  5176 | /search/    |               1866
 43350 | /search/    |               1905
 17238 | /search/    |               1905
 20689 | /search/    |               1982
  5781 | /hg/search/ |               1987
  8031 | /search/    |               2665
 10325 | /search/    |               3192
 11289 | /search/    |               3414
 40763 | /search/    |               3433
 42849 | /search/    |               3433
 13087 | /search/    |               3895
 13159 | /search/    |               3949
 13802 | /do/search/ |               4051
 14407 | /search/    |               4209
 14507 | /search/    |               4211
 15394 | /search/    |               4230
 15533 | /search/    |               4258
 45274 | /search/    |               5303
 20923 | /search/    |               5400
 21317 | /search/    |               5534
 22928 | /search/    |               5918
 22956 | /search/    |               5926
 37448 | /search/    |               6393
 25094 | /search/    |               6412
 25594 | /search/    |               6547
 39655 | /search/    |               6596
 27371 | /search/    |               6986
 27452 | /a/search/  |               7003
 27623 | /search/    |               7041
 31041 | /search/    |               7767
 36943 | /search/    |               8622
 37381 | /search/    |               8711
 37411 | /search/    |               8716
 40424 | /search/    |               9124
 44451 | /search/    |               9525
(43 righe)

Quindi dovrei eseguire il seguente comando?

DELETE FROM incoming_referers WHERE path LIKE '%/search/' AND id IN (42845,43350,42849);

Sì, quel DELETE sembra corretto e dovrebbe consentirti di eseguire un backup pulito.

Credeteci o no, altri fallimenti. Nel primo tentativo, ho individuato un’altra chiave duplicata su /m/search che, per qualche motivo, non era stata rilevata dal wildcard %/search/ e l’ho eliminata. Ho rieseguito l’indicizzazione (ogni volta richiedeva quasi un’ora!) e sono emersi altri fallimenti che segnalavano una chiave duplicata su users(username_lower).

discourse=# reindex index index_users_on_username_lower;              
ERROR:  could not create unique index "index_users_on_username_lower"
DETAIL:  Key (username_lower)=(john_smith) is duplicated.

Eppure, la cosa strana è che c’era una sola riga con username_lower=john_smith! È il momento di indossare il cappello da investigatore.

Osservando l’interfaccia di amministrazione del forum, abbiamo notato due utenti distinti chiamati rispettivamente “john_smith” e “John_Smith”-- notate la diversa capitalizzazione. Così ho eliminato quello tutto in minuscolo a livello di forum, dato che non era attivo da 4 anni, e l’indicizzazione è stata ricostruita correttamente.

Ho eseguito un’ulteriore ricostruzione completa, ciascuna delle quali richiedeva quasi un’ora, e FINALMENTE penso che siamo a posto: solo un errore, ma nessun duplicato, solo un pg_toast ccnew. L’ho eliminato.

discourse=# REINDEX SCHEMA CONCURRENTLY public;               
WARNING:  cannot reindex invalid index "pg_toast.pg_toast_2783329_index_ccnew" concurrently, skipping
REINDEX

È stato un viaggio.

Queste correzioni al database potranno essere automatizzate in un futuro aggiornamento di Discourse?

È più probabile che la corruzione dell’indice di PostgreSQL si risolva in un futuro aggiornamento di PostgreSQL, se Peter ottiene una riproducibilità coerente.

Se un REINDEX concorrente fallisce perché la tabella contiene dati non validi, è necessario:

  1. Correggere i dati non validi, come è stato fatto qui.

  2. Elencare gli indici non validi utilizzando SELECT pg_class.relname FROM pg_class, pg_index WHERE pg_index.indisvalid = false AND pg_index.indexrelid = pg_class.oid;

  3. Eliminare ciascun indice non valido elencato sopra con DROP INDEX <indexname>;

  4. Riprovare il comando REINDEX.

Dopo aver eseguito l’aggiornamento alla versione 2.5.0beta5 e seguito le indicazioni post-aggiornamento per reindicizzare il database, ottengo questo:

discourse=# REINDEX SCHEMA CONCURRENTLY public;
ERROR:  could not create unique index "index_plugin_store_rows_on_plugin_name_and_key_ccnew"
DETAIL:  Key (plugin_name, key)=(discourse-data-explorer, q:-10) is duplicated.

Preferirei non fare esperimenti su questo… quindi, come posso eliminare in modo sicuro il duplicato?

No. L’aggiornamento non danneggia il tuo indice, ma segnala semplicemente che è già danneggiato. Puoi verificare provando a ripristinare il tuo backup su un altro server (anche uno che esegue pg10) oppure provando a ricostruire l’indice sulla tua installazione corrente. Non è chiaro cosa causi gli indici danneggiati, ma c’è la speranza che pg12 riduca la probabilità che ciò accada.

Ci sono alcuni vantaggi prestazionali nell’aggiornamento, ma rimandarlo non è una cattiva idea.

È troppo tardi ora, quindi sto ancora cercando indicazioni sui prossimi passi.

Qualcosa come

select id, plugin_name, key from plugin_store_rows where plugin_name like '%discourse-data-explorer%'

dovrebbe restituirti le righe. Penso che sia abbastanza sicuro cancellarle.

Penso che sia necessario ilike