Atualizando o Discourse para Rails 6

Olá Equipe,

O Rails 6.0.0 foi lançado há 25 dias, então acho que é hora de atualizar o Discourse :slight_smile: Houve algumas etapas que precisei realizar para fazer isso funcionar.

  1. Corrigir testes quebrados
  • Adicionar método vazio trigger_transactional_callbacks? em lib/mini_sql_multisite_connection.rb
  • UrlHelper está carregando por padrão ActionView::Helpers::UrlHelper em vez de lib/UrlHelper. Resolvi isso adicionando :: na frente, no entanto, o que vocês acham sobre mudar o nome dessa classe?
  • No Rails 5.2.3, o MigrationContext aceitava um argumento; no 6.0.0, um esquema adicional é necessário
  1. Corrigir métodos depreciados
  1. Usar o autoloader clássico como primeira etapa antes do Zeitwerk
  2. Corrigir migrações no Rails 6.0.0 - o Rails mais recente não permite erro de migração antiga (você não pode definir uma coluna já definida como ‘integer’)

Realizei testes de fumaça para confirmar que o Discourse funciona conforme o esperado. Além disso, executei testes de desempenho para garantir que não haja regressão (usei as 500 iterações padrão).

Teste Rails 5.2.3 Rails 6.0.0 Porcentagem
categories-50 27 24 88,89%
categories-75 31 26 83,87%
categories-90 36 37 102,78%
categories-99 52 50 96,15%
home-50 27 26 96,30%
home-75 30 28 93,33%
home-90 39 38 97,44%
home-99 53 55 103,77%
topic-50 35 27 77,14%
topic-75 36 29 80,56%
topic-90 37 39 105,41%
topic-99 56 50 89,29%
categories_admin-50 47 47 100,00%
categories_admin-75 54 59 109,26%
categories_admin-90 64 66 103,13%
categories_admin-99 132 116 87,88%
home_admin-50 47 46 97,87%
home_admin-75 51 56 109,80%
home_admin-90 63 64 101,59%
home_admin-99 110 97 88,18%
topic_admin-50 50 49 98,00%
topic_admin-75 58 59 101,72%
topic_admin-90 65 67 103,08%
topic_admin-99 113 86 76,11%
load_rails 2593 2618 100,96%
rss_kb 318800 287332 90,13%
pss_kb 306913 275378 89,73%
Média 89,31%

Vou criar um pull request com todas as alterações mencionadas acima. Por favor, me avise se quiser que eu ajuste algo ou realize testes adicionais para garantir que tudo funcione conforme o esperado.

PR - DEV: Upgrading Discourse to Rails 6 by KrisKotlarek · Pull Request #8083 · discourse/discourse · GitHub

Abraços,
Kris

Isso é incrível :confetti_ball:

Você pode colocar isso em uma tabela Markdown com a variação percentual? Uma olhada rápida mostra que não houve muitas mudanças, o que é ótimo.

No que diz respeito aos plugins, temos uma tarefa rake que instala todos os plugins oficiais. Você pode executá-la e garantir que as especificações dos plugins passem no Rails 6? (O comando rake plugin:spec deve resolver)

Atualizei a postagem original para exibir a tabela. Obrigado por me indicar as especificações dos plugins. Vi que duas especificações falharam no Travis; vou analisar e corrigi-las.

Há dois números aqui que estou achando muito interessantes:

O RSS na versão 6.0 é quase 10% melhor.

O tópico (tempo mediano) — que é nossa rota mais comum — é 22% mais rápido.

Isso é uma melhoria de desempenho realmente séria. Você consegue medir consistentemente 22% de velocidade a mais em topic-50? Pode confirmar se a página real está sendo renderizada corretamente?

Executei o benchmark 3 vezes e, desta vez, os resultados são menos espetaculares. Meu fluxo é digitar ruby script/bench.rb na branch correta master ou rails6, pressionar Enter e não tocar no teclado para não afetar os resultados
| | topic-50 | RSS |
| — | — | — | — |
| 5.2.3 | 50 | 322852 |
| 5.2.3 | 50 | 309684 |
| 5.2.3 | 50 | 346376 |
| Média | 50 | 326304 |
| 6.0.0 | 49 | 328844 |
| 6.0.0 | 49 | 321824 |
| 6.0.0 | 49 | 283584 |
| Média | 49 | 311417 |

Também conectei meu servidor de desenvolvimento ao banco de dados de desempenho para garantir que a página do tópico esteja correta. A captura de tela abaixo parece boa para mim

Gostaria de ter sua opinião sobre uma correção.
Baixei todos os plugins, mas uma nova especificação está falhando em comparação com a master (./plugins/discourse-data-explorer/spec/controllers/queries_controller_spec.rb:32)

  1) DataExplorer::QueryController quando desativado nega cada requisição
     Failure/Error: render 'default/empty'

     ActionView::Template::Error:
       número incorreto de argumentos (2 fornecidos, 1 esperado)

Isso está corrigido na master rspec-rails https://github.com/rspec/rspec-rails/blob/4-0-dev/lib/rspec/rails/view_rendering.rb
alterando
def self.call(_template) para def self.call(_template, _source = nil)

Posso aplicar um monkey patch no rspec-rails com um novo arquivo em lib/freedom_patched/rspec-rails.rb, mas gostaria de garantir que essa seja a melhor abordagem.

Acho que essa é a última mudança que está impedindo a fusão do Rails 6.

Além disso, notei que essa especificação está quebrada, mas ela também está quebrada na master. Posso tentar corrigi-la (./plugins/discourse-calendar/spec/jobs/update_holiday_usernames_spec.rb:14)

 Failure/Error: expect(DiscourseCalendar.users_on_holiday).to eq([post.user.username])
       esperado: ["bruce1"]
            obtido: []

Por fim, há métodos depreciados nos plugins que posso corrigir facilmente amanhã.

Qual é a sua opinião sobre o rspec-rails?

Nossa, acho que vamos ter que fazer monkey patch até que o rspec-rails 4 seja lançado; não consigo pensar em nenhuma correção mais limpa por aqui.

Ou… talvez… usar o gem beta por enquanto, se tudo estiver funcionando?

Entendido, vou tentar instalar a versão beta hoje à noite e ver como fica. Pode ser uma atualização fácil e tranquila.

Fiz algumas correções adicionais.

Primeiramente, descobri por que um teste estava falhando para mim tanto na branch master quanto na rails6 - FIX: Freezed time used in update_holiday_usernames_spec.rb should be UTC by KrisKotlarek · Pull Request #3 · discourse/discourse-calendar · GitHub

Também criei pull requests para métodos obsoletos em vários plugins:

Rebasei o master mais recente na branch rails6.

Por fim, atualizei o rspec-rails para a versão 4.0.0.beta2 e ele funciona bem na minha máquina local. O Travis teve alguns problemas, no entanto, vejo os mesmos problemas em outros pull requests, então não acredito que isso esteja correlacionado com a atualização do rspec-rails.

Isso já foi mesclado :confetti_ball: :confetti_ball: :confetti_ball:

Vou acompanhar de perto hoje. Muito obrigado por todo esse trabalho.

E um grande agradecimento à equipe do Rails por tornar essa atualização tão agradável!!

Vou retornar a este tópico com alguns gráficos bem legais.

A atualização está parecendo bastante tranquila, o que é ótimo. O desempenho é estável e permanece incrivelmente semelhante.

Memória e CPU parecem notavelmente similares.

Minha única preocupação (e algo que gostaria de esclarecer) é que parece haver threads “descontroladas” por alguns segundos regularmente nos web workers.

De alguma forma, algumas solicitações estão causando a criação de um grande número de threads, que depois desaparecem.

Continuarei investigando isso; precisamos obter backtraces quando o número estiver alto, para que possamos identificar o culpado.

Considerando que tudo o mais está muito bom, não vou reverter a atualização.

Isso deve ser corrigido conforme:

Isso é resultado de um novo código no Rails 6 que protege o acesso a variáveis vinculadas à thread, determinando se é possível usar ou não prepared statements.

No Discourse, não usamos prepared statements de forma alguma, então esse patch não é algo de que precisamos.

Veja mais em:

E … confirmado … minha correção elimina a grande quantidade de picos de thread

Vale mencionar também … foi assim que depurei:

  1. Escrevi esta pequena classe
# frozen_string_literal: true

class Thread
  attr_accessor :origin
end

class ThreadDetective
  def self.test_thread
    Thread.new { sleep 1 }
  end
  def self.start(max_threads)
    @thread ||= Thread.new do
      self.new.monitor(max_threads)
    end

    @trace = TracePoint.new(:thread_begin) do |tp|
      Thread.current.origin = Thread.current.inspect
    end
    @trace.enable
  end

  def self.stop
    @thread&.kill
    @thread = nil
    @trace&.disable
    @trace.stop
  end

  def monitor(max_threads)
    STDERR.puts "Monitorando threads em #{Process.pid}"

    while true
      threads = Thread.list

      if threads.length > max_threads
        str = +("-" * 60)
        str << "#{threads.length} encontrados no processo #{Process.pid}!\n"

        threads.each do |thread|
          str << "\n"
          if thread.origin
            str << thread.origin
          else
            str << thread.inspect
          end
          str << "\n"
        end
        str << ("-" * 60)

        STDERR.puts str
      end
      sleep 1
    end
  end

end
  1. Em seguida, integrei no unicorn um require desta classe no after_fork e executei ThreadDetective.start(14)

  2. A classe observou diligentemente cada vez que uma thread era criada usando um TracePoint e adicionou um pequeno frame na thread chamado origin para me ajudar a rastrear sua origem. Assim que foi observado um grande número de threads, ela imprimiu as informações no STDERR. Isso pode ser rastreado em /var/www/discourse/logs/unicorn.stderr.log

Assim que soube que 100 threads estavam vindo todas de um único ponto, ficou muito fácil isolar a causa raiz.

Percebi que não consigo mais usar dev.local como meu hostname no modo de desenvolvimento no Rails 6, então adicionei uma variável de ambiente para configurar essa lista de permissões:

Não deveríamos precisar manter esse monkey patch a longo prazo, pois acabamos de implementar uma correção no Rails.

Olá,

obrigado pelo seu esforço em trazer o Rails 6 para o Discourse! Posso humildemente perguntar quando isso deve ser integrado ao Discourse? Ou já está na versão 2.4.0.beta? Estou apenas perguntando sobre a possibilidade de isso quebrar algum plugin que as pessoas tenham instalado em suas instâncias.

Com os melhores cumprimentos,
Andreas.

Isso está ativo desde setembro para todos os usuários do canal de lançamento padrão. Foi apresentado pela primeira vez na versão 2.4.0.beta5.

Tudo bem, muito obrigado. Desejo a você o melhor para 2020 e agradeço por tudo o que você está fazendo aqui.