Обновление Discourse до Rails 6

Привет, команда,

Rails 6.0.0 был выпущен 25 дней назад, поэтому, думаю, пришло время обновить Discourse :slight_smile: Мне пришлось выполнить несколько шагов, чтобы это заработало.

  1. Исправление сломанных спецификаций
  • Добавлен пустой метод trigger_transactional_callbacks? в lib/mini_sql_multisite_connection.rb
  • UrlHelper по умолчанию загружает ActionView::Helpers::UrlHelper вместо lib/UrlHelper. Я решил это, добавив :: в начале, однако, что вы думаете насчёт переименования этого класса?
  • В Rails 5.2.3 MigrationContext принимал один аргумент, в 6.0.0 требуется дополнительный параметр схемы
  1. Исправление устаревших методов
  • update_attributes! заменён на update!
  • content_type теперь содержит кодировку, вместо этого следует использовать медиа-тип — rails/guides/source/upgrading_ruby_on_rails.md at main · rails/rails · GitHub
  • Исправлено предупреждение о уже инициализированных константах (TRADITIONAL_ESCAPED_CHAR, RFC_5987_ESCAPED_CHAR). Перед удалением я подтвердил, что значения в ActionPack совпадают
  1. Использование классического автозагрузчика как первого шага перед Zeitwerk
  2. Исправление миграций в Rails 6.0.0 — новейшая версия Rails не допускает ошибок из старых миграций (нельзя определить уже определённый столбец ‘integer’)

Я провёл дымовые тесты, подтверждающие, что Discourse работает как ожидалось. Кроме того, я запустил тесты производительности, чтобы убедиться в отсутствии регрессий (использовал стандартные 500 итераций).

Тест Rails 5.2.3 Rails 6.0.0 Процент
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%
Среднее 89.31%

Я создам pull request со всеми упомянутыми выше изменениями. Пожалуйста, дайте знать, если вы хотите, чтобы я что-то скорректировал или провёл дополнительные тесты для подтверждения корректной работы всего.

PR — https://github.com/discourse/discourse/pull/8083

С уважением,
Крис

Это потрясающе :confetti_ball:

Можете ли вы оформить это в виде таблицы Markdown с процентным изменением? Беглый взгляд показывает, что изменений немного, что отлично.

Что касается плагинов, у нас есть задача rake, которая устанавливает все официальные плагины. Можете ли вы запустить её и убедиться, что спецификации плагинов проходят на Rails 6? (Команда rake plugin:spec должна справиться с этим)

Я обновил исходный пост, чтобы отобразить таблицу. Спасибо, что указали мне на спецификации плагинов. Вижу, что две спецификации не прошли проверку на Travis, я разберусь и исправлю их.

Вот два числа, которые меня очень заинтересовали:

RSS на версии 6.0 почти на 10% лучше.

Тема (медианное время) — наш самый частый маршрут — работает на 22% быстрее.

Это действительно значительное улучшение производительности. Вы можете последовательно зафиксировать ускорение на 22% на topic-50? Можете подтвердить, что страница отображается корректно?

Я запускал бенчмарк 3 раза, и на этот раз результаты менее впечатляющие. Мой процесс: в правильной ветке master или rails6 ввожу ruby script/bench.rb, нажимаю Enter и не трогаю клавиатуру, чтобы не повлиять на результаты.
| | topic-50 | RSS |
| — | — | — | — |
| 5.2.3 | 50 | 322852 |
| 5.2.3 | 50 | 309684 |
| 5.2.3 | 50 | 346376 |
| Среднее | 50 | 326304 |
| 6.0.0 | 49 | 328844 |
| 6.0.0 | 49 | 321824 |
| 6.0.0 | 49 | 283584 |
| Среднее | 49 | 311417 |

Я также подключил свой сервер разработки к базе данных производительности, чтобы убедиться, что страница темы отображается корректно. Скриншот ниже выглядит для меня нормально.

Хотел бы узнать ваше мнение по поводу одного исправления.
Я скачал все плагины, однако один новый тест не проходит по сравнению с master (./plugins/discourse-data-explorer/spec/controllers/queries_controller_spec.rb:32):

  1) DataExplorer::QueryController when disabled denies every request
     Failure/Error: render 'default/empty'

     ActionView::Template::Error:
       wrong number of arguments (given 2, expected 1)

В ветке master это исправлено в rspec-rails https://github.com/rspec/rspec-rails/blob/4-0-dev/lib/rspec/rails/view_rendering.rb
путем изменения
def self.call(_template) на def self.call(_template, _source = nil).

Я могу применить патч к rspec-rails, создав новый файл lib/freedom_patched/rspec-rails.rb, но хотел убедиться, что это наилучший подход.

Думаю, это последнее изменение, которое блокирует слияние Rails 6.

Кроме того, я заметил, что этот тест сломан, но он также сломан и в master. Я могу попробовать его исправить (./plugins/discourse-calendar/spec/jobs/update_holiday_usernames_spec.rb:14):

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

Наконец, в плагинах есть устаревшие методы, которые я могу легко исправить завтра.

Какое у вас мнение насчет rspec-rails?

О боже, полагаю, придется применить monkey patch, пока не выйдет rspec-rails 4; не могу придумать более чистого решения.

Или… может… пока использовать бета-версию gem, если всё работает?

Понял, попробую установить бета-версию сегодня вечером и посмотрю, как всё пройдёт. Обновление может пройти легко и без проблем.

Я внес несколько дополнительных исправлений.

Прежде всего, я выяснил, почему один тест не проходил у меня в ветках master и rails6FIX: Freezed time used in update_holiday_usernames_spec.rb should be UTC by KrisKotlarek · Pull Request #3 · discourse/discourse-calendar · GitHub

Также я создал запросы на слияние (pull requests) для устаревших методов в различных плагинах:

Я перебил ветку rails6 на актуальный master.

Наконец, я обновил rspec-rails до версии 4.0.0.beta2, и у меня на локальной машине всё работает отлично. У Travis возникли некоторые проблемы, однако я вижу те же самые проблемы и в других запросах на слияние, поэтому не думаю, что они связаны с обновлением rspec-rails.

Это уже влито :confetti_ball: :confetti_ball: :confetti_ball:

Сегодня буду внимательно следить за этим, огромное спасибо за эту работу.

И отдельное спасибо команде Rails за то, что сделали это обновление таким приятным!!

Вернусь к этой теме с несколькими красивыми графиками.

Обновление проходит довольно спокойно, что отлично. Производительность стабильна и остаётся практически неизменной.

Использование памяти и процессора выглядит remarkably похожим.

Единственное, что меня беспокоит (и в чём я хочу разобраться) — это то, что на веб-воркерах периодически на несколько секунд появляются «разбегающиеся» потоки.

То есть какие-то запросы вызывают создание огромного количества потоков, которые затем исчезают.

Буду продолжать расследование: нам нужно получить трассировки стека, когда количество потоков велико, чтобы найти виновника.

Учитывая, что всё остальное выглядит очень хорошо, я не буду откатывать обновление.

Это должно быть исправлено согласно:

Это результат нового кода в Rails 6, который защищает доступ к переменной, привязанной к потоку, определяющей, можно ли использовать подготовленные выражения или нет.

В Discourse мы вообще не используем подготовленные выражения, поэтому этот патч нам не нужен.

Подробнее см. по адресу:

И … подтверждено … мое исправление устраняет значительное количество всплесков потоков

Также стоит отметить … вот как я это отладил:

  1. Я написал этот небольшой класс
# 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 "Monitoring threads in #{Process.pid}"

    while true
      threads = Thread.list

      if threads.length > max_threads
        str = +("-" * 60)
        str << "#{threads.length} found in Process #{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. Затем я подключил unicorn after_fork, добавил require этого класса и запустил ThreadDetective.start(14)

  2. Класс внимательно отслеживал каждый раз, когда создавался поток, используя TracePoint, и добавлял небольшой фрейм в поток под названием origin, чтобы помочь мне отследить, откуда он появился. Как только наблюдалось большое количество потоков, он выводил информацию в STDERR. Это можно отслеживать в /var/www/discourse/logs/unicorn.stderr.log

Как только я узнал, что все 100 потоков исходят из одного места, стало очень легко изолировать корневую причину.

Я заметил, что в режиме разработки Rails 6 больше нельзя использовать dev.local в качестве имени хоста, поэтому я добавил переменную окружения для настройки этого белого списка:

Нам не следует долго держать этот патч-обход, так как мы только что внедрили исправление в Rails.

Привет!

Спасибо за ваши усилия по интеграции Rails 6 в Discourse! Могу ли я вежливо спросить, когда это ожидается в Discourse? Или это уже включено в версию 2.4.0.beta? Я спрашиваю лишь о том, не приведет ли это к поломке каких-либо плагинов, установленных пользователями на их инстансах.

С наилучшими пожеланиями,
Андреас.

Это доступно в режиме реального времени с сентября для всех, кто использует канал стандартных релизов. Впервые функция была представлена в версии 2.4.0.beta5.

Хорошо, большое спасибо. Желаю всего наилучшего в 2020 году и спасибо за всё, что вы здесь делаете.