【发布时间】:2018-10-23 19:31:16
【问题描述】:
我已经将database.yml 中的statement_timeout 配置为几秒钟,但我的应用程序中有一些昂贵的查询,这需要更长的查询执行时间。在每个查询级别上实现此目标的推荐方法是什么?
我需要临时将statement_timeout 设置为更大的值,执行查询并将其重置为默认值?或者甚至不需要重置?
【问题讨论】:
标签: ruby-on-rails database postgresql timeout
我已经将database.yml 中的statement_timeout 配置为几秒钟,但我的应用程序中有一些昂贵的查询,这需要更长的查询执行时间。在每个查询级别上实现此目标的推荐方法是什么?
我需要临时将statement_timeout 设置为更大的值,执行查询并将其重置为默认值?或者甚至不需要重置?
【问题讨论】:
标签: ruby-on-rails database postgresql timeout
我认为您只能通过更改整个连接的statement_timeout 然后将其恢复回来来实现:
def execute_expensive_query
ActiveRecord::Base.connection.execute 'SET statement_timeout = 600000' # 10 minutes
# DB query with long execution time
ensure
ActiveRecord::Base.connection.execute 'SET statement_timeout = 5000' # 5 seconds
end
在数据库级别,您可以为当前事务设置statement_timeout,仅按照this guide:
BEGIN;
SET LOCAL statement_timeout = 250;
...
COMMIT;
【讨论】:
为了扩展已接受的答案,以下是实现模块 DatabaseTimeout 的方法,该模块还确保将 statement_timeout 设置重置为其原始值。
# Ruby's `Timeout` doesn't prevent queries from running for a long time.
#
# To prove this, run the following in a console (yes, twice):
# Timeout.timeout(1.second) { ActiveRecord::Base.connection.execute('SELECT pg_sleep(100);') }
# Timeout.timeout(1.second) { ActiveRecord::Base.connection.execute('SELECT pg_sleep(100);') }
# => The 2nd call should run for a long time.
#
# DatabaseTimeout's purpose is to enforce that each query doesn't run for more than the given timeout:
# DatabaseTimeout.timeout(1.second) { ActiveRecord::Base.connection.execute('SELECT pg_sleep(100);') }
# DatabaseTimeout.timeout(1.second) { ActiveRecord::Base.connection.execute('SELECT pg_sleep(100);') }
# => Both queries are interrupted after 1 second
module DatabaseTimeout
# Usage: DatabaseTimeout.timeout(10) { run_some_query }
def self.timeout(nb_seconds)
original_timeout = ActiveRecord::Base.connection.execute('SHOW statement_timeout').first['statement_timeout']
ActiveRecord::Base.connection.execute("SET statement_timeout = '#{nb_seconds.to_i}s'")
yield
ensure
if original_timeout
ActiveRecord::Base.connection.execute("SET statement_timeout = #{original_timeout}")
end
end
end
【讨论】:
DatabaseTimeout.timeout 时,SHOW 语句和 2 个 SET 语句是事务的一部分,我认为这不是问题。
DatabaseTimeout.timeout 包装在一个数据库事务中,或者冒着更改超时时间污染并发查询(在同一个 AR 连接上)的风险,不是吗?
statement_timeout,可以使用SET LOCAL statement_timeout = '10s';,需要包装在事务中:)