Migración de base de datos vBulletin 5 - Errores en el script de importación

Ok, un rápido resumen.

Estoy colaborando en la migración de un foro que actualmente está en vbulletin3.
En un entorno de staging, partiendo de un volcado de la base de datos (20 GB, sí, has leído bien).

Ejecuté la actualización a vBulletin 5. Tardó 5-6 horas pero se completó. La versión es vBulletin 5.4.
Hice una limpieza de los nombres de usuario para que fueran aceptados por discourse.

Ahora, instalé docker discourse y seguí de forma general esta guía para la preparación. De forma general significa que la mayor parte era redundante o estaba desactualizada, pero ayudó a tener una idea de qué hacer.

Estoy en el paso en el que literalmente me estoy quedando ciego, ya que tengo casi cero experiencia en codificación Ruby.
Así que, las partes relevantes, después de terminar la instalación, entré en el contenedor con ./launcher enter app y luego:

  • Añadí freetds-dev y libmariadb-dev.
  • Edité el Gemfile para añadir el gem php_serialize.
  • Desde la shell, ejecuté export IMPORT=1 para establecer el entorno de importación.
  • Como usuario discourse, ejecuté bundle install --no-deployment --without test --without development --path vendor/bundle.

Obtuve el error:

You are trying to install in deployment mode after changing
your Gemfile. Run `bundle install` elsewhere and add the
updated Gemfile.lock to version control.

If this is a development machine, remove the /var/www/discourse/Gemfile freeze
by running `bundle config unset deployment`.

The list of sources changed
The dependencies in your gemfile changed

You have added to the Gemfile:
* mysql2
* redcarpet
* php_serialize
* sqlite3 (~> 1.3, >= 1.3.13)
* ruby-bbcode-to-md
* reverse_markdown
* tiny_tds
* csv
* parallel

Así que, continué con:

  • bundle config unset deployment y volví a ejecutar el comando anterior.
  • Comprobé que tanto mysql2 como php_serialize estaban presentes (lo estaban).
  • Añadí los avatares antiguos del foro (sin adjuntos que importar) y asigné la propiedad de los directorios al usuario discourse en su propio /home/discourse.
  • Edité script/import_scripts/vbulletin5.rb para cambiar la referencia de conexión a la base de datos.
  • Como usuario discourse, ejecuté bundle exec ruby script/import_scripts/vbulletin5.rb.

Esto me devolvió un error sobre tzinfo Integer values not supported que encontré mencionado aquí en este discourse.

Loading existing groups...
Loading existing users...
Loading existing categories...
Loading existing posts...
Loading existing topics...

importing groups...
       41 / 41 (100.0%)  [2294 items/min]  ]
importing users
Traceback (most recent call last):
        15: from script/import_scripts/vbulletin5.rb:726:in `<main>'
        14: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        13: from script/import_scripts/vbulletin5.rb:46:in `execute'
        12: from script/import_scripts/vbulletin5.rb:79:in `import_users'
        11: from /var/www/discourse/script/import_scripts/base.rb:916:in `batches'
        10: from /var/www/discourse/script/import_scripts/base.rb:916:in `loop'
         9: from /var/www/discourse/script/import_scripts/base.rb:917:in `block in batches'
         8: from script/import_scripts/vbulletin5.rb:98:in `block in import_users'
         7: from /var/www/discourse/script/import_scripts/base.rb:264:in `create_users'
         6: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
         5: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
         4: from /var/www/discourse/script/import_scripts/base.rb:265:in `block in create_users'
         3: from script/import_scripts/vbulletin5.rb:110:in `block (2 levels) in import_users'
         2: from script/import_scripts/vbulletin5.rb:718:in `parse_timestamp'
         1: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/tzinfo-2.0.5/lib/tzinfo/timezone.rb:575:in `utc_to_local'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/tzinfo-2.0.5/lib/tzinfo/timestamp.rb:138:in `for': Integer values are not supported (ArgumentError)

La sugerencia de @Haddoq fue cambiar una línea de Time.zone.at(@tz.utc_to_local(timestamp)) a Time.zone.at(timestamp).

También sugiere añadir lastvisit en la consulta de usuario porque de lo contrario causará otro error, así que lo hice también.

Sin embargo, ahora cuando lanzo la migración con bundle exec ruby script/import_scripts/vbulletin5.rb esto es lo que obtengo:

Loading existing groups...
Loading existing users...
Loading existing categories...
Loading existing posts...
Loading existing topics...

importing groups...
       41 / 41 (100.0%)  [120217 items/min]
importing users
Traceback (most recent call last):
        13: from script/import_scripts/vbulletin5.rb:727:in `<main>'
        12: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        11: from script/import_scripts/vbulletin5.rb:46:in `execute'
        10: from script/import_scripts/vbulletin5.rb:79:in `import_users'
         9: from /var/www/discourse/script/import_scripts/base.rb:916:in `batches'
         8: from /var/www/discourse/script/import_scripts/base.rb:916:in `loop'
         7: from /var/www/discourse/script/import_scripts/base.rb:917:in `block in batches'
         6: from script/import_scripts/vbulletin5.rb:80:in `block in import_users'
         5: from script/import_scripts/vbulletin5.rb:723:in `mysql_query'
         4: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:22:in `query'
         3: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:147:in `query'
         2: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:147:in `handle_interrupt'
         1: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:148:in `block in query'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:148:in `_query': You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'CASE WHEN u.scheme='blowfish:10' THEN token (Mysql2::Error)
                 WHEN u.scheme='lega' at line 2

En este punto estoy un poco perdido. ¿Alguien puede ayudar?

paging @Canapin ya que ha estado en “Migration Vietnam” y podría saber más :heart:

Trabajar 8 horas seguidas es malo.

Al añadir u.lastvisit al final de SELECT u.userid, u.username, u.homepage, u.usertitle, u.usergroupid, u.joindate, u.email, olvidé añadir una , después.

Lo siento Canapin por la notificación :frowning:

1 me gusta

Problema de seguimiento. La guía que estaba siguiendo indicaba que el proceso de registro podía reiniciarse si se ralentizaba.

Pero al reiniciarlo, obtengo errores sobre usuarios que ya existen en la base de datos postgres.

Tonto de mí, fui y eliminé a todos los usuarios con id > 1 en la base de datos (dejando básicamente el discobot, el sistema y el administrador) y reinicié. Eso hace que la importación continúe, pero el correo electrónico de todos los usuarios creados anteriormente está marcado como “ya en uso” en algún lugar.

¿Qué puedo hacer para limpiarlo y no debería este proceso en su lugar omitir la inserción si ya existe un nombre de usuario coincidente?

Editar: Bien, descubrí que necesito limpiar users, email_tokens y user_emails.

1 me gusta

Desafortunadamente, no hice un seguimiento de lo que he estado modificando en mis importaciones anteriores, por lo que tengo poco conocimiento. Ahora tengo una instancia personal de Discourse donde escribo este tipo de cosas… ¡Debería haberlo hecho antes!
Soy más hábil en la importación de cosas cuando estoy trabajando en una.

En cuanto a eliminar usuarios, es posible que hayas querido usar UserDestroyer a través de la consola de Rails:

2 Me gusta

Genial, lo tendré en cuenta para la migración “real”. Por ahora, solo estoy haciendo una prueba de todo el proceso mientras escribo un manual :slight_smile:

1 me gusta

¿Por qué hiciste eso? A menos que hayas cambiado algo en el script que importaría esos de manera diferente, deberías simplemente reiniciar y hacer que importe los que no importó ya.

Si necesitas empezar de nuevo, es mucho más fácil restaurar una copia de seguridad o eliminar y crear una nueva base de datos.

Debería encontrar los ids de importación en la tabla UserCustomFields. No estoy muy seguro de cómo podrías obtener ese error.

Los cambios son los que enumeré. Estoy tan sorprendido como tú, pero el script no progresaba y simplemente daba error y se detenía.

¿Hay alguna forma de acelerar la importación?

Tengo más de 90.000 usuarios en esta comunidad y la velocidad de importación se degrada con el tiempo por alguna razón y no me imagino por qué.

Se ejecutó toda la noche y solo para los usuarios estamos en 25123 / 95635 ( 26.3%) [42 elementos/min]

Hay varios órdenes de magnitud más de publicaciones. ¿Cuánto tiempo debería esperar que ocurra una migración? ¿Días? ¿Semanas?

¿Cuánta RAM? Ese es probablemente el problema. Puedes detener y reiniciar.

Me han tardado semanas. Por eso existen los importadores masivos.

Solo tiene 2 GB de RAM. Es una máquina de prueba. Podría ejecutarlo localmente en lugar de una VM (¿16 GB de RAM serían mucho mejores?) y luego empaquetar todo y subirlo eventualmente, supongo.

¿Puedes dar más detalles sobre los importadores masivos? Es la primera vez que oigo hablar de ellos y definitivamente deberían haber aparecido cuando buscaba “migrar vbulletin a discourse” en Google :angry:

GIF de Jason Segel frustrado de NETFLIX

¿Puedo ejecutar esto incluso si el otro importador ya procesó algunos usuarios o debería limpiar?

Fui y lo intenté, en el peor de los casos no funcionará. Me están bombardeando con

ERROR: no implicit conversion of nil into String
/var/www/discourse/script/bulk_import/base.rb:861:in `encode'
/var/www/discourse/script/bulk_import/base.rb:861:in `normalize_charset'
/var/www/discourse/script/bulk_import/base.rb:856:in `normalize_text'
script/bulk_import/vbulletin5.rb:123:in `block in import_users'
/var/www/discourse/script/bulk_import/base.rb:725:in `block (2 levels) in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/script/bulk_import/base.rb:723:in `block in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:196:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:722:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:340:in `create_users'
script/bulk_import/vbulletin5.rb:120:in `import_users'
script/bulk_import/vbulletin5.rb:63:in `execute'
/var/www/discourse/script/bulk_import/base.rb:100:in `run'
script/bulk_import/vbulletin5.rb:781:in `<main>'

Supongo que debería limpiar los usuarios y grupos que son las únicas cosas que el otro importador creó/comenzó a crear.

Antes de volver a meterme en la base de datos, ¿hay algún comando de ruby que pueda ejecutar que se encargue de eso de forma limpia?

Por esta vez, simplemente destruiré y recrearé la instalación de discourse. Gracias al cielo por Docker.

No, instalación limpia, el script todavía falla con este genérico

ERROR: no implicit conversion of nil into String
/var/www/discourse/script/bulk_import/base.rb:861:in `encode'
/var/www/discourse/script/bulk_import/base.rb:861:in `normalize_charset'
/var/www/discourse/script/bulk_import/base.rb:856:in `normalize_text'
script/bulk_import/vbulletin5.rb:123:in `block in import_users'
/var/www/discourse/script/bulk_import/base.rb:725:in `block (2 levels) in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/script/bulk_import/base.rb:723:in `block in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:196:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:722:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:340:in `create_users'
script/bulk_import/vbulletin5.rb:120:in `import_users'
script/bulk_import/vbulletin5.rb:63:in `execute'
/var/www/discourse/script/bulk_import/base.rb:100:in `run'
script/bulk_import/vbulletin5.rb:781:in `<main>'

¿Hay alguna forma de depurar esto para entender al menos qué valor es nil y se espera que sea otra cosa?

Ok, el error parece estar relacionado con la codificación.
En bulk_import/vbulletin5.rb puedo especificar la codificación (en nuestro caso, UTF8mb4 pero en el archivo base.rb no parece mapear a nada en el mapa de caracteres

Hola

Algunos consejos generales:

  1. Si haces cambios en el script, generalmente es aconsejable empezar desde cero o al menos desde un punto conocido y bueno. Puedes hacerlo más fácilmente restaurando una copia de seguridad tomada justo antes de ejecutar la migración, como dijo @pfaffman.
  2. Los importadores masivos generalmente tardarán mucho menos, pero este en particular tiene el potencial de consumir ENORMES cantidades de RAM porque almacena cosas en la memoria. Para una migración masiva particular de vBulletin que hice a partir de un archivo SQL de 2 GB sin comprimir, el proceso necesitó 22 GB de RAM (doble comprobación, no es un error tipográfico).
  3. Si haces cambios en el script, te sugiero que crees una versión de prueba de la entrada con, digamos, 100 o 1000 registros para cada tabla (pero ten cuidado con la integridad referencial, es decir, no trunques tablas referenciadas por otras tablas). Probar cambios con un proceso de más de 8 horas erosionará tu cordura muy rápidamente.

Un consejo más específico sobre los rastreos de pila: busca líneas que mencionen el archivo específico que ejecutaste. En este caso, sí, es un problema de codificación, pero lo más relevante es el hecho de que se trata de nombres de usuario:

Dijiste que tuviste que sanear los nombres de usuario, así que verificaría dos veces si los codificaste como se esperaba en el script.

2 Me gusta

Puedes simplemente soltar, crear y migrar la base de datos en lugar de recrear Discourse. Sin embargo, es un poco complicado, ya que tienes que

  sv stop unicorn

y luego

  rake db:drop db:create db:migrate

Se quejará y te dirá que establezcas una variable de entorno que necesitas poner en la línea anterior a la tarea de rake.

También puedes restaurar una copia de seguridad, lo que puede ser más conveniente.

Para tu información, no creo haber utilizado nunca un script de migración masiva.

1 me gusta

Eso es aún más extraño, ya que los nombres de usuario se han saneado para seguir las directrices de Discourse, básicamente se han cambiado para que sean solo letras, números o _, nada más.

En cualquier caso, cambiar el conjunto de caracteres de utf8mb4 a utf8, ya que era el predeterminado, hizo que funcionara, pero ahora estoy recibiendo errores de correos electrónicos no válidos.

ERROR: can't modify frozen String: "24ef401b30f5161e5a0bb27ec49ed921@email.invalid"
/var/www/discourse/script/bulk_import/base.rb:457:in `downcase!'
/var/www/discourse/script/bulk_import/base.rb:457:in `process_user_email'
/var/www/discourse/script/bulk_import/base.rb:726:in `block (2 levels) in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/script/bulk_import/base.rb:723:in `block in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:196:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:722:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:351:in `create_user_emails'
script/bulk_import/vbulletin5.rb:151:in `import_user_emails'
script/bulk_import/vbulletin5.rb:66:in `execute'
/var/www/discourse/script/bulk_import/base.rb:100:in `run'
script/bulk_import/vbulletin5.rb:781:in `<main>'

Ahora voy a investigar de qué se trata esto, ya que la importación “no masiva” detectaba algunos correos electrónicos mal formados pero los reemplazaba automáticamente.

Después de hacerlo, simplemente crea una copia de seguridad de tu instalación de Discourse vacía y prístina.
Siempre puedes restaurarla muy rápidamente y empezar de nuevo.

2 Me gusta

Además, si tiene problemas, por ejemplo, con la importación de publicaciones, también puede salir del script una vez que la importación de usuarios se haya completado satisfactoriamente y crear otra copia de seguridad. Luego puede reiniciar el script y continuar desde el momento en que ya ha importado los usuarios.

1 me gusta