我想为postgres设置 statement_timeout
为个别迁移。我似乎无法做到这一点。这是我的实验。
def change
execute <<~SQL
SET LOCAL statement_timeout = 1; -- ms
-- this does not cause a timeout which is expected, because pg
-- only applies the timeout to the next protocol message / statement,
-- and rails sends everthing inside execute in the same statement
select pg_sleep(1); -- seconds
SQL
# if uncommented, this DOES cause a timeout, which is expected
# execute <<~SQL
# select pg_sleep(1); -- seconds
# SQL
# this does not cause a timeout, which is unexpected
remove_column :foos, :bar
# we do get here, which is unexpected
raise "we finished"
end
我该怎么做?
你可以尝试使用低级别的 exec_query
或更低 execute_and_clear
或更低 exec_no_cache
.
我想你的问题是关于设置 statement_timeout
为一个 个人陈述 在您的迁移过程中(不是为一个 单独迁移).
您可以通过使用 Postgres的 SET
而不是 SET LOCAL
.
class SomeMigration < ActiveRecord::Migration[6.0]
# Disables BEGIN/COMMIT around migration. SET LOCAL will have no effect.
disable_ddl_transaction!
def change
# Applies to the current database session, until changed or reset.
execute <<~SQL
SET statement_timeout = '1s';
SQL
# Will raise `ActiveRecord::QueryCanceled`.
execute <<~SQL
SELECT pg_sleep(2);
SQL
# Resets (disables) timeout.
execute <<~SQL
SET statement_timeout = DEFAULT;
SQL
# Will not raise.
execute <<~SQL
SELECT pg_sleep(2);
SQL
end
end
请注意,您 可以 使用 SET LOCAL
如果你不打电话 disable_ddl_transaction!
. 两者结合是行不通的,因为 SET LOCAL
在事务块外没有任何影响。它会记录下来。
WARNING: SET LOCAL can only be used in transaction blocks
PS: 你对 disable_ddl_transaction!
会提高 NoMethodError
. 应该是在 def change
定义。
开启后 日志时间 并做了更多的调查,我想我得出的结论是,postgres只是不认为掉列需要超过1ms的时间,即使是这样的事情被记录下来。
statement: ALTER TABLE "foos" DROP COLUMN "bar"
duration: 1.171 ms
duration: 0.068 ms
duration: 0.328 ms
但是用下面的配方,
class TimeoutTest < ActiveRecord::Migration[5.2]
def change
execute <<~SQL
SET statement_timeout = 1; -- ms
SET lock_timeout = 1; -- ms
SQL
# I can't get this to fail due to a statement timeout, but does fail due to lock timeout
# interestingly, if both timeouts are set, it will be statement timeout which happens first. not
# sure if this is a pg bug, or if calculating the lock timeout somehow increases the overall statement
# overhead
remove_column :foos, :bar
# With 100k of rows of test data, this does fail the statement timeout
Foo.unscoped.update_all(bar: 5)
end
end