Add new error class `QueryCanceled` which will be raised when canceling statement due to user request (#31235)
This changes `StatementTimeout` to `QueryCanceled` for PostgreSQL. In MySQL, errno 1317 (`ER_QUERY_INTERRUPTED`) is only used when the query is manually cancelled. But in PostgreSQL, `QUERY_CANCELED` error code (57014) which is used `StatementTimeout` is also used when the both case. And, we can not tell which reason happened. So I decided to introduce new error class `QueryCanceled` closer to the error code name.
This commit is contained in:
parent
ad0630f0ae
commit
0e2cd3d749
|
@ -1,3 +1,8 @@
|
|||
* Add new error class `QueryCanceled` which will be raised
|
||||
when canceling statement due to user request.
|
||||
|
||||
*Ryuta Kamizono*
|
||||
|
||||
* Add `#up_only` to database migrations for code that is only relevant when
|
||||
migrating up, e.g. populating a new column.
|
||||
|
||||
|
|
|
@ -635,6 +635,7 @@ module ActiveRecord
|
|||
ER_CANNOT_ADD_FOREIGN = 1215
|
||||
ER_CANNOT_CREATE_TABLE = 1005
|
||||
ER_LOCK_WAIT_TIMEOUT = 1205
|
||||
ER_QUERY_INTERRUPTED = 1317
|
||||
ER_QUERY_TIMEOUT = 3024
|
||||
|
||||
def translate_exception(exception, message)
|
||||
|
@ -663,6 +664,8 @@ module ActiveRecord
|
|||
LockWaitTimeout.new(message)
|
||||
when ER_QUERY_TIMEOUT
|
||||
StatementTimeout.new(message)
|
||||
when ER_QUERY_INTERRUPTED
|
||||
QueryCanceled.new(message)
|
||||
else
|
||||
super
|
||||
end
|
||||
|
|
|
@ -420,7 +420,7 @@ module ActiveRecord
|
|||
when LOCK_NOT_AVAILABLE
|
||||
LockWaitTimeout.new(message)
|
||||
when QUERY_CANCELED
|
||||
StatementTimeout.new(message)
|
||||
QueryCanceled.new(message)
|
||||
else
|
||||
super
|
||||
end
|
||||
|
|
|
@ -343,6 +343,10 @@ module ActiveRecord
|
|||
class StatementTimeout < StatementInvalid
|
||||
end
|
||||
|
||||
# QueryCanceled will be raised when canceling statement due to user request.
|
||||
class QueryCanceled < StatementInvalid
|
||||
end
|
||||
|
||||
# UnknownAttributeReference is raised when an unknown and potentially unsafe
|
||||
# value is passed to a query method when allow_unsafe_raw_sql is set to
|
||||
# :disabled. For example, passing a non column name value to a relation's
|
||||
|
|
|
@ -116,5 +116,32 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "raises QueryCanceled when canceling statement due to user request" do
|
||||
assert_raises(ActiveRecord::QueryCanceled) do
|
||||
s = Sample.create!(value: 1)
|
||||
latch = Concurrent::CountDownLatch.new
|
||||
|
||||
thread = Thread.new do
|
||||
Sample.transaction do
|
||||
Sample.lock.find(s.id)
|
||||
latch.count_down
|
||||
sleep(0.5)
|
||||
conn = Sample.connection
|
||||
pid = conn.query_value("SELECT id FROM information_schema.processlist WHERE info LIKE '% FOR UPDATE'")
|
||||
conn.execute("KILL QUERY #{pid}")
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
Sample.transaction do
|
||||
latch.wait
|
||||
Sample.lock.find(s.id)
|
||||
end
|
||||
ensure
|
||||
thread.join
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -120,8 +120,8 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
test "raises StatementTimeout when statement timeout exceeded" do
|
||||
assert_raises(ActiveRecord::StatementTimeout) do
|
||||
test "raises QueryCanceled when statement timeout exceeded" do
|
||||
assert_raises(ActiveRecord::QueryCanceled) do
|
||||
s = Sample.create!(value: 1)
|
||||
latch1 = Concurrent::CountDownLatch.new
|
||||
latch2 = Concurrent::CountDownLatch.new
|
||||
|
@ -148,6 +148,33 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
test "raises QueryCanceled when canceling statement due to user request" do
|
||||
assert_raises(ActiveRecord::QueryCanceled) do
|
||||
s = Sample.create!(value: 1)
|
||||
latch = Concurrent::CountDownLatch.new
|
||||
|
||||
thread = Thread.new do
|
||||
Sample.transaction do
|
||||
Sample.lock.find(s.id)
|
||||
latch.count_down
|
||||
sleep(0.5)
|
||||
conn = Sample.connection
|
||||
pid = conn.query_value("SELECT pid FROM pg_stat_activity WHERE query LIKE '% FOR UPDATE'")
|
||||
conn.execute("SELECT pg_cancel_backend(#{pid})")
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
Sample.transaction do
|
||||
latch.wait
|
||||
Sample.lock.find(s.id)
|
||||
end
|
||||
ensure
|
||||
thread.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_warning_suppression
|
||||
|
|
Loading…
Reference in New Issue