Completata la migrazione da vB3 a Discourse, inclusa la versione legacy vB3: grazie ai like

Ho appena completato, per la maggior parte (ancora in fase di test, ma le parti difficili sono sostanzialmente finite e la migrazione funziona bene), la migrazione di un forum vb3 vecchio di 15 anni, che originariamente (credeteci o no) era un forum vb2 del 2000.

Questo forum esiste da circa 20 anni (con codice PHP personalizzato inimmaginabile, tabelle, plugin, ecc.), e Discourse è, a mio avviso, l’unico software per forum “degno” di un progetto di migrazione, che ho avviato circa una settimana fa, passando dalla fattibilità al completamento (sostanzialmente).

Prima di tutto, voglio ringraziare l’intero team di Discourse per un tale straordinario “capolavoro” di forum moderno. Discourse è davvero straordinario, a mio parere (e sono sicuro che lo sia anche per molti altri).

In secondo luogo, vorrei ringraziare il team che ha codificato lo script di migrazione originale vbulletin.rb. Questo script era circa al 95% corretto. L’ho modificato per farlo funzionare correttamente con vb3. Ad esempio, vb3 non ha una tabella filedata come vb4 (relativa agli allegati); quindi ho modificato vbulletin.rb per farlo funzionare. Inoltre, c’erano problemi nell’importare i forum figli vb3 come categorie, ma ho modificato il codice per importare tutti i forum come categorie di primo livello e poi ho scritto un po’ di postgres psql per creare nuove relazioni genitore-figlio tra le categorie (o configurate manualmente). C’erano altre “sorprese” (una storia per un’altra giornata), ma quei goblin hanno reso il tutto divertente e sfidante, ma non insormontabile.

In terzo luogo, vorrei ringraziare Sam e il suo codice lithium.rb, dove ho usato la sua routine import_likes come base per il mio ringraziamento vb3 (da un plugin legacy, non parte di vB OOTB) per trasformarlo in like di Discourse. Ho apportato piccole modifiche alla routine import_likes e, dopo alcune ore di debug, sono riuscito a farla funzionare.

Come potete vedere da questo post del 2018, migrato oggi:

Ho ritenuto importante che i ringraziamenti vb degli utenti venissero migrati nei like di Discourse, quindi ecco il codice che ho usato per migrare i like per altre anime perse di forum vb3:

Per prima cosa ho creato una tabella mysql vb3 chiamata user_actions e ho scritto uno script PHP per riempirla partendo dalla tabella legacy vb ```post_thanks``, in questo modo (non rifinito, ma funzionante):

ubuntu:/var/www/includes/cron# cat thanks_to_discourse.php
 <?php

/**************************************************
+-----------+------------+-----------+-----------+
| thanker   | unixtime   | id        | thanked   |
+-----------+------------+-----------+-----------+
|         1 | 1584149592 | 303045211 | 302093876 |
| 302153369 | 1584136706 | 303045214 | 302116191 |
| 302108573 | 1584128526 | 303045211 | 302093876 |
| 302153369 | 1584126659 | 303042175 | 302116191 |
| 302153369 | 1584126400 | 303045174 | 302116191 |
| 302153369 | 1584117711 | 303045184 |         1 |
|     37898 | 1584108187 | 303045175 |         1 |
| 302181242 | 1584106664 | 303045201 | 302122047 |
| 302181242 | 1584104642 | 303045074 | 302052697 |
| 302025710 | 1584103722 | 303045184 |         1 |
+-----------+------------+-----------+-----------+
 **************************************************/

$query = 'SELECT p.threadid AS threadid,t.userid AS thanker,t.date AS unixtime,t.postid AS postid,p.userid AS thanked from post_thanks as t LEFT JOIN post p ON p.postid = t.postid';

$allthanks = $vbulletin->db->query_read($query);

/****************************************
mysql> describe user_actions;
+-----------------+------------------+------+-----+---------+----------------+
| Field           | Type             | Null | Key | Default | Extra          |
+-----------------+------------------+------+-----+---------+----------------+
| id              | int(11)          | NO   | PRI | NULL    | auto_increment |
| action_type     | int(11) unsigned | NO   |     | NULL    |                |
| user_id         | int(11) unsigned | NO   |     | NULL    |                |
| target_topic_id | int(11) unsigned | YES  |     | NULL    |                |
| target_post_id  | varchar(16)      | YES  |     | NULL    |                |
| target_user_id  | int(11) unsigned | YES  |     | NULL    |                |
| acting_user_id  | int(11) unsigned | YES  |     | NULL    |                |
| created_at      | timestamp        | YES  |     | NULL    |                |
| updated_at      | timestamp        | YES  |     | NULL    |                |
+-----------------+------------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
 *************************************/

$action_code = 2; // discourse like action == 2
$target_user_id = "";
while ($action = $vbulletin->db->fetch_array($allthanks)) {

    #code to deal with how discourse identifies the first post in a topic in the post_custom_fields table
    $query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
    $threadinfo = $vbulletin->db->query_first($query);
    if ($threadinfo['firstpostid'] == $action['postid']) {
        $action['postid'] = 'thread-' . $action['threadid'];
    }

    $update = 'INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, target_user_id,acting_user_id, created_at, updated_at)' .
        ' VALUES (' . $action_code . ',' .
        '"' . $action['thanked'] . '",' .
        '"' . $action['threadid'] . '",' .
        '"' . $action['postid'] . '",' .
        '"' . $target_user_id . '",' .
        '"' . $action['thanker'] . '",' .
        'FROM_UNIXTIME(' . $action['unixtime'] . '),' .
        'FROM_UNIXTIME(' . $action['unixtime'] . '))';
    $doit = $vbulletin->db->query_write($update);
}

Quindi, questa tabella mysql user_actions è inclusa nel dump della migrazione.

Ecco la routine import_likes modificata che ho usato per completare la migrazione sul lato Discourse:

def import_likes
    puts "\nimporting likes..."

    # created mysql user_actions table in vb3 using PHP script and included that table with migration dump
    sql = "select acting_user_id as user_id, target_post_id as post_id, created_at from user_actions"
    results = mysql_query(sql)
    puts "length() method form : #{results.count}\n\n"

    puts "skip loading unique id map"
    existing_map = {}
    PostCustomField.where(name: 'import_id').pluck(:post_id, :value).each do |post_id, import_id|
      existing_map[import_id] = post_id
      #puts "postcustomfield existing_map post_id: #{post_id} import_id #{import_id}\n"
    end

    puts "loading data into temp table"

    #manually created the temp like_data table so I could check the table after session ends
    #DB.exec("create temp table like_data(user_id integer, post_id integer, created_at timestamp without time zone)")
    
    puts "like_data temp table created"
    PostAction.transaction do
      results.each do |result|

        result["user_id"] = user_id_from_imported_user_id(result["user_id"].to_s)
        result["post_id"] = existing_map[result["post_id"].to_s]

        next unless result["user_id"] && result["post_id"]

        puts "insert like table user_id: #{result["user_id"]} post_id #{result["post_id"]}\n"

        DB.exec("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)",
          user_id: result["user_id"],
          post_id: result["post_id"],
          created_at: result["created_at"]
        )

      end
    end


    puts "creating missing post actions"
    DB.exec <<~SQL

    INSERT INTO post_actions (post_id, user_id, post_action_type_id, created_at, updated_at)
             SELECT l.post_id, l.user_id, 2, l.created_at, l.created_at FROM like_data l
             LEFT JOIN post_actions a ON a.post_id = l.post_id AND l.user_id = a.user_id AND a.post_action_type_id = 2
             WHERE a.id IS NULL
    SQL

    puts "creating missing user actions"
    DB.exec <<~SQL
    INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
             SELECT pa.user_id, 1, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
             FROM post_actions pa
             JOIN posts p ON p.id = pa.post_id
             LEFT JOIN user_actions ua ON action_type = 1 AND ua.target_post_id = pa.post_id AND ua.user_id = pa.user_id

             WHERE ua.id IS NULL AND pa.post_action_type_id = 2
    SQL

    # reverse action
    DB.exec <<~SQL
    INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
             SELECT p.user_id, 2, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
             FROM post_actions pa
             JOIN posts p ON p.id = pa.post_id
             LEFT JOIN user_actions ua ON action_type = 2 AND ua.target_post_id = pa.post_id AND
                ua.acting_user_id = pa.user_id AND ua.user_id = p.user_id

             WHERE ua.id IS NULL AND pa.post_action_type_id = 2
    SQL
    puts "updating like counts on posts"

    DB.exec <<~SQL
        UPDATE posts SET like_count = coalesce(cnt,0)
                  FROM (
        SELECT post_id, count(*) cnt
        FROM post_actions
        WHERE post_action_type_id = 2 AND deleted_at IS NULL
        GROUP BY post_id
    ) x
    WHERE posts.like_count <> x.cnt AND posts.id = x.post_id

    SQL

    puts "updating like counts on topics"

    DB.exec <<-SQL
      UPDATE topics SET like_count = coalesce(cnt,0)
      FROM (
        SELECT topic_id, sum(like_count) cnt
        FROM posts
        WHERE deleted_at IS NULL
        GROUP BY topic_id
      ) x
      WHERE topics.like_count <> x.cnt AND topics.id = x.topic_id

    SQL
    end
end

Spero che questo post sia utile ad altri che potrebbero migrare da vB3 a Discourse, dato che molte persone hanno usato per anni il plugin legacy vb3 “ringraziamento”.

Inoltre, ho trascorso molto tempo nel database di Discourse e all’interno di molti script ruby di Discourse, quindi questa migrazione è stata delicata (non certo per i deboli di cuore), ma certamente fattibile.

In realtà, mi è piaciuto farlo. Questa è stata la prima volta per me in ruby; quindi è stato divertente iniziare a imparare ruby e, come persona abituata a mysql da molto tempo, la parte postgres è stata per lo più di routine.

Andremo online presto… Grazie al team di Discourse!!!

Ben fatto e grazie per aver condiviso!!

Grazie @codinghorror

Ho appena individuato un piccolo gremlin che sto per risolvere. La routine import_likes non sta trasferendo i “mi piace” sul primo post (quello dell’argomento), ma solo sulle risposte.

Dovrò mettermi al lavoro per capire come risolvere e poi pubblicare qui la routine import_likes aggiornata.

Sembra che fossi un po' troppo frettoloso nel pubblicare, dato che c’è ancora un altro piccolo goblin da sconfiggere prima di poter dichiarare vittoria.

Aggiornamento:

Ho aggiunto questo codice allo script PHP su vB3 per compensare la differenza tra il modo in cui vB3 e Discourse gestiscono il primo post in un argomento/thread:

    <?php
    # Codice vB3 per gestire come Discourse identifica il primo post in un argomento nella tabella post_custom_fields
    $query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
    $threadinfo = $vbulletin->db->query_first($query);
    if ($threadinfo['firstpostid'] == $action['postid']) {
        $action['postid'] = 'thread-' . $action['threadid'];
    }

Ora funziona come previsto e i “grazie ricevuti” in vB3 vengono migrati su Discourse, incluso il primo post nell’argomento; modificando la tabella di migrazione da vB3 per allinearla alla tabella post_custom_fields di Discourse.

Sto eseguendo il test finale, ma sembra che funzionerà, basandomi sui test preliminari.

Inoltre, ho aggiornato il codice nel mio post originale aggiungendo il frammento PHP sopra.

Ho letto che non state migrando le password perché gli hash sono diversi. Potreste dare un’occhiata al mio plugin per la migrazione delle password (qui). Probabilmente supporta già gli hash VB3, e se non lo facesse, sono disponibile ad aggiungerne il supporto.

Grazie Michael,

Non intendiamo migrare le password e preferiamo che gli utenti le cambino (optando per password più robuste e lunghe).

Comunque, grazie per l’offerta di aiuto! Molto apprezzato.

Abbiamo però un piccolo problema su cui forse puoi aiutaci.

Per qualche motivo, il numero totale di “like” ricevuti dagli utenti non viene aggiornato correttamente nelle visualizzazioni del profilo, anche se tutto il resto sembra funzionare bene. Ho provato a scavare nel problema, ma i miei tentativi non mi stanno dando i risultati che mi aspetto.

Conosci un buon modo per aggiornare manualmente questi contatori di “like” del profilo?

Quale rake task hai utilizzato? E qual è il problema? I numeri sono tutti completamente errati, o solo per alcuni utenti, o…?

Ciao.

Per cominciare, ho provato l’unico rake che aveva senso per me (all’epoca, dato che il problema era legato a user_actions e post_actions), ma non ha dato alcun risultato (in realtà, a posteriori, avrei dovuto leggere quel rake per vedere esattamente cosa facesse prima, LOL):

rake user_actions:rebuild    

Finora, francamente, non ho ancora determinato cosa vada storto nello script di migrazione import_likes. Se sapessi esattamente cosa e dove va storto, avrei potuto risolverlo (spesso passo ore o giorni a trovare un problema che, una volta individuato, richiede solo un minuto per essere risolto… l’essenza della programmazione, LOL). Ho ripristinato da uno snapshot diverse volte e ho rieseguito la routine import_likes in vari modi (aggiungendo dichiarazioni puts extra, ecc.) e ho esaminato le tabelle. I risultati sono stati inconcludenti. Questo è aggravato dalla mia inesperienza in ruby e in discourse in generale, poiché non sono ancora fluente nel backend. (Tuttavia, sto migliorando con begin... rescue ... end… haha, che è diventato uno dei miei nuovi strumenti di debug come rubynube).

Ciò che ho osservato è che la tabella temporanea postgres like_data (sul lato discourse) è corretta e contiene tutti i dati nel formato giusto; le voci corrispondono sia alla tabella di migrazione mysql che al sito vB.

Il processo sembra bloccarsi nella creazione di user_actions e forse anche post_actions, perché i miei test hanno mostrato che i dati non vengono trasferiti correttamente in queste tabelle chiave.

Si prega di notare che la migrazione principale vB3 è corretta. Questo problema è legato a un plugin legacy vB3 “grazie” che sto convertendo in like di Discourse. Il mio prossimo passo, quando avrò tempo più tardi oggi, spero, sarà sedermi e cercare di capire esattamente dove si interrompe il processo durante il trasferimento dalla tabella like_data (che è corretta) a user_actions e post_actions.

La mia conclusione preliminare, basata sui test di ieri, è che ruby raking non aiuterà, poiché il problema risiede nel processo di aggiornamento delle tabelle user_actions o post_actions; quindi devo determinare esattamente dove e perché si verifica l’interruzione. Potrebbe essere semplice come un disallineamento dei tipi di database o qualche altro piccolo “gremlin” nel codice.

Il nostro sito Discourse non è ancora attivo, quindi non è un emergenza e, come molte persone qui e altrove, compiti routinari come occuparsi di immigrazione o banche sono diventati più che un po' problematici, per non dire altro, e oggi devo tornare in città per alcune attività non legate a Discourse. Preferirei essere qui a esplorare discourse e a cercare code gremlins.

Ho eseguito di nuovo lo script di migrazione (senza apportare modifiche allo script) includendo la conversione personalizzata da vb3 thanks a Discourse likes (import_likes), e sembra che ora vada tutto bene.

Proverò a sincronizzare l’ultimo dump del database vB (il grande “D”) e vediamo cosa succede!

Sono molto soddisfatto di Discourse… Grazie ancora per questo “capolavoro” di forum moderno. Sto ancora facendo dei test, ma lo metterò online il prima possibile.

5,6k like ricevuti… È tantissimo amore :heart:

Dopo aver convertito tutti i vb thanks in discourse likes, ecco un esempio di post migrato dal 2000, vent’anni fa, che mostra i like migrati nell’argomento:

… e sto iniziando a non vedere l’ora di imparare a scrivere plugin interessanti… :slight_smile:

Sono molto soddisfatto di Discourse e del team… e soprattutto di begin .... rescue ...puts "guarda me".... end in ruby… che salvataggio!

È vero… i cani vecchi possono imparare nuovi trucchi se gli lanci qualche osso :slight_smile:

Grazie al TEAM Discourse! 20 anni fa, abbiamo avviato un forum per utenti Unix e Linux su vB2 e circa 15 anni fa, siamo “passati” a vB3. Oggi segna un grande cambiamento per gli utenti Unix e Linux in tutto il mondo e per noi, con la migrazione a Discourse ora attiva.

Grazie mille per aver creato questo software e per averlo reso disponibile a tutti come progetto open source. La vostra generosità è molto apprezzata. A mio avviso (e sono sicuro che lo pensino innumerevoli altri), Discourse è di gran lunga il miglior software per forum sul pianeta nel 2020.

Per quanto riguarda gli altri utenti legacy di vB3 che desiderano migrare a Discourse, questa migrazione non è così “semplice” come potrei aver fatto apparire. A meno che non siate esperti di programmazione e a vostro agio con l’integrazione diretta con i vostri database da riga di comando, vi consiglio di valutare i servizi di alcuni dei professionisti talentuosi e attivi qui su meta.discourse.org.

Grazie ancora, Team Discourse!

Nota: Aggiornerò questo post con alcune lezioni apprese sul nuovo sito: