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

Sim, vou fazer isso se conseguir concluir pelo menos a importação do usuário. Atualmente, está falhando ao tentar trabalhar nos e-mails

Basta remover a primeira linha do script script/bulk_import/vbulletin5.rb
# frozen_string_literal: true

2 curtidas

Ok, então, executando apenas as três primeiras funções:

 def execute
    # enable as per requirement:
    #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:

Estou assumindo que essa mensagem sobre garantir a consistência é para quando a importação completa for feita? Ou devo executá-la a cada “etapa” que eu executar e então fazer uma cópia do diretório discourse do host para ter um backup?

1 curtida

Ao iniciá-lo novamente com as próximas 4 funções ativas, retorna um erro para IDs já existentes

Isso pode ser um “tudo ou nada”? Talvez espere que tudo seja feito em uma grande transação?

Tentei novamente. O processo decorreu durante algum tempo e, de repente, isto.

A parte frustrante é que parece ter sido do nada.
Agora, ao executá-lo novamente, surge este erro.

Estou demasiado cansado agora para verificar a que se refere. Especialmente porque duplicate key value não deveria realmente acontecer se eu simplesmente relancei o script de importação em massa, pois não??

Peço desculpas a quem se sentir ofendido por esta postagem porque, para ser honesto, estou lidando com esses problemas desde segunda-feira e, neste ponto, estou cansado de fazer depuração/correção para o código do Discourse.

Após a enésima tentativa (parei de contar depois da sétima), acho que vou desistir porque parece que a migração não é algo que o Discourse tenha investido muito tempo para suportar.

Acredito que o maior problema é que o conjunto de caracteres usado neste enorme banco de dados é utf8mb4, que não é suportado pelo script(?).

Usar utf8 (padrão) simplesmente gera muitos erros que estão sendo relatados, mas não está claro o que está acontecendo, pois o script continua mesmo assim. A entrada no banco de dados está sendo ignorada? Copiada com alguns caracteres não suportados (os quadrados clássicos)?

Além disso, as três execuções mais recentes (usando os importadores em massa), com o mesmo conjunto exato de instruções seguidas, têm resultados diferentes. Esta última execução atingiu a importação de tópicos, começou imediatamente a relatar erros, mas continuou (???):

Carregando aplicação...
Iniciando...
Pré-carregando I18n...
Corrigindo os maiores números de postagens...
Carregando IDs de grupos importados...
Carregando IDs de usuários importados...
Carregando IDs de categorias importados...
Carregando IDs de tópicos importados...
Carregando IDs de postagens importados...
Carregando índices de grupos...
Carregando índices de usuários...
Carregando índices de categorias...
Carregando índices de tópicos...
Carregando índices de postagens...
Carregando índices de ações de postagem...
Importando categorias...
Importando categorias pai...
      5 -   1104/segERROR:  duplicate key value violates unique constraint "unique_index_categories_on_name"
DETAIL:  Key (COALESCE(parent_category_id, '-1'::integer), name)=(-1, Armata Brancaleone) already exists.
CONTEXT:  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 tópicos...
    600 -   4073/seg
ERROR: undefined method `[]' for 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>'

Até finalmente travar neste:

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

Mas não antes de basicamente spammar constantemente esses dois erros:

ERROR: undefined method `gsub!' for 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>'

e

ERROR: invalid byte sequence in 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>'

Por favor, note que fui passo a passo comentando qual função executar, depois executando o rake import:ensure_consistency antes de continuar comentando as que acabaram de ser executadas e assim por diante, porque se eu apenas deixar o script inteiro reexecutar os passos anteriores, ele simplesmente falha ao encontrar IDs duplicados.

Antes que o argumento usual de “você não pode reclamar de software livre” surja, quero esclarecer que estou contribuindo para outros projetos de código aberto e também estou fazendo software de graça, mas é de suma importância para mim que, se eu lançar algo, esse algo funcione e seja bem documentado (mesmo que apenas para que eu possa evitar as milhares de mensagens perguntando legitimamente ‘como isso funciona’) ou estou pronto para corrigir qualquer bug que apareça.

Embora o Discourse pareça ter uma ótima experiência “out-of-the-box”, deve ficar bem claro que é 2022 e as comunidades existiam muito antes deste produto. A “adoção” precisaria ter um forte suporte de migração e não parece ser o estado atual para o Discourse.

Reconheço que um banco de dados de 20 GB é um caso extremo, mas não estamos tendo problemas com o tamanho aqui, mas sim com o conjunto de caracteres ou quem sabe o quê, pois nem mesmo há um erro constante e a maioria das coisas: não há documentação além de caçar threads e posts deixados por quem passou pela mesma provação no passado, esperando que uma solução alternativa tenha sido encontrada e que o código-fonte não tenha mudado muito desde então.

Neste ponto, eu recomendaria fortemente a qualquer pessoa que venha do vBulletin que suspenda qualquer migração até que o que parece ser uma reformulação do script de migração (em andamento, parece?) seja concluída.

Embora eu sinta sua dor (migrações são um assunto complicado), como Especialista em Migração do Discourse, deixe-me esclarecer as coisas.

Temos um framework de migração maduro com mais de 60 scripts para diferentes plataformas, um framework separado de importação em massa com 5 scripts e um framework mais novo em desenvolvimento que melhora massivamente todos os aspectos - desempenho, organização de código, testabilidade, verificabilidade, documentação e assim por diante.

Temos uma equipe de Migrações separada com amplo suporte de desenvolvedores principais e contribuímos com melhorias genéricas de volta ao código a cada migração que concluímos. Estamos constantemente realizando migrações para clientes, que variam de triviais a incrivelmente complexas.

Nosso objetivo final é tornar as migrações o mais simplificadas possível tanto para clientes hospedados quanto para a comunidade, mas a quantidade de código que está em escopo durante uma migração é simplesmente massiva, e a configuração de software em nível de sistema, mudanças de software de terceiros e a variabilidade dos dados de entrada apenas complicam o problema.

Novamente, gostaria que todas essas coisas fossem mais fáceis, mas torná-las assim leva incontáveis horas de trabalho para criar e manter, e há apenas um número limitado de pessoas para isso.

Não desista! :slight_smile:

5 curtidas

Agradeço e entendo que o escopo é imenso. É frustrante continuar tropeçando em exceção sobre exceção e o fato de que o projeto está escrito em ruby não ajuda a encontrar ajuda além de vir aqui basicamente, o que, não pode acomodar todas as solicitações de ajuda, pois, como você diz, alguns têm casos muito específicos que são simplesmente impossíveis de ajudar, a menos que se tenha acesso aos dados reais.

Eu também, em grande parte, culpo o clusterfuck absoluto que é a estrutura do vbulletin.

Acabei de verificar esta manhã e este é um resumo dos tamanhos das tabelas.

Para dar contexto, a tabela “text” é onde está o conteúdo real.
a tabela node está mantendo a hierarquia e closure… deixe-me citar aqui porque nem consigo:

A Tabela de Fechamento constrói os relacionamentos pai-filho entre todos os nós. A maioria do seu banco de dados é composta por arquivos anexados que não deveriam ser armazenados no banco de dados de qualquer maneira.

Portanto, no geral, para um fórum com ~8 GB de conteúdo, há um overhead de 28 GB. Ótimo, parabéns vbulletin.

2 curtidas

É isso que quero dizer quando digo que é frustrante.

Novamente, o mesmo conjunto de ações (seguindo um runbook escrito por mim com todas as tentativas e erros), executando em uma nova instalação do Discourse.

Resultado:

Tired Tv Land GIF by TV Land Classic

Onde está import_user_account_id? :expressionless:
Mas o mais importante? Como você conseguiu não causar um erro na execução anterior, onde falhou na importação do tópico? :confounded:

Comentando essa invocação de função (que parece ter sido importante de qualquer maneira) e iniciando novamente:

Esses erros de chaves duplicadas… o script não deveria saber que já fez esses IDs e seguir em frente?

Cada importação é diferente. Você pensaria que um script que funciona para uma instância de seu-fórum-favorito-anteriormente funcionaria, mas não funciona. E para um fórum enorme, é realmente difícil. Simplesmente não é algo que seja fácil de suportar. E os importadores em massa acessam o banco de dados diretamente em vez de contar com o rails para conseguir verificar automaticamente as coisas à medida que avançam.

Esse é um problema não infrequente, e não culpa do script. Você precisará descobrir como mover seu banco de dados antigo para utf8.

Existe um forte suporte de migração. Simplesmente não existe suporte de migração gratuito. Fiz cerca de 100 migrações e escrevi vários scripts de importação para sistemas não suportados ou personalizados. Provavelmente cobraria de US$ 3000 a US$ 5000 para importar seu banco de dados. Isso não é uma oferta, é apenas para dar uma ideia de quanto trabalho é para alguém que já fez isso várias vezes. Suspeito que, se você pagasse por um ano de hospedagem Business, a CDCK faria isso gratuitamente, o que pode ser menos do que eu cobraria para fazer. (Ah, mas você pode não ser elegível para hospedagem Business com um banco de dados desse tamanho).

1 curtida

Continuando minha exploração aqui.

  • O script faz referência a uma função que não existe: import_user_account_id. Vocês (desenvolvedores do Discourse) talvez queiram corrigir isso.
  • A lógica que verifica os títulos dos tópicos está, de alguma forma, enlouquecendo com alguns tópicos que, por alguma razão, têm uma string vazia como título. Por mais que isso não devesse acontecer, a verificação que avalia isso deveria capturá-la e retornar nil, mas aparentemente isso quebra a lógica subsequente escrita na importação (veja aqui).

Tive esse problema com uma importação que fiz recentemente. Seria melhor retornar algo como “o tópico XXX está sem título” ou pegar a primeira linha de texto da postagem, mas isso é difícil de fazer neste contexto. Acho que o que eu faria seria consertar manipulando seu banco de dados e usar outra coisa para gerar títulos onde eles estiverem faltando.

Sim, estou basicamente “sanitizando” o banco de dados quando encontro esses problemas, mas é difícil, pois preciso depurar o script e depois adivinhar o que está causando o problema a cada vez :sweat_smile:

Ainda sem pistas sobre a função import_user_account_id ausente :expressionless:

Especialmente com as festas de fim de ano chegando muito em breve, é improvável que alguém corrija isso a menos que esteja usando o script. (Normalmente, quando digo isso, o Richard aparece e salva o dia.)

2 curtidas

LOL, bem, acho que vou decepcioná-lo hoje. Tentei, mas suspeito que o commit deste importador estava incompleto e não incluiu algumas alterações em base.rb. O @justin trabalhou nisso, talvez ele saiba. Suspeito que isso poderia ter sido algo específico do cliente que pode ser comentado sem mais consequências.

Eu também nunca usei os importadores em massa.

Sim, scripts de importação podem ser complexos e depender de especificidades do banco de dados, mas alguns scripts simplesmente não estão em um estado funcional. Isso vale para este também, e existem outros scripts com, por exemplo, # frozen_string_literal: true que simplesmente não funcionam de imediato.

2 curtidas

Ha!

Isso é (pelo menos)

parte do motivo pelo qual acho tão difícil enviar PRs para as alterações que faço. Quando termino, há tanta coisa específica do caso que tenho medo de que o que quer que eu envie seja quebrado de alguma forma.

Sim. Acho que algo passou e adicionou frozen_string_literal a todos os arquivos. A maioria dos arquivos foi corrigida porque tinham testes, mas não há testes para os scripts de importação.

2 curtidas

Ei, só para esclarecer, não estou esperando que ninguém conserte isso agora (estilo Karen). Estou apenas apontando algumas coisas que claramente têm alguns problemas no próprio codebase e provavelmente são apenas “ops, esqueci de adicionar essa mudança ao commit! :sweat_smile:

Já aceitei que essa migração não acontecerá antes de janeiro, no mínimo, neste momento.

Todos deveriam apenas aproveitar as festas :slight_smile:
Vou levantar isso ou abrir um novo tópico depois das festas, mesmo que definitivamente terei menos tempo para dedicar a essa migração :frowning:

2 curtidas

Sim, totalmente verdade - para importações, geralmente não envio um PR antes de fazer duas importações de clientes diferentes.

1 curtida

Apenas mantendo isso atualizado.

Fiz algumas alterações em base.rb após discuti-las com outros engenheiros de nossa comunidade. Muitos erros foram causados por gsub! falhando porque, aparentemente, tínhamos alguns tópicos com '' como título.

Adicionamos uma função que imita a função normalize_text, que simplesmente retorna o imported_id da thread se não houver conteúdo, convertido para string.

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

Em seguida, em vbulletin5.rb, alteramos a linha em create_topic para:

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

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

Isso eliminou o problema. Basicamente, gsub! não lida bem com a entrada nil.

No entanto, isso fez o script continuar, mas quando chegou a import_private_topics, ele travou. Existem 253.427 tópicos privados (pm) em nosso banco de dados, que são várias ordens de magnitude menos do que as respostas. Após 9 horas, parei o script para ver o que estava acontecendo.

Ao iniciar a interface, notei algumas coisas.

  1. Minha conta não foi importada porque o usuário administrador criado estava usando o mesmo e-mail, suponho. Óbvio, mas algo que deveria ser escrito em algum lugar, talvez?
  2. Apenas algumas das categorias (subfóruns do vbulletin) foram importadas.
  3. Apenas tópicos e suas primeiras respostas foram importados (não tenho certeza se todos realmente) e todos foram importados sem estarem nas categorias corretas, mesmo aqueles que tinham uma categoria que teria sido criada. Tudo é importado “sem categoria”.
  4. O “contador de respostas” mostra -1, provavelmente porque as respostas não foram importadas.

Adicionarei a visão geral, MUITOS problemas com esta importação em massa desapareceriam se ela implementasse uma abordagem de paginação. Acho que as respostas sumiram porque o script tentou processá-las todas de uma vez e com 7 GB de dados isso foi impossível. Para ser honesto, fico perplexo que um importador em massa não aborde a importação com uma abordagem de paginação. Mesmo simplesmente pegando 1000 registros por vez, escrevendo-os e armazenando o último ID de registro escrito e repetindo o loop resolveria qualquer problema com grandes bancos de dados.

1 curtida

Para constar, estou acompanhando isso com interesse e apreciando muito as atualizações. :pray: Não sei muito sobre migrações até agora, mas estou achando isso muito informativo.

4 curtidas