Le notifiche email falliscono se esistono intestazioni duplicate

Vedi Email notifications fail if duplicate headers exist - #14 by simonk per la descrizione del bug
Aggiunto sopra da @pfaffman

Testo originale

Ciao,

gestisco un’installazione di Discourse piuttosto piccola da alcuni anni. Ha un traffico piuttosto basso, quindi ci è voluto un po’ per notare che l’invio di email (notifiche, riepiloghi) aveva ovviamente iniziato a fallire alcuni mesi fa. Le analisi indicano l’aggiornamento a 2.8.0.beta7 intorno al 22-10-2022, in precedenza eravamo alla 2.8.0.beta4. Almeno non ho ricevuto alcuna email da quell’installazione riguardo a post o messaggi.

Le email si accumulano in Sidekiq, con un messaggio che non riesco a collegare, né a trovare nulla di pertinente quando lo cerco: c’erano segnalazioni di messaggi undefined method, ma nessuna delle condizioni corrisponde alla mia. (Non è TLS, non è un timeout verso il server di posta, non è il plugin events, e la correzione per i media sicuri dovrebbe essere già stata applicata - inoltre, i messaggi di errore esatti erano diversi.)

Errore in Sidekiq:

Wrapped NoMethodError: undefined method `value' for #<Array:0x00007f7fd5277d68> Did you mean? values_at

La parte dopo #<Array: è diversa per ogni email in coda. Ho reinstallato Discourse su una nuova VM ieri sera e ho ripristinato da un backup recente - ma apparentemente il problema delle email è stato ripristinato con i dati :thinking:

Dato che il tasso di errore ha iniziato a salire alla fine di ottobre, sono abbastanza sicuro che questo sia stato introdotto da 2.8.0.beta7:

Qualsiasi aiuto, o suggerimento per debuggare ulteriormente il problema, è molto apprezzato.

1 Mi Piace

Hai plugin installati? Se hai plugin non standard, dovresti rimuoverli.

Ho già provato, il problema persiste :frowning:

## I plugin vanno qui
## vedi https://meta.discourse.org/t/19157 per i dettagli
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Qualsiasi comando personalizzato da eseguire dopo la compilazione

EDIT: FTR:

Plugin previsti:

root@discourse:/var/discourse# grep -B5 "git clone" containers/app.yml-with-plugins
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-spoiler-alert.git
          - git clone https://github.com/discourse/discourse-prometheus.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-chat-integration.git
          - git clone https://github.com/discourse/discourse-voting.git
          - git clone https://github.com/discourse/discourse-checklist.git
          - git clone https://github.com/discourse/discourse-whos-online.git
          - git clone https://github.com/discourse/discourse-calendar.git
          - git clone https://github.com/discourse/discourse-affiliate.git
          - git clone https://github.com/discourse/discourse-reactions.git
          - git clone https://github.com/discourse/discourse-surveys.git

La vecchia installazione ha:

root@discourse-old:/var/discourse# grep -B5 "git clone" containers/app.yml
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-spoiler-alert.git
          - git clone https://github.com/discourse/discourse-prometheus.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-chat-integration.git
          - git clone https://github.com/discourse/discourse-voting.git
          - git clone https://github.com/discourse/discourse-checklist.git
          - git clone https://github.com/davidtaylorhq/discourse-whos-online.git

Questa è un’installazione standard? Se hai un container dati separato, è stato ricostruito? Postgres è aggiornato? (Anche se penso che ci sia ancora un controllo per questo nel codice)

Sì, è un’installazione standard; per quanto posso vedere, è in esecuzione un solo container. (Avvia una nuova VM, riassegna il DNS, apt update, apt dist-upgrade, riavvia, git pull, ./discourse-setup, configurazione basata sul Web, carica backup, ripristina backup, riabilita posta, vedi di nuovo gli errori.) Nota che la vecchia installazione è stata in grado di inviare via email il link al backup, e l’invio di email di test funziona ancora nella nuova installazione — sembrano fallire solo le email relative ai post.

Per essere più precisi, si tratta di un’installazione minima di Debian 10, su cui ho installato Discourse:

root@discourse:~# history
    1  apt update
    2  apt dist-upgrade
    3  reboot ; exit
    4  git clone https://github.com/discourse/discourse_docker.git /var/discourse
    5  apt install git rsync
    6  git clone https://github.com/discourse/discourse_docker.git /var/discourse
    7  cd /var/discourse
       [attach /dev/vdb, fdisk, mkfs.ext4, mount as /var/lib/docker]
   18  apt-get install git apt-transport-https ca-certificates curl gnupg2 software-properties-common -y
   19  df -h
   20  curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo \"$ID\")/gpg | apt-key add -
   21  add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo \"$ID\") $(lsb_release -cs) stable\"
   22  apt-get update -y
   23  apt-get install docker-ce -y
   24  ./discourse-setup

E un container:

root@discourse:~# docker ps
CONTAINER ID   IMAGE                 COMMAND        CREATED        STATUS        PORTS                                                                      NAMES
ac408a70305d   local_discourse/app   \"/sbin/boot\"   12 hours ago   Up 12 hours   0.0.0.0:80-\u003e80/tcp, :::80-\u003e80/tcp, 0.0.0.0:443-\u003e443/tcp, :::443-\u003e443/tcp   app

Modifica:

root@discourse:~# apt update
Hit:1 https://download.docker.com/linux/debian buster InRelease
Hit:2 http://deb.debian.org/debian buster InRelease
Get:3 http://deb.debian.org/debian buster-updates InRelease [51,9 kB]
Get:4 http://security.debian.org/debian-security buster/updates InRelease [65,4 kB]
Fetched 117 kB in 2s (60,5 kB/s)    
Reading package lists... Done
Building dependency tree       
Reading state information... Done
All packages are up to date.

Il primo ripristino è effettivamente fallito, però. database_restorer.rb è fallito in restore_dump a causa di link duplicati nel post_id 3841. Ho finito per sostituire questi link in un post del 2017 con uno screenshot di essi sulla vecchia installazione e facendo un altro backup; solo allora sono stato in grado di ripristinare il backup sulla nuova installazione. Dato che hai menzionato postgres, questo è successo durante CREATE INDEX con ERROR: could not create unique index \"unique_post_links\". Ulteriori informazioni: EXCEPTION: psql failed: DETAIL: Key (topic_id, post_id, url)=(1300, 3841, [redacted]) is duplicated.

Anche se non penso che questo sia direttamente correlato, ho pensato di menzionarlo.

Quindi, se nessuno conosce un modo semplice per risolvere il problema, analizziamolo correttamente. Ma ho bisogno del tuo aiuto, poiché Discourse è un’applicazione piuttosto complessa che utilizza una serie di tecnologie con cui non ho familiarità. Quindi: come vengono inviate le e-mail a Sidekiq?

Quale componente di Discourse sta fornendo a Sidekiq un metodo “value”?

Con le notifiche e-mail che smettono di funzionare dopo un aggiornamento, Discourse è purtroppo piuttosto inutile ora. Invece di un’attenzione immediata, gli argomenti richiedono ora giorni per ricevere attenzione, se mai ne ricevono, poiché le persone non effettuano sondaggi attivi nel 2022. Nessuna notifica ⇒ non è successo nulla ⇒ non è necessario controllare il sito :frowning:

1 Mi Piace

Sembra un indice corrotto. Non credo di averlo mai visto su postgres 13. Sembra che tu abbia risolto questi problemi su un vecchio sito e poi aggiornato eseguendo il backup e il ripristino su un nuovo sito?

Sembra che il problema abbia a che fare con qualcosa nel codice che invia notifiche, ma è un problema di sidekiq.

Come ho detto, il vecchio sito è in esecuzione, ho eseguito un backup e l’ho messo su una nuova installazione, il ripristino è fallito. Ho modificato il post indicato come colpevole finché il ripristino sulla nuova installazione non ha funzionato. Solo per vedere di avere di nuovo/ancora il problema con Sitekiq.

Il vecchio sito esegue anche postgres 13 (ma risale a diversi anni fa, quindi molto probabilmente non è iniziato con quella versione :slightly_smiling_face:)

root@discourse-old:/var/discourse# ./launcher enter app
x86_64 arch detected.
root@discourse-app:/var/www/discourse# psql --version
psql (PostgreSQL) 13.5 (Debian 13.5-1.pgdg110+1)

Quindi, secondo questi commenti di chiusura del post, il Database di Discourse può corrompersi — e venire riparato.

Ho provato con un nuovo utente, riceve correttamente la sua email di registrazione. Ma le notifiche sulle risposte ai suoi post, no; Sidekiq va in errore.

Quindi, per me questo significa che Discourse sta fornendo informazioni errate a Sidekiq quando gli istruisce di inviare Notifiche (al contrario dell’email di registrazione). Come fare ulteriori debug?

OK, quindi se gli indici vengono riparati, questo mi suggerisce che qualcosa viene chiamato con un array anziché con un modello che ha value. Il problema non è con sidekiq, in sé, ma con la funzione che sidekiq sta causando per essere chiamata.

Quindi sembra che qualcosa venga chiamato che restituisce un array anziché un singolo elemento, ma non posso indovinare cosa. Penso che dovrai guardare in /var/discourse/shared/standalone/logs/rails/production.log (o qualcosa di molto simile se le mie dita o la mia memoria mi tradiscono). Quindi puoi cercare in quei log quell’errore (o farlo accadere di nuovo in modo che sia alla fine del file). Dovresti ottenere maggiori informazioni su cosa sta fallendo lì.

Grazie, ma non dice molto:

Started POST "/sidekiq/retries" for 185.39.142.187 at 2022-04-11 16:31:35 +0000
start
Started GET "/sidekiq/retries" for 185.39.142.187 at 2022-04-11 16:31:35 +0000
  Rendered email/notification.html.erb (Duration: 42.8ms | Allocations: 4323)
  Rendered layouts/email_template.html.erb (Duration: 0.3ms | Allocations: 29)
Job exception: undefined method `value' for #<Array:0x00007ff393af6c78>
Did you mean?  values_at

fail

shared/standalone/log/rails/production_errors.log è vuoto.

L’errore viene visualizzato in Admin -\u003e Log -\u003e Log errori? In tal caso, è possibile ottenere uno stack trace completo che potrebbe essere utile.

1 Mi Piace

Oh, bello — sì, lo fa:

Quindi, se ho capito bene,

 433    def header_value(name)
 434      header = @message.header[name]
 435      return nil unless header
*436      header.value
 437    end

è – nel codice di Discourse? – dove Sidekiq si blocca?

Questo viene chiamato da…

 228      MessageBuilder.custom_headers(SiteSetting.email_custom_headers).each do |key, _|
*229        value = header_value(key)
 230
 231        # Remove Auto-Submitted header for group private message emails, it does
 232        # not make sense there and may hurt deliverability.

Quindi potrebbe essere un’intestazione personalizzata?

In effetti ho una voce lì:

Screenshot 2022-04-12 at 11-27-31 Administration - Freifunk Kreis GT

Ho reimpostato questo ai valori predefiniti e… in realtà sembra che le notifiche via email vengano inviate di nuovo, come verificato dai lettori.

Grazie!

Rimane una domanda: perché »Auto-Submitted: auto-generated|Precedence: bulk« causa questo errore? Afferma che le intestazioni personalizzate devono essere separate da »|«.

1 Mi Piace

(Disclaimer: non sono un programmatore Ruby)

Penso che questo sia un comportamento particolarmente spiacevole nella libreria di posta elettronica che Discourse sta utilizzando. Ecco la funzione header_value:

Per quanto ne so, @message.header[name] sta chiamando questo metodo:

https://www.rubydoc.info/github/mikel/mail/Mail%2FHeader:[]

Come da RFC, molti campi possono apparire più di una volta, restituiremo una stringa del valore se c’è solo un’intestazione, o se ci sono più intestazioni corrispondenti, restituiremo un array di valori nell’ordine in cui appaiono nell’intestazione ordinati dall’alto verso il basso.

Discourse imposta automaticamente un’intestazione Precedence, quindi poiché ne stai aggiungendo una anche tu tramite l’impostazione email_custom_headers, ci sono ora due intestazioni Precedence, e @message.header[\"Precedence\"] restituisce un array invece di una stringa.

Penso che questo bug verrà attivato ogni volta che email_custom_headers contiene un’intestazione che esiste già sull’oggetto del messaggio.

5 Mi Piace

Questo mi sembra quello che sta succedendo (ho suggerito che qualcosa fosse un array invece di un singolo elemento, ma non riuscivo a immaginare come potesse essere vero) ed è un bug.

Cambierò il titolo e la categoria di questo argomento.

3 Mi Piace

Ho unito una correzione per questo oggi, se rileviamo un’intestazione duplicata utilizziamo quella definita nel core di Discourse anziché quella personalizzata dall’impostazione del sito:

4 Mi Piace

Questo argomento è stato chiuso automaticamente dopo 2 giorni. Non sono più consentite nuove risposte.