在我的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
定时工作正常,但相应的查询未被取消。它很容易在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"]
此代码将输出“超时”,然后在result = ActiveRecord::Base.connection.execute("SELECT 1 AS value")
上阻止,直到pg_sleep
查询完成。
如何从Rails中取消此类查询?我在Heroku上托管我的应用程序,因此权限仅限于运行pg_cancel_backend
或pg_terminate_backend
等命令。
答案 0 :(得分:1)
您可以在会话级别设置statement_timeout
(无需事务并跳过local
)。
或交易:
t=# begin; set local statement_timeout to 1; select pg_sleep(3);end;
BEGIN
Time: 0.096 ms
SET
Time: 0.098 ms
ERROR: canceling statement due to statement timeout
Time: 1.768 ms
ROLLBACK
Time: 0.066 ms
或作为用户的默认值:
alter user no_long_qries set statement_timeout to 1;
答案 1 :(得分:0)
通过将此超时添加到config/database.yml
:
default: &default
adapter: postgresql
...
variables:
statement_timeout: 25000
或者这是config/environment.rb
:
ActiveRecord::Base.connection.execute('set statement_timeout to 25000')
答案 2 :(得分:0)
我找到了不需要修改当前查询的很酷的解决方案。
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
在控制台中运行时,我需要添加检查连接是否处于活动状态。
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"]
如果不调用ActiveRecord::Base.connection.active?
,则会引发ActiveRecord::StatementInvalid: PG::QueryCanceled
。
这非常好用,但我确定是否有任何隐藏的问题。