Mic*_*iak 5 postgresql ruby-on-rails heroku
在我的 Rails 应用程序中,我有一个负责生成报告的控制器。其中一些报告需要很长时间才能生成,在 Heroku 上超过了 30 秒的限制。在这种情况下,我想在 25 秒后向用户显示通知并取消数据库查询。我最初的想法是使用Timeout.
class ReportsController < ApplicationController
def expensive_report
Timeout.timeout(25) do
@results = ExpensiveQuery.new(params).results
end
rescue Timeout::Error
render action: "timeout"
end
end
Run Code Online (Sandbox Code Playgroud)
超时工作正常,但不会取消相应的查询。在 Rails 控制台中很容易重现
begin
Timeout.timeout(1) do
ActiveRecord::Base.connection.execute("SELECT pg_sleep(10)")
end
rescue Timeout::Error
puts "Timeout"
end
result = ActiveRecord::Base.connection.execute("SELECT 1 AS value")
puts result[0]["value"]
Run Code Online (Sandbox Code Playgroud)
此代码将输出“超时”,然后在线阻塞,result = ActiveRecord::Base.connection.execute("SELECT 1 AS value")直到pg_sleep查询完成。
如何从 Rails 中取消此类查询?我在 Heroku 上托管我的应用程序,因此权限仅限于运行诸如pg_cancel_backend或 之类的命令pg_terminate_backend。
我找到了很酷的解决方案,不需要修改当前查询。
class ReportsController < ApplicationController
def expensive_report
Timeout.timeout(25) do
@results = ExpensiveQuery.new(params).results
end
rescue Timeout::Error
ActiveRecord::Base.connection.raw_connection.cancel
render action: "timeout"
end
end
Run Code Online (Sandbox Code Playgroud)
在控制台中运行时,我需要添加检查连接是否处于活动状态。
begin
Timeout.timeout(1) do
ActiveRecord::Base.connection.execute("SELECT pg_sleep(10)")
end
rescue Timeout::Error
ActiveRecord::Base.connection.raw_connection.cancel
ActiveRecord::Base.connection.active?
puts "Timeout"
end
result = ActiveRecord::Base.connection.execute("SELECT 1 AS value")
puts result[0]["value"]
Run Code Online (Sandbox Code Playgroud)
不跟注ActiveRecord::Base.connection.active?就会加注ActiveRecord::StatementInvalid: PG::QueryCanceled。
这工作得很好,但我还不确定是否存在任何隐藏的问题。