Migrare una mailing list su Discourse (mbox, Listserv, Google Groups, ecc)

Sto cercando di importare un dump mbox standard da una mailing list, ma sto riscontrando problemi di “Processo terminato”, tipicamente dopo un lungo periodo trascorso nella fase di “indicizzazione […]mbox”. Si tratta di un file mbox di grandi dimensioni proveniente da un progetto open-source con dieci anni di post.

Cose che ho provato:

  • Dividere il file mbox in chunk. Questo ha funzionato parzialmente e ha importato con successo molti post, ma ora sono bloccato sull’indicizzazione di uno di questi chunk. Ho provato a dividere anche quel file in chunk: il primo è stato importato alla fine, mentre il secondo sembra ora bloccato.

  • Aumentare la memoria disponibile sul nostro server. L’utilizzo della memoria aumenta lentamente durante l’indicizzazione e attualmente si stabilizza intorno ai 16 GB (su 32 GB totali) per un tentativo di importazione di uno di questi chunk, un file mbox da 80 MB:

Durante questo periodo, 1 CPU rimane costantemente al massimo.

Qualsiasi consiglio sarebbe molto apprezzato, in particolare l’aumento della verbosità dell’output di debug, nel caso in cui il processo si blocchi su un post specifico. Il file index.db nella cartella import è di circa 800 MB.

Sono nuovo di Ruby e non uso regolarmente SQL, quindi mi sta risultando difficile capire cosa stia succedendo. Inoltre, questo server da 32 GB è costoso, quindi vorrei ridimensionarlo presto a 4 GB :slight_smile:

Grazie per qualsiasi aiuto!

1 Mi Piace

Immagino che il parser si blocchi su una specifica email in quel mbox. Il file index.db è un database SQLite. Esamina la tabella email, filtra per il nome del file mbox nella colonna filename e individua il valore più alto nella colonna last_line_number. È molto probabile che il parser si blocchi sulla prossima email dopo quel numero di riga all’interno del file mbox.

3 Mi Piace

Molte grazie @gerhard, sono riuscito a identificare l’ultima email indicizzata con successo e quella immediatamente successiva che (presumo) stia causando il blocco. Tuttavia, a mio avviso non sembra esserci nulla di eccezionale in queste email. Va bene se ti invio queste due email di esempio in un messaggio privato per vedere se salta all’occhio qualcosa?

Certo, puoi inviarmi un messaggio privato. Prova anche a rimuovere quelle email dal file mbox e verifica se l’indicizzazione funziona.

3 Mi Piace

Grazie, inviato. Non sono riuscito a inviarti un messaggio privato direttamente, quindi l’ho fatto tramite il gruppo del team; spero che vada bene. Proverò a rimuovere anche l’email e vediamo fino a dove arriviamo prima di un altro blocco.

1 Mi Piace

Grazie per le e-mail. Non ho notato nulla di strano e nei miei test ha funzionato senza problemi.

Questo non è stato testato, ma potresti provare ad applicare la seguente patch git prima di eseguire lo script di importazione. Aggiunge un timeout di 60 secondi per l’analisi di un’e-mail. Questo potrebbe aiutarti a individuare il colpevole e procedere, se il problema interessa solo un paio di messaggi.

From 92efb4fc68724cfa20d5de48ba33b99c126a3a08 Mon Sep 17 00:00:00 2001
From: Gerhard Schlager
Date: Fri, 2 Oct 2020 17:27:39 +0200
Subject: [PATCH] Add timeout for parsing email in mbox importer

---
 script/import_scripts/mbox/support/indexer.rb | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/script/import_scripts/mbox/support/indexer.rb b/script/import_scripts/mbox/support/indexer.rb
index dc6e092c29..01523dad13 100644
--- a/script/import_scripts/mbox/support/indexer.rb
+++ b/script/import_scripts/mbox/support/indexer.rb
@@ -65,11 +65,15 @@ module ImportScripts::Mbox
     def index_emails(directory, category_name)
       all_messages(directory, category_name) do |receiver, filename, opts|
         begin
-          msg_id = receiver.message_id
-          parsed_email = receiver.mail
-          from_email, from_display_name = receiver.parse_from_field(parsed_email)
-          body, elided, format = receiver.select_body
-          reply_message_ids = extract_reply_message_ids(parsed_email)
+          msg_id = parsed_email = from_email = from_display_name = body = elided = format = reply_message_ids = nil
+
+          Timeout.timeout(60) do
+            msg_id = receiver.message_id
+            parsed_email = receiver.mail
+            from_email, from_display_name = receiver.parse_from_field(parsed_email)
+            body, elided, format = receiver.select_body
+            reply_message_ids = extract_reply_message_ids(parsed_email)
+          end
 
           email = {
             msg_id: msg_id,
-- 
2.28.0
3 Mi Piace

Grazie mille @gerhard, la tua patch funziona alla perfezione. Per le mie esigenze, ritengo che saltare i messaggi problematici vada bene, dato che sono pochi; tuttavia, ora abbiamo un’output aggiuntivo che potrebbe essere utile per risolvere il problema o per rendere lo script di importazione più robusto:

Failed to index message in /shared/import/data/lammps-users/chunk_10.mbox at lines 726814-729353
execution expired
["/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogumbo-2.0.2/lib/nokogumbo/html5.rb:243:in `escape_text'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogumbo-2.0.2/lib/nokogumbo/html5.rb:214:in `serialize_node_internal'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogumbo-2.0.2/lib/nokogumbo/html5/node.rb:58:in `write_to'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node.rb:699:in `serialize'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node.rb:855:in `to_format'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node.rb:711:in `to_html'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogumbo-2.0.2/lib/nokogumbo/html5/node.rb:28:in `block in inner_html'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node_set.rb:238:in `block in each'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node_set.rb:237:in `upto'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node_set.rb:237:in `each'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogumbo-2.0.2/lib/nokogumbo/html5/node.rb:28:in `map'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogumbo-2.0.2/lib/nokogumbo/html5/node.rb:28:in `inner_html'",
"/var/www/discourse/lib/html_to_markdown.rb:74:in `block (2 levels) in hoist_line_breaks!'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node_set.rb:238:in `block in each'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node_set.rb:237:in `upto'",
"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/nokogiri-1.10.10/lib/nokogiri/xml/node_set.rb:237:in `each'",
"/var/www/discourse/lib/html_to_markdown.rb:57:in `block in hoist_line_breaks!'",
"/var/www/discourse/lib/html_to_markdown.rb:54:in `loop'",
"/var/www/discourse/lib/html_to_markdown.rb:54:in `hoist_line_breaks!'",
"/var/www/discourse/lib/html_to_markdown.rb:16:in `initialize'",
"/var/www/discourse/lib/email/receiver.rb:387:in `new'",
"/var/www/discourse/lib/email/receiver.rb:387:in `select_body'",
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:74:in `block (2 levels) in index_emails'", 
"/usr/local/lib/ruby/2.6.0/timeout.rb:108:in `timeout'",
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:70:in `block in index_emails'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:139:in `block (2 levels) in all_messages'",
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:171:in `block in each_mail'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:190:in `block in each_line'",
 "/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:189:in `each_line'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:189:in `each_line'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:166:in `each_mail'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:132:in `block in all_messages'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:125:in `foreach'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:125:in `all_messages'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:66:in `index_emails'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:25:in `block in execute'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:22:in `each'", 
"/var/www/discourse/script/import_scripts/mbox/support/indexer.rb:22:in `execute'", 
"/var/www/discourse/script/import_scripts/mbox/importer.rb:43:in `index_messages'", 
"/var/www/discourse/script/import_scripts/mbox/importer.rb:27:in `execute'", 
"/var/www/discourse/script/import_scripts/base.rb:47:in `perform'", 
"script/import_scripts/mbox.rb:12:in `<module:Mbox>'", 
"script/import_scripts/mbox.rb:10:in `<module:ImportScripts>'", 
"script/import_scripts/mbox.rb:9:in `<main>'"]

Come in precedenza, posso condividere il messaggio specifico se utile – questa volta il messaggio di errore mi fornisce i numeri di riga specifici, così possiamo almeno essere abbastanza sicuri di aver identificato il messaggio corretto.

4 Mi Piace

Certo, condividimi i messaggi e darò un’occhiata. Se c’è un problema, risolverlo non solo migliorerà l’importatore, ma anche Discourse stesso, che utilizza lo stesso parser per le email in arrivo.

1 Mi Piace

Ho eseguito questo script ogni giorno per diversi mesi per un sito che avrebbe davvero bisogno di passare all’iscrizione della categoria al gruppo, ma ciò non è stato ancora fatto. Funziona bene, tranne che ogni tanto devo ottenere un nuovo file cookies.txt. Circa un mese fa, è successo qualcosa e ha iniziato a lamentarsi con il messaggio: “Sembra che tu non abbia i permessi per vedere gli indirizzi email. Aborto.” Ho fatto… qualcosa… e ha ricominciato a funzionare. Poco più di una settimana fa è successo di nuovo e ho ridescritto i cookie con più browser/plugin per i cookie, continuando a ottenere la versione dei post senza email. Posso vedere gli indirizzi quando sono loggato con il browser web.

Qualcun altro ha avuto problemi ultimamente? Qualche idea su cosa fare? Ho provato a modificare quali domini sono presenti nella chiamata add_cookies nello script, ma non ha aiutato.

1 Mi Piace

Beh, sto guardando di nuovo questa cosa e sembra che link come

 https://groups.google.com/forum/message/raw?msg=GROUP_NAME/THREAD_ID/POST_ID

un tempo includessero gli indirizzi email completi, ma ora non lo fanno più. Posso confermare che quando sono loggato, posso cliccare su “About” e vedere gli indirizzi email completi nell’interfaccia web di Google Groups, ma se apri l’URL sopra, che lo script di scraping sta utilizzando nello stesso browser web, ottieni i dati con gli indirizzi email oscurati.

Il mio sospetto è che abbiano aumentato la privacy o qualcosa del genere.

Ecco un altro indizio: posso aprire quel link nel mio browser e funziona, ma se copio il comando “copy as cURL”, quel comando curl non ottiene gli indirizzi email. Sigh. Bene, ho provato con un altro browser e il comando curl ha funzionato. Non riesco proprio a capire perché lo script non ottenga gli indirizzi email.

Quindi forse c’è qualche altra cosa specifica del browser che sta facendo?

Non l’ho provato di recente, quindi è possibile che siano state apportate modifiche che lo scraper non riesce al momento a gestire.

@riking ha notato che Google Takeout esporta file mbox per i proprietari del gruppo, quindi potrebbe essere un’opzione da valutare.

5 Mi Piace

Grazie. Beh, una settimana fa ha funzionato per un secondo sito e poi ho aggiornato il mio file dei cookie un’ultima volta, e ha scaricato i dati per il primo sito. Sembra che abbia funzionato solo per un giorno o due, e ora non funziona più, né per l’uno né per l’altro sito. Vedo l’indirizzo email completo nel mio browser, scarico i cookie per quella scheda, ma senza successo.

Controllerò il takout. AGGIORNAMENTO: Beh, per ottenere il file mbox sembra che tu debba essere un amministratore super, non solo un proprietario.

4 Mi Piace

Uno strumento da riga di comando per convertire una mailing list Mailman2 (cioè il contenuto del file config.pck con opzioni, membri, moderatori, flag privati o pubblici, ecc.) in una categoria Discourse è disponibile qui: Client Challenge

1 Mi Piace

@gerhard hai qualche idea su come adattare queste istruzioni per usare un dev install invece dell’installazione standard? Sento di essere arrivato vicino a far funzionare una migrazione listserv usando solo pochi comandi, ma non riesco a far funzionare quello che presumo sia l’ultimo passaggio, usando nessuno dei due:

ruby /src/script/import_scripts/mbox.rb ~/import/settings.yml
bundle exec ruby /src/script/import_scripts/mbox.rb /home/discourse/import/settings.yml

Entrambi falliscono nell’importare tutte le dipendenze. Vedi qui per l’insieme completo di comandi che ho usato e gli errori. Qualche idea? Manca qualche chiamata a d/bundle forse?

La prossima cosa che proverò è usare una VM Ubuntu e fare un’“installazione standard” lì, ma questo sembra un po’ eccessivo dato che l’installazione dev funziona altrimenti molto bene.

Sono un completo neofita di discourse (e ruby, e soprattutto docker), quindi scusa se è ovvio o (peggio) irrilevante!

3 Mi Piace

Sembra che tu abbia capito la maggior parte delle cose.

Non ho mai provato questo metodo con l’installazione di sviluppo basata su Docker, ma credo che tu debba aggiungere "gem 'sqlite3'" al file Gemfile ed eseguire apt install -y libsqlite3-dev all’interno del container prima di lanciare d/bundle install.

Dopo di ciò, bundle exec ruby ... dovrebbe funzionare.

3 Mi Piace

@gerhard grazie per il gentile sollecito – ho rieseguito tutto da zero (da git clone in poi) aggiungendo gem 'sqlite3' alla fine di /src/Gemfile, dato che ho assunto fosse quello a cui ti riferivi, e ha funzionato! Per completezza, ecco le istruzioni che ho utilizzato (per la mailing list mne_analysis):

1. Nell’host Ubuntu

git clone https://github.com/discourse/discourse.git
cd discourse
d/boot_dev --init
d/rails db:migrate RAILS_ENV=development
d/shell
vim /src/Gemfile  # aggiungi gem 'sqlite3' alla fine
exit
d/bundle

2. Nella shell Docker

sudo mkdir -p /shared/import/data
sudo chown -R discourse:discourse /shared/import
wget -r -l1 --no-parent --no-directories "https://mail.nmr.mgh.harvard.edu/pipermail//mne_analysis/" -P /shared/import/data/mne_analysis -A "*-*.txt.gz"
rm /shared/import/data/mne_analysis/robots.txt.tmp
gzip -d /shared/import/data/mne_analysis/*.txt.gz
wget https://gist.githubusercontent.com/larsoner/940cd6c7100b87c4c5668cb0bc540afb/raw/9e78513620d11355ad0e10f4a2470996c26ebc8c/mailmanToMBox.py -O ~/mailmanToMBox.py
python3 ~/mailmanToMBox.py /shared/import/data/mne_analysis/
rm /shared/import/data/mne_analysis/*.txt
sudo apt install -y libsqlite3-dev  # per me nessun effetto

# verifica i risultati
cat /shared/import/data/mne_analysis/*.mbox > ~/all.mbox
sudo apt install -y procmail
mkdir -p ~/split
export FILENO=0000
formail -ds sh -c 'cat > ~/split/msg.$FILENO' < ~/all.mbox
rm -rf ~/split ~/all.mbox

# impostazioni
wget https://raw.githubusercontent.com/discourse/discourse/master/script/import_scripts/mbox/settings.yml -O /shared/import/settings.yml

# esegui
cd /src
bundle exec ruby script/import_scripts/mbox.rb /shared/import/settings.yml

Questo ha prodotto molte informazioni utili e alla fine:

...
Aggiornamento degli argomenti in evidenza nelle categorie
        5 / 5 (100.0%)  [6890 elementi/min]   ]  
Reset dei contatori degli argomenti


Completato (00h 06min 21sec)

Poi ho chiuso e, sull’host Ubuntu:

d/unicorn &
google-chrome http://0.0.0.0:9292

Fatto!

Probabilmente modificherò le impostazioni per eliminare il prefisso [Mne_analysis], ma sono entusiasta che funzioni già così bene!

4 Mi Piace

@gerhard Il tuo importatore mbox può essere utilizzato solo al momento dell’installazione iniziale di Discourse, oppure può essere impiegato in un secondo momento, anche dopo che altri utenti stanno già utilizzando Discourse? Se l’importatore viene utilizzato mentre Discourse è in uso da parte di altri utenti, questi ultimi subiranno qualche effetto collaterale?

1 Mi Piace

Per far sì che l’importatore estragga i messaggi da Google Groups, ho dovuto annullare questa modifica in /script/import_scripts/google_groups.rb
https://review.discourse.org/t/fix-google-groups-import-changed-login-url-9432/10615
Ho ripristinato la riga

    wait_for_url { |url| url.start_with?("https://accounts.google.com") }

alla versione originale:

    wait_for_url { |url| url.start_with?("https://myaccount.google.com") }

Altrimenti, ogni volta avrei ricevuto questo messaggio:

Accesso in corso...
Accesso fallito. Controlla il contenuto del tuo file cookies.txt
6 Mi Piace

@gerhard Ho notato che dopo l’importazione, sebbene i messaggi sembrino corretti, non ci sono affatto utenti in staging, anche se sembrerebbe che dovrebbero esserci (ho utilizzato l’impostazione predefinita staged: true). L’output è simile a questo:

...
indicizzazione delle risposte e degli utenti

creazione delle categorie
        1 / 1 (100.0%)  [13440860 elementi/min]  
creazione degli utenti

creazione degli argomenti e dei post
     7399 / 7399 (100.0%)  [1421 elementi/min]     
...

Dovrebbe essere visualizzato anche un contatore degli utenti?

Ho anche provato ad eseguire con staged: false e l’output è stato lo stesso; nessuno degli utenti della mailing list appartiene a gruppi. Nel caso in cui sia utile vedere cosa viene effettivamente elaborato, ecco uno dei tanti file .mbox in fase di importazione:

2020-December.zip (49.5 KB)

L’unica impostazione non predefinita è stata l’aggiunta di:

tags:
  "Mne_analysis": "mne_analysis"

Sarebbe ottimo vedere questi utenti in modo che possano rivendicare i loro vecchi post quando si registrano; quindi, qualsiasi consiglio o idea è molto apprezzato!

1 Mi Piace

Probabilmente dovrebbe accettare semplicemente entrambi?