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

Sí, voy a hacer eso si consigo completar al menos la importación de usuarios. Actualmente está fallando al intentar trabajar en los correos electrónicos

Simplemente elimina la primera línea del script script/bulk_import/vbulletin5.rb
# frozen_string_literal: true

2 Me gusta

De acuerdo, ejecutando solo las tres primeras funciones:

 def execute
    # habilitar según el requisito:
    #SiteSetting.automatic_backups_enabled = false
    #SiteSetting.disable_emails = "non-staff"
    #SiteSetting.authorized_extensions = '*'
    #SiteSetting.max_image_size_kb = 102400
    #SiteSetting.max_attachment_size_kb = 102400
    #SiteSetting.clean_up_uploads = false
    #SiteSetting.clean_orphan_uploads_grace_period_hours = 43200
    #SiteSetting.max_category_nesting = 3

    import_groups
    import_users
    import_group_users

    #import_user_emails
    #import_user_stats
    #import_user_profiles
    #import_user_account_id

    #import_categories
    #import_topics
    #import_topic_first_posts
    #import_replies

    #import_likes

    #import_private_topics
    #import_topic_allowed_users
    #import_private_first_posts
    #import_private_replies

    #create_oauth_records
    #create_permalinks
    #import_attachments
  end

Resultado:

¿Supongo que ese mensaje sobre garantizar la coherencia es para cuando se complete la importación completa? ¿O debería ejecutarlo en cada “paso” que ejecuto y luego hacer una copia del directorio discourse del host para tener una copia de seguridad?

1 me gusta

Lanzarlo de nuevo con las siguientes 4 funciones activas devuelve un error para ids que ya existen

¿Puede ser esto un “todo o nada”? ¿Quizás espera que todo se haga en una gran transacción?

Volví a intentarlo. El proceso continuó durante bastante tiempo y de repente esto.

La parte frustrante es que parece que fue de la nada.
Ahora, al volver a ejecutarlo, aparece este error.

Estoy demasiado cansado ahora para comprobar a qué se refiere. Especialmente porque duplicate key value (valor de clave duplicada) no debería ocurrir en absoluto si simplemente volví a ejecutar el script de importación masiva, ¿verdad?

Me gustaría empezar pidiendo disculpas a cualquiera que se sienta atacado por esta publicación porque, para ser honesto, he estado lidiando con estos problemas desde el lunes y, en este punto, estoy cansado de depurar/corregir el código de Discourse.

Después del enésimo intento (dejé de contar después del séptimo), creo que me rendiré porque parece que la migración no es algo en lo que Discourse haya invertido mucho tiempo para admitir.

Creo que el mayor problema es que el conjunto de caracteres utilizado en esta enorme base de datos es utf8mb4, que no es compatible con el script (¿?).

Usar utf8 (predeterminado) simplemente genera muchos errores que se informan, pero no está claro qué está sucediendo, ya que el script continúa de todos modos. ¿Se omite la entrada en la base de datos? ¿Se copia con caracteres no compatibles (los cuadrados clásicos)?

Además de eso, las tres últimas ejecuciones diferentes (utilizando los importadores masivos), siguiendo exactamente el mismo conjunto de instrucciones, tienen resultados diferentes. Esta última ejecución llegó a la importación de temas, comenzó a informar errores de inmediato pero continuó (???):

Cargando aplicación...
Iniciando...
Pre-cargando I18n...
Corrigiendo los números de publicación más altos...
Cargando IDs de grupos importados...
Cargando IDs de usuarios importados...
Cargando IDs de categorías importadas...
Cargando IDs de temas importados...
Cargando IDs de publicaciones importadas...
Cargando índices de grupos...
Cargando índices de usuarios...
Cargando índices de categorías...
Cargando índices de temas...
Cargando índices de publicaciones...
Cargando índices de acciones de publicaciones...
Importando categorías...
Importando categorías principales...
      5 -   1104/segERROR:  la violación de la restricción de clave única \"unique_index_categories_on_name\"
DETALLE:  La clave (COALESCE(parent_category_id, '-1'::integer), name)=(-1, Armata Brancaleone) ya existe.
CONTEXTO: COPY categories, line 69
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:204:in `get_last_result'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:204:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:361:in `create_categories'
script/bulk_import/vbulletin5.rb:291:in `import_categories'
script/bulk_import/vbulletin5.rb:69:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'
Importando temas...
    600 -   4073/seg
ERROR: método indefinido `[]' para nil:NilClass
/var/www/discourse/script/bulk_import/base.rb:513:in `process_topic'
/var/www/discourse/script/bulk_import/base.rb:724: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:721: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:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:364:in `create_topics'
script/bulk_import/vbulletin5.rb:321:in `import_topics'
script/bulk_import/vbulletin5.rb:70:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'

Hasta que finalmente se bloquea en este:

script/bulk_import/vbulletin5.rb:779:in `<main>'
 572329 -    531/seg
Importando respuestas...
client_loop: send disconnect: Connection reset

Pero no sin antes spammar constantemente estos dos errores:

ERROR: método indefinido `gsub!' para nil:NilClass
script/bulk_import/vbulletin5.rb:727:in `preprocess_raw'
script/bulk_import/vbulletin5.rb:369:in `block in import_topic_first_posts'
/var/www/discourse/script/bulk_import/base.rb:723: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:721: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:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:367:in `create_posts'
script/bulk_import/vbulletin5.rb:361:in `import_topic_first_posts'
script/bulk_import/vbulletin5.rb:71:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'

y

ERROR: secuencia de bytes inválida en UTF-8
script/bulk_import/vbulletin5.rb:727:in `gsub!'
script/bulk_import/vbulletin5.rb:727:in `preprocess_raw'
script/bulk_import/vbulletin5.rb:369:in `block in import_topic_first_posts'
/var/www/discourse/script/bulk_import/base.rb:723: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:721: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:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:367:in `create_posts'
script/bulk_import/vbulletin5.rb:361:in `import_topic_first_posts'
script/bulk_import/vbulletin5.rb:71:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'

Tenga en cuenta que he ido paso a paso comentando qué función ejecutar, luego ejecutando rake import:ensure_consistency antes de continuar comentando las que ya se ejecutaron y así sucesivamente, porque si simplemente dejo que todo el script vuelva a ejecutar pasos anteriores, simplemente falla al encontrar IDs duplicados.

Antes de que salga el argumento habitual de “no puedes quejarte del software libre”, quiero aclarar que estoy contribuyendo a otros proyectos de código abierto y también estoy creando software gratis, pero para mí es primordial que si lanzo algo, ese algo funcione y esté bien documentado (incluso solo para evitar los miles de mensajes que preguntan legítimamente ‘cómo funciona esto’) o estoy listo para corregir cualquier error que surja.

Si bien Discourse parece tener una gran experiencia lista para usar, debe quedar claro que estamos en 2022 y las comunidades existieron mucho antes que este producto. La “adopción” necesitaría un sólido soporte de migración y no parece ser el estado actual de Discourse.

Reconozco que una base de datos de 20 GB es un caso extremo, pero aquí no tenemos problemas con el tamaño, sino con el conjunto de caracteres o quién sabe qué, ya que ni siquiera hay un error constante y la mayoría de la información: no hay documentación más allá de buscar hilos y publicaciones dejadas por quienes han pasado por la misma terrible experiencia en el pasado, esperando que se encontrara una solución y que el código fuente no haya cambiado mucho desde entonces.

En este punto, recomendaría encarecidamente a cualquiera que venga de vBulletin que espere cualquier migración hasta que se complete lo que parece ser una revisión del script de migración (¿en curso, al parecer?).

Si bien entiendo tu dolor (las migraciones son un tema complicado), como Especialista en Migraciones de Discourse, permíteme aclarar las cosas.

Tenemos un marco de migración maduro con más de 60 scripts para diferentes plataformas, un marco de importación masiva separado con 5 scripts y un marco más nuevo en desarrollo que mejora masivamente en todos los aspectos: rendimiento, organización del código, capacidad de prueba, verificabilidad, documentación, etc.

Tenemos un equipo de Migraciones separado con un amplio soporte de desarrolladores principales, y contribuimos con mejoras genéricas al código con cada migración que completamos. Constantemente realizamos migraciones para clientes, que van desde triviales hasta increíblemente complejas.

Nuestro objetivo final es hacer que las migraciones sean lo más fluidas posible tanto para los clientes alojados como para la comunidad, pero la cantidad de código que está en el ámbito de una migración es simplemente demasiado masiva, y la configuración del software a nivel del sistema, los cambios en el software de terceros y la variabilidad de los datos de entrada solo complican el problema.

Nuevamente, desearía que todas estas cosas fueran menos dolorosas, pero hacerlas así requiere incontables horas de trabajo para crear y mantener, y solo hay un número limitado de personas disponibles.

¡No te rindas! :slight_smile:

5 Me gusta

Aprecio y entiendo que el alcance es inmenso. Es simplemente frustrante seguir tropezando con una excepción tras otra y el hecho de que el proyecto esté escrito en ruby no ayuda a encontrar ayuda más allá de venir aquí, lo cual, como dices, no puede acomodar todas las solicitudes de ayuda, ya que algunos tienen casos muy específicos con los que es simplemente imposible ayudar a menos que se tenga acceso a los datos reales.

También, en gran medida, culpo al absoluto desastre que es la estructura de vbulletin.

Acabo de revisar esta mañana y este es un resumen de los tamaños de las tablas.

Para dar contexto, la tabla “text” es donde está el contenido real.
la tabla node contiene la jerarquía y closure… déjame citar aquí porque ni siquiera puedo:

La tabla Closure construye las relaciones padre-hijo entre todos los nodos. La mayoría de tu base de datos está compuesta por archivos adjuntos que de todos modos no deberían almacenarse en la base de datos.

Así que, en general, para un foro con ~8 GB de contenido, hay una sobrecarga de 28 GB. Genial, felicidades vbulletin.

2 Me gusta

Esto es lo que quiero decir cuando digo que es frustrante.

De nuevo, el mismo conjunto de acciones (siguiendo un manual escrito por mí con todos los ensayos y errores), ejecutándose en una nueva instalación de Discourse.

Resultado:

Tired Tv Land GIF by TV Land Classic

¿Dónde está import_user_account_id? :expressionless:
Pero lo más importante? ¿Cómo te las arreglaste para no causar un error en la ejecución anterior donde falló en la importación del tema? :confounded:

Comentando esa invocación de función (que de todos modos parece que era importante) y lanzando de nuevo:

Esos errores de claves duplicadas… ¿no debería el script saber que ya ha procesado esas id y seguir adelante?

Cada importación es diferente. Podrías pensar que un script que funciona para una instancia de tu-foro-anteriormente-favorito simplemente funcionará, pero no es así. Y para un foro enorme, es realmente difícil. Simplemente no es algo que sea fácil de admitir. Y los importadores masivos acceden directamente a la base de datos en lugar de depender de Rails para poder verificar automáticamente las cosas a medida que avanzan.

Ese es un problema no infrecuente, y no es culpa del script. Deberás averiguar cómo migrar tu antigua base de datos a utf8.

Hay un fuerte soporte de migración. Simplemente no hay soporte de migración gratuito. He realizado alrededor de 100 migraciones y he escrito varios scripts de importación para sistemas no admitidos o personalizados. Probablemente cobraría entre $3000 y $5000 por importar tu base de datos. Eso no es una oferta, es solo para darte una idea de cuánto trabajo implica para alguien que lo ha hecho muchas veces. Sospecho que si pagaras un año de alojamiento Business, CDCK lo haría gratis, lo que podría ser menos de lo que yo cobraría por hacerlo. (Oh, pero es posible que no seas elegible para el alojamiento Business con una base de datos de ese tamaño).

1 me gusta

Continuando mi exploración aquí.

  • El script hace referencia a una función que no existe: import_user_account_id. Ustedes (los desarrolladores de Discourse) podrían querer arreglar eso.
  • La lógica que verifica los títulos de los temas se está volviendo loca con algunos temas que, por alguna razón, tienen una cadena vacía como título. Por mucho que eso no debería suceder, la verificación que evalúa eso debería atraparlo y devolver nil, pero aparentemente eso rompe la lógica de seguimiento escrita en la importación (ver aquí).

Tuve ese problema con alguna importación que hice recientemente. Sería mejor que devolviera algo como “el tema XXX no tiene título” o que extrajera la primera línea de texto de la publicación, pero eso es difícil de hacer en este contexto. Creo que lo que me tentaría hacer es arreglarlo manipulando tu base de datos y usar algo más para generar títulos donde falten.

Sí, básicamente estoy “limpiando” la base de datos cuando encuentro estos problemas, pero es difícil porque tengo que depurar el script y luego adivinar qué está causando el problema cada vez :sweat_smile:

Todavía no tengo ni idea de la función faltante import_user_account_id :expressionless:

Especialmente teniendo en cuenta que las vacaciones están muy cerca, es poco probable que alguien solucione eso a menos que esté usando el script. (Normalmente, cuando digo eso, Richard entra y salva el día).

2 Me gusta

JAJAJA, bueno, supongo que hoy te decepcionaré. Lo intenté, pero sospecho que la confirmación de este importador estaba incompleta y no incluía algunos cambios en base.rb. @justin trabajó en esto, tal vez él sepa. Sospecho que esto podría haber sido algo específico del cliente que se puede comentar sin más consecuencias.

Yo tampoco he usado nunca los importadores masivos.

Sí, los scripts de importación pueden ser complejos y depender de los detalles específicos de la base de datos, pero algunos scripts simplemente no están en un estado funcional. Eso también se aplica a este, y hay algunos scripts más con, por ejemplo, # frozen_string_literal: true que simplemente no funcionan de inmediato.

2 Me gusta

¡Ja!

Eso es (al menos)

parte de por qué me resulta tan difícil enviar PRs para los cambios que hago. Para cuando termino, hay tantas cosas específicas del caso que me temo que cualquier cosa que envíe se romperá de alguna manera.

Sí. Creo que algo pasó y añadió frozen_string_literal a todos los archivos. La mayoría de los archivos se arreglaron porque tenían pruebas, pero no hay pruebas para los scripts de importación.

2 Me gusta

Hola, solo para aclarar, no espero que nadie arregle esto ahora (estilo Karen). Solo estoy señalando algunas cosas que claramente tienen problemas en el propio código base y probablemente son solo “¡ups, olvidé agregar este cambio al commit! :sweat_smile:”.

Ya he aceptado que esta migración no sucederá antes de enero, como mínimo, en este momento.

Todos deberían disfrutar de las fiestas :slight_smile:
Mencionaré esto o abriré un nuevo hilo después de las fiestas, incluso si definitivamente tendré menos tiempo para dedicar a esta migración :frowning:

2 Me gusta

Sí, totalmente cierto; para las importaciones, normalmente no envío una PR antes de haber hecho dos importaciones de diferentes clientes.

1 me gusta

Manteniendo esto actualizado.

He realizado algunos cambios en base.rb después de discutirlo con otros ingenieros de nuestra comunidad. Muchos errores fueron causados por gsub! que fallaba porque aparentemente teníamos algunos temas con '' como título.

Agregamos una función que imita la función normalize_text que simplemente devuelve el imported_id del hilo si no hay contenido, convertido a cadena.

  def normalize_text_thread(text, imported_id)
    return imported_id.to_s unless text.present?
    @html_entities.decode(normalize_charset(text.presence || "").scrub)
  end

Luego, en vbulletin5.rb, cambiamos la línea en create_topic a:

create_topics(topics) do |row|
      created_at = Time.zone.at(row[5])

      title = normalize_text_thread(row[1], row[0])

Eso eliminó el problema. Básicamente, gsub! no maneja bien la entrada nil.

Sin embargo, esto hizo que el script continuara, pero cuando llegó a import_private_topics, se colgó allí. Hay 253.427 temas privados (pm) en nuestra base de datos, que son varios órdenes de magnitud menos que las respuestas. Después de 9 horas, detuve el script para ver qué estaba pasando realmente.

Al iniciar la interfaz, noté un par de cosas.

  1. Mi cuenta no fue importada porque el usuario administrador creado usaba la misma dirección de correo electrónico, supongo. Obvio, pero ¿algo que debería escribirse en algún lugar tal vez?
  2. Solo algunas de las categorías (subforos de vbulletin) fueron importadas.
  3. Solo se importaron los temas y su primera respuesta (no estoy seguro si realmente todos) y se importaron todos sin estar en las categorías correctas, incluso los que tenían una categoría que se habría creado. Todo se importa “sin categoría”.
  4. El “contador de respuestas” muestra -1, probablemente porque las respuestas en realidad no se importaron en absoluto.

Agregaré el resumen general, MUCHOS problemas con esta importación masiva desaparecerían si implementara un enfoque de paginación. Creo que las respuestas se han perdido porque el script intentó procesarlas todas a la vez y con 7 GB de datos fue imposible. Me desconcierta que un importador masivo no aborde la importación con un enfoque de paginación, para ser honesto. Incluso tomar 1000 registros a la vez, escribirlos y almacenar el último ID de registro escrito y repetirlo resolvería cualquier problema con bases de datos grandes.

1 me gusta

Para que lo sepas, sigo esto con interés y aprecio mucho las actualizaciones. :pray: No sé mucho sobre migraciones hasta ahora, pero encuentro esto muy informativo.

4 Me gusta