Migrando banco de dados vBulletin 5 - Erros no script de importação

Ok, um rápido resumo.

Estou a fazer voluntariado na migração de um fórum que está atualmente no vbulletin3.
Num ambiente de staging, a partir de um dump da base de dados (20GB, leu bem).

Executei o upgrade para vBulletin 5. Levou 5-6 horas, mas correu bem. A versão é vBulletin 5.4.
Fiz alguma limpeza nos nomes de utilizador para serem aceites pelo discourse.

Agora, instalei o docker discourse e segui vagamente este guia para a preparação. Vagamente, significando que a maior parte era redundante ou desatualizada, mas ajudou a ter uma ideia do que fazer.

Estou na fase em que estou literalmente a ficar cego, pois tenho quase zero experiência em programação Ruby.
Portanto, as partes relevantes, após terminar a instalação, entrei no container com ./launcher enter app e depois:

  • Adicionei freetds-dev e libmariadb-dev
  • Editei o Gemfile para adicionar a gem php_serialize.
  • A partir da shell, executei export IMPORT=1 para definir o ambiente de importação.
  • Como utilizador discourse, executei bundle install --no-deployment --without test --without development --path vendor/bundle

Recebi o erro:

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

Portanto, continuei com:

  • bundle config unset deployment e executei novamente o comando anterior.
  • Verifiquei que tanto mysql2 como php_serialize estavam lá (estavam).
  • Adicionei os avatares antigos do fórum (sem anexos para importar) e atribuí a propriedade dos diretórios ao utilizador discourse no seu próprio /home/discourse.
  • Editei script/import_scripts/vbulletin5.rb para alterar a referência para a ligação à base de dados.
  • Como utilizador discourse, executei bundle exec ruby script/import_scripts/vbulletin5.rb.

Isto retornou-me um erro sobre tzinfo Integer values not supported que encontrei mencionado aqui neste 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)

A sugestão do @Haddoq foi mudar uma linha de Time.zone.at(@tz.utc_to_local(timestamp)) para Time.zone.at(timestamp).

Também sugere adicionar lastvisit na query do utilizador, pois caso contrário causará outro erro, o que também fiz.

No entanto, agora, quando executo a migração com bundle exec ruby script/import_scripts/vbulletin5.rb, isto é o que recebo:

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

Neste ponto, estou um pouco perdido. Alguém pode ajudar?

paging @Canapin já que ele esteve em “Migration Vietnam” e pode saber mais :heart:

Trabalhar por mais de 8 horas seguidas faz mal.

Ao adicionar u.lastvisit ao final de SELECT u.userid, u.username, u.homepage, u.usertitle, u.usergroupid, u.joindate, u.email, eu esqueci de adicionar uma vírgula depois disso.

Desculpe Canapin por te chamar :frowning:

1 curtida

Certo, problema de acompanhamento. O guia que eu estava seguindo afirmava que o processo de registro poderia ser reiniciado se ele ficasse lento.

Mas quando reiniciado, recebo erros sobre usuários já existindo no banco de dados postgres.

Tolo de mim, fui e deletei todos os usuários com id > 1 no banco de dados (deixando basicamente o discobot, sistema e administrador) e reiniciei. Isso faz com que a importação continue, mas os e-mails de todos os usuários criados anteriormente são marcados como “já em uso” em algum lugar.

O que posso fazer para limpar isso e esse processo não deveria, em vez disso, pular a inserção se já houver um nome de usuário correspondente?

Editar: Certo, descobri que preciso limpar users, email_tokens e user_emails.

1 curtida

Infelizmente, eu não acompanhei o que tenho ajustado em minhas importações anteriores, então tenho pouco conhecimento. Agora tenho uma instância pessoal do Discourse onde escrevo esse tipo de coisa… Eu deveria ter feito isso antes!
Sou mais habilidoso em importações quando estou realmente trabalhando em uma.

Quanto a destruir usuários, você pode ter querido usar UserDestroyer através do console Rails:

2 curtidas

Ótimo, vou me lembrar disso para a migração “real”. Por enquanto, estou apenas fazendo um teste de todo o processo enquanto escrevo um runbook :slight_smile:

1 curtida

Por que você fez isso? A menos que você tenha alterado algo no script que importaria esses de forma diferente, você deveria apenas reiniciar e fazer com que ele importe os que ainda não foram importados.

Se você precisar recomeçar, é muito mais fácil restaurar um backup ou excluir e criar um novo banco de dados.

Ele deve encontrar os IDs de importação na tabela UserCustomFields. Não tenho certeza de como você obteria esse erro.

As alterações são as que listei. Fico tão surpreso quanto você, mas o script não progredia e simplesmente dava erro e parava.

Existe alguma maneira de acelerar a importação?

Tenho mais de 90 mil usuários nesta comunidade e a velocidade de importação degrada com o tempo por algum motivo, e não consigo imaginar porquê.

Rodou a noite toda e, apenas para os usuários, estamos em 25123 / 95635 ( 26.3%) [42 itens/min]

Existem várias ordens de magnitude a mais de posts. Quanto tempo devo esperar que uma migração aconteça? Dias? semanas?

Quanta RAM? Provavelmente é esse o problema. Você pode parar e reiniciar.

Já tive casos que levaram semanas. É por isso que existem os importadores em massa.

Ele tem apenas 2 GB de RAM. É uma máquina de teste. Eu poderia executá-lo localmente em vez de uma VM (16 GB de RAM seriam muito melhores?) e, em seguida, empacotar tudo e fazer o upload eventualmente, eu acho.

Você pode dar mais detalhes sobre importadores em massa? É a primeira vez que ouço falar sobre isso e definitivamente deveria ter aparecido quando eu estava pesquisando por “migrar vbulletin para discourse” no Google :angry:

Frustrated Jason Segel GIF by NETFLIX

Posso executá-lo mesmo que o outro importador já tenha processado alguns usuários ou devo limpar?

Fui em frente e tentei, no pior dos casos não vai funcionar. Estou recebendo spam com

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>'

Presumo que eu deva limpar os usuários e grupos que são as únicas coisas que o outro importador criou/começou a criar.

Antes de eu mexer no banco de dados novamente, existe algum comando ruby que eu possa executar que cuide disso de forma limpa?

Por desta vez, vou apenas prosseguir e destruir e recriar a instalação do discourse. Graças a Deus pelo Docker.

Não, instalação limpa, o script ainda falha com esta mensagem genérica

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>'

Existe alguma maneira de depurar isso para que pelo menos eu possa entender qual valor está sendo nil e deveria ser outra coisa?

Ok, o erro parece estar relacionado à codificação.
No bulk_import/vbulletin5.rb posso especificar a codificação (no nosso caso, UTF8mb4, mas no arquivo base.rb não parece mapear para nada no mapa de caracteres

Olá!

Algumas dicas gerais:

  1. Se você fizer alterações no script, geralmente é aconselhável começar do zero ou, pelo menos, de um ponto conhecido e bom. Você pode fazer isso mais facilmente restaurando um backup feito pouco antes de executar a migração, como @pfaffman disse.
  2. Importadores em massa geralmente levam muito menos tempo, mas este em particular tem o potencial de consumir MUITA RAM porque ele armazena em cache coisas na memória. Para uma migração em massa específica do vBulletin que fiz de um arquivo SQL de 2 GB descompactado, o processo precisou de 22 GB de RAM (verifiquei duas vezes, não é erro de digitação).
  3. Se você fizer alterações no script, sugiro que crie uma versão de teste da entrada com, digamos, 100 ou 1000 registros para cada tabela (mas cuidado com a integridade referencial - ou seja, não trunque tabelas referenciadas por outras tabelas). Testar alterações com um processo de mais de 8 horas corroerá sua sanidade muito rapidamente.

Uma dica mais específica sobre rastros de pilha: procure por linhas que mencionem o arquivo específico que você executou. Neste caso, sim, é um problema de codificação, mas o mais relevante é o fato de que se trata de nomes de usuário:

Você disse que teve que higienizar nomes de usuário, então eu verificaria novamente se você os codificou como esperado pelo script.

2 curtidas

Você pode apenas descartar, criar e migrar o banco de dados em vez de recriar o Discourse. É um pouco complicado, no entanto, pois você tem que

  sv stop unicorn

e então

  rake db:drop db:create db:migrate

Ele reclamará e dirá para definir uma variável de ambiente que você precisa colocar na linha antes da tarefa do rake.

Você também pode restaurar um backup, o que pode ser mais conveniente.

Para constar, acho que nunca usei um script de migração em massa.

1 curtida

Isso é ainda mais estranho, pois os nomes de usuário foram todos higienizados para seguir as diretrizes do Discourse, basicamente todos foram alterados para serem letras, números ou _, nada mais.

Em qualquer caso, mudar o charset de utf8mb4 para utf8, como era o padrão, fez com que funcionasse, mas agora estou recebendo erros de e-mails invá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>'

Vou descobrir do que se trata agora, pois a importação “não em massa” estava detectando alguns e-mails malformados, mas os substituindo automaticamente.

Depois de fazer isso, basta criar um backup da sua instalação vazia e intocada do Discourse.
Você sempre pode restaurá-la muito rapidamente e começar de novo.

2 curtidas

Para complementar, se você tiver problemas com, por exemplo, a importação de posts, você também pode sair do script após a importação do usuário ter sido concluída com sucesso e criar outro backup. Em seguida, você pode reiniciar o script e continuar do momento em que já importou os usuários.

1 curtida