大家好,
Rails 6.0.0 已于 25 天前发布,我认为现在是更新 Discourse 的时候了 为此我完成了一些必要的步骤。
修复损坏的测试规范
在 lib/mini_sql_multisite_connection.rb 中添加空方法 trigger_transactional_callbacks?
UrlHelper 默认加载的是 ActionView::Helpers::UrlHelper 而非 lib/UrlHelper。我通过在前面添加 :: 解决了这个问题,不过,你们觉得是否应该更改该类的名称?
在 Rails 5.2.3 中,MigrationContext 接受一个参数,而在 6.0.0 中则需要额外的 schema 参数。
修复已弃用的方法
在引入 Zeitwerk 之前,首先使用经典自动加载器。
修复 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%
我将创建一个包含上述所有更改的拉取请求。如果您希望我对任何内容进行调整,或需要我进行额外测试以确保一切正常运行,请随时告知。
PR - DEV: Upgrading Discourse to Rails 6 by KrisKotlarek · Pull Request #8083 · discourse/discourse · GitHub
此致,
Kris
sam
(Sam Saffron)
2019 年9 月 9 日 07:56
2
太棒了
你能把它放进一个带有百分比变化的 Markdown 表格中吗?粗略一看,变化不大,这很好。
就插件而言,我们有一个 rake 任务 可以安装所有官方插件。你能运行它并确保插件规范在 Rails 6 上通过吗?(运行 rake plugin:spec 应该就能搞定)
我已更新原帖以显示表格。感谢您指出插件规格。我看到有两个规格在 Travis 上失败,我会查看并修复它们。
sam
(Sam Saffron)
2019 年9 月 9 日 09:38
4
这里有两个数字让我非常感兴趣:
6.0 版本的 RSS 性能提升了近 10% 。
主题(中位时间)——这是我们最常用的路由——速度提升了 22% 。
这确实是显著的性能提升。您能否在 topic-50 上持续测得 22% 的提速?能否确认实际页面渲染正常?
我运行了三次基准测试,这次的结果没那么显著。我的流程是在正确的分支(master 或 rails6)中键入 ruby script/bench.rb,然后按回车,之后不再触碰键盘,以免影响测试结果。
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)
我可以通过在 lib/freedom_patched/rspec-rails.rb 中添加一个新文件来对 rspec-rails 进行猴子补丁,但我想确认这是否是最佳方案。
我认为这是阻碍 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 有什么看法?
sam
(Sam Saffron)
2019 年9 月 11 日 00:44
7
天哪,我想我们只能先通过 monkey patch 来应对,直到 rspec-rails 4 发布,这里想不出更干净的修复方案了。
或者……也许……如果一切正常的话,暂时使用 beta 版本的 gem?
明白了,我今晚试试安装测试版,看看情况如何。更新过程可能会很轻松顺利。
我进行了一些额外的修复。
首先,我找到了为什么某个测试在 master 和 rails6 分支中都失败的原因 - FIX: Freezed time used in update_holiday_usernames_spec.rb should be UTC by KrisKotlarek · Pull Request #3 · discourse/discourse-calendar · GitHub
我还为各种插件中的已弃用方法创建了拉取请求:
我将最新的 master 分支变基到了 rails6 分支。
最后,我将 rspec-rails 更新到了 4.0.0.beta2 版本,在我的本地机器上运行正常。Travis 遇到了一些问题,但我看到其他拉取请求中也存在同样的问题,所以我认为这与 rspec-rails 的升级无关。
sam
(Sam Saffron)
2019 年9 月 12 日 00:45
10
现在已合并:
今天会密切关注,非常感谢大家的辛勤工作。
也要特别感谢 Rails 团队,让这次升级如此愉快!!
稍后会在本主题中分享一些漂亮的图表。
sam
(Sam Saffron)
2019 年9 月 12 日 05:47
11
升级过程相当平稳,这很好。性能表现均衡,且与之前相比几乎一致。
内存和 CPU 的使用情况也极为相似。
我唯一的担忧(也是我想要彻底查明的)是,Web Worker 似乎会定期出现持续几秒的“失控”线程。
因此,某些请求似乎会导致大量线程被创建,随后又迅速消失。
我将继续调查此问题,我们需要在线程数量较高时获取堆栈回溯信息,以便找出罪魁祸首。
鉴于其他方面表现都非常出色,我将不会回退此次升级。
sam
(Sam Saffron)
2019 年9 月 12 日 07:36
12
这应根据以下内容进行修复:
committed 07:34AM - 12 Sep 19 UTC
This is a temporary workaround for the issue in https://github.com/rails/rails/p… ull/36949
Discussing a proper fix in Rails with the Rails team.
Prior to this fix we were spinning up a thread every time we closed a connection
to the db.
这是 Rails 6 中新代码的结果,该代码保护对线程绑定变量的访问,以决定是否可以使用预编译语句。
在 Discourse 中,我们完全不使用预编译语句,因此此补丁并非我们所需要。
更多信息请参见:
master ← 97jaz:thread-local-prepared-statements
@rafaelfranca / @matthewd
There is a slight unintended consequence to this … change I will monkey patch out of Discourse.
The implementation of RubyThreadLocalVar wacks a finalizer on the object, the finalizer spins a thread.
https://github.com//blob/bbeacbcebf72668ed04df0738df7e3a654f7c177/lib/concurrent/atomic/ruby_thread_local_var.rb#L101-L111
We use no prepared statements, but are now paying the penalty of spinning up a thread every time a connection is closed.
This has this impact on my graphs for the big multisites:
https://meta.discourse.org/t/upgrading-discourse-to-rails-6/128004/11?u=sam
Since once false this thing can never be turned true I will just implement an Immutable ThreadLocalVar class here for the cases where prepared statements are false. (bind anything but current value and it raises.
```diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 91d5d08121..970a1f9e8f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -114,6 +114,18 @@ def self.quoted_table_names # :nodoc:
@quoted_table_names ||= {}
end
+ class StaticThreadLocalVar
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def bind(value)
+ raise "attempting to change immutable local var" if value != @value
+ yield if block_given?
+ end
+ end
+
def initialize(connection, logger = nil, config = {}) # :nodoc:
super()
@@ -132,7 +144,7 @@ def initialize(connection, logger = nil, config = {}) # :nodoc:
@prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
@visitor.extend(DetermineIfPreparableVisitor)
else
- @prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
+ @prepared_statement_status = StaticThreadLocalVar.new(false)
end
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
```
Thoughts? Should we patch this in Rails?
sam
(Sam Saffron)
2019 年9 月 12 日 10:21
13
而且……已确认……我的修复解决了大量线程激增的问题
另外值得一提的是……我是这样调试的:
我编写了这个小类
# 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
随后,我在 unicorn 的 after_fork 中引入该类,并运行 ThreadDetective.start(14)
该类利用 TracePoint 勤勉地监控每次线程的创建,并在每个线程上添加一个名为 origin 的小标记,以帮我追踪其来源。一旦观察到大量线程,它就会将相关信息输出到 STDERR。这些信息可以在 /var/www/discourse/logs/unicorn.stderr.log 中查看。
当我确认这 100 个线程都来自同一个位置后,隔离根本原因就变得非常容易了。
eviltrout
(Robin Ward)
2019 年9 月 12 日 17:13
14
我注意到在 Rails 6 的开发模式下,我无法再使用 dev.local 作为主机名,因此我添加了一个环境变量来配置该白名单:
committed 05:12PM - 12 Sep 19 UTC
Rails 6 seems to introduce a whitelist of allowed hosts. I personally
use `dev.l… ocal` for development and this no longer works.
This introduces a new ENV variable, `DISCOURSE_DEV_HOST`. If present,
it will whitelist that host for development mode.
sam
(Sam Saffron)
2019 年9 月 15 日 22:03
15
我们不需要长期保留这个猴子补丁,因为我们刚刚在 Rails 中修复了这个问题。
committed 03:11PM - 15 Sep 19 UTC
amotl
(Andreas Motl)
2020 年1 月 7 日 00:27
16
你好,
感谢你为将 Rails 6 引入 Discourse 所做的努力!我想冒昧地问一下,这预计何时会在 Discourse 中发布?或者它是否已经包含在 2.4.0.beta 中?我只是想了解一下,这是否可能会破坏用户实例上已安装的任何插件。
此致,
Andreas。
Falco
(Falco)
2020 年1 月 7 日 00:34
17
对于所有使用默认发布渠道的用户,此功能自 9 月起已正式上线。它首次在 2.4.0.beta5 版本中亮相。
amotl
(Andreas Motl)
2020 年1 月 7 日 01:38
18
好的,非常感谢。祝2020年一切顺利,也感谢你们在此所做的一切。