1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Increment @counter of prepared postgres statements prior to running the query.

If the next query or the prepared statement itself get interrupted, this prevents the database session getting stuck perpetually retrying to recreate the same prepared statement.
This commit is contained in:
Bill Harding 2021-01-06 08:34:37 -08:00 committed by Martin Tepper
parent 56ea638290
commit 2416da9975
3 changed files with 25 additions and 5 deletions

View file

@ -1,3 +1,9 @@
* Increment postgres prepared statement counter before making a prepared statement, so if the statement is aborted
without Rails knowledge (e.g., if app gets kill -9d during long-running query or due to Rack::Timeout), app won't end
up in perpetual crash state for being inconsistent with Postgres.
*wbharding*, *Martin Tepper*
* Switch to database adapter return type for `ActiveRecord::Calculations.calculate`
when called with `:average` (aliased as `ActiveRecord::Calculations.average`)

View file

@ -228,11 +228,7 @@ module ActiveRecord
end
def next_key
"a#{@counter + 1}"
end
def []=(sql, key)
super.tap { @counter += 1 }
"a#{@counter += 1}"
end
private

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true
require "cases/helper"
require "models/computer"
require "models/developer"
module ActiveRecord
module ConnectionAdapters
@ -16,6 +18,8 @@ module ActiveRecord
end
class StatementPoolTest < ActiveRecord::PostgreSQLTestCase
fixtures :developers
if Process.respond_to?(:fork)
def test_cache_is_per_pid
cache = StatementPool.new nil, 10
@ -37,6 +41,20 @@ module ActiveRecord
cache["foo"] = "bar"
assert_nothing_raised { cache.clear }
end
def test_prepared_statements_do_not_get_stuck_on_query_interruption
pg_connection = ActiveRecord::Base.connection.instance_variable_get(:@connection)
pg_connection.stub(:get_last_result, -> { raise "random error" }) do
assert_raises(RuntimeError) do
Developer.where(name: "David").last
end
# without fix, this raises PG::DuplicatePstatement: ERROR: prepared statement "a3" already exists
assert_raises(RuntimeError) do
Developer.where(name: "David").last
end
end
end
end
end
end