mirror of https://github.com/rails/rails
Translate Trilogy syscall errors as conn failed
At GitHub we get a fair number of Trilogy `ETIMEDOUT` errors (for known
reasons that we might be able to improve somewhat, but I doubt we'll
make them go away entirely). These are very much retryable network
errors, so it'd be handy if these `ETIMEDOUT` errors were translated to
`ConnectionFailed` instead of `StatementInvalid`, making them
`retryable_connection_error`s.
ed2bc92b82/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (L1077)
We're already translating `ECONNRESET` (via matching on the error
message) and `EPIPE` to `ConnectionFailed`. Rather than adding another
case, this commit treats all of the Trilogy `SystemCallError` subclasses
as `ConnectionFailed`.
This requires bumping trilogy 2.7 so we can get
https://github.com/trilogy-libraries/trilogy/pull/143
This commit is contained in:
parent
776626ff98
commit
6616770ec9
2
Gemfile
2
Gemfile
|
@ -158,7 +158,7 @@ platforms :ruby, :windows do
|
||||||
group :db do
|
group :db do
|
||||||
gem "pg", "~> 1.3"
|
gem "pg", "~> 1.3"
|
||||||
gem "mysql2", "~> 0.5"
|
gem "mysql2", "~> 0.5"
|
||||||
gem "trilogy", ">= 2.5.0"
|
gem "trilogy", ">= 2.7.0"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -543,7 +543,7 @@ GEM
|
||||||
timeout (0.4.1)
|
timeout (0.4.1)
|
||||||
tomlrb (2.0.3)
|
tomlrb (2.0.3)
|
||||||
trailblazer-option (0.1.2)
|
trailblazer-option (0.1.2)
|
||||||
trilogy (2.6.0)
|
trilogy (2.7.0)
|
||||||
turbo-rails (1.5.0)
|
turbo-rails (1.5.0)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
activejob (>= 6.0.0)
|
activejob (>= 6.0.0)
|
||||||
|
@ -654,7 +654,7 @@ DEPENDENCIES
|
||||||
syntax_tree (= 6.1.1)
|
syntax_tree (= 6.1.1)
|
||||||
tailwindcss-rails
|
tailwindcss-rails
|
||||||
terser (>= 1.1.4)
|
terser (>= 1.1.4)
|
||||||
trilogy (>= 2.5.0)
|
trilogy (>= 2.7.0)
|
||||||
turbo-rails
|
turbo-rails
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
useragent
|
useragent
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require "active_record/connection_adapters/abstract_mysql_adapter"
|
require "active_record/connection_adapters/abstract_mysql_adapter"
|
||||||
|
|
||||||
gem "trilogy", "~> 2.4"
|
gem "trilogy", "~> 2.7"
|
||||||
require "trilogy"
|
require "trilogy"
|
||||||
|
|
||||||
require "active_record/connection_adapters/trilogy/database_statements"
|
require "active_record/connection_adapters/trilogy/database_statements"
|
||||||
|
@ -209,10 +209,11 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
case exception
|
case exception
|
||||||
when Errno::EPIPE, SocketError, IOError
|
when SocketError, IOError
|
||||||
return ConnectionFailed.new(message, connection_pool: @pool)
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
||||||
when ::Trilogy::Error
|
when ::Trilogy::Error
|
||||||
if /Connection reset by peer|TRILOGY_CLOSED_CONNECTION|TRILOGY_INVALID_SEQUENCE_ID|TRILOGY_UNEXPECTED_PACKET/.match?(exception.message)
|
if /TRILOGY_CLOSED_CONNECTION|TRILOGY_INVALID_SEQUENCE_ID|TRILOGY_UNEXPECTED_PACKET/.match?(exception.message) ||
|
||||||
|
exception.is_a?(SystemCallError)
|
||||||
return ConnectionFailed.new(message, connection_pool: @pool)
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -358,6 +358,38 @@ class TrilogyAdapterTest < ActiveRecord::TrilogyTestCase
|
||||||
assert_includes error.message, "/var/invalid.sock"
|
assert_includes error.message, "/var/invalid.sock"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "EPIPE raises ActiveRecord::ConnectionFailed" do
|
||||||
|
assert_raises(ActiveRecord::ConnectionFailed) do
|
||||||
|
@conn.raw_connection.stub(:query, -> (*) { raise Trilogy::SyscallError::EPIPE }) do
|
||||||
|
@conn.execute("SELECT 1")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ETIMEDOUT raises ActiveRecord::ConnectionFailed" do
|
||||||
|
assert_raises(ActiveRecord::ConnectionFailed) do
|
||||||
|
@conn.raw_connection.stub(:query, -> (*) { raise Trilogy::SyscallError::ETIMEDOUT }) do
|
||||||
|
@conn.execute("SELECT 1")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ECONNREFUSED raises ActiveRecord::ConnectionFailed" do
|
||||||
|
assert_raises(ActiveRecord::ConnectionFailed) do
|
||||||
|
@conn.raw_connection.stub(:query, -> (*) { raise Trilogy::SyscallError::ECONNREFUSED }) do
|
||||||
|
@conn.execute("SELECT 1")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ECONNRESET raises ActiveRecord::ConnectionFailed" do
|
||||||
|
assert_raises(ActiveRecord::ConnectionFailed) do
|
||||||
|
@conn.raw_connection.stub(:query, -> (*) { raise Trilogy::SyscallError::ECONNRESET }) do
|
||||||
|
@conn.execute("SELECT 1")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Create a temporary subscription to verify notification is sent.
|
# Create a temporary subscription to verify notification is sent.
|
||||||
# Optionally verify the notification payload includes expected types.
|
# Optionally verify the notification payload includes expected types.
|
||||||
def assert_notification(notification, expected_payload = {}, &block)
|
def assert_notification(notification, expected_payload = {}, &block)
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Rails
|
||||||
def gem_for_database(database = options[:database])
|
def gem_for_database(database = options[:database])
|
||||||
case database
|
case database
|
||||||
when "mysql" then ["mysql2", ["~> 0.5"]]
|
when "mysql" then ["mysql2", ["~> 0.5"]]
|
||||||
when "trilogy" then ["trilogy", ["~> 2.4"]]
|
when "trilogy" then ["trilogy", ["~> 2.7"]]
|
||||||
when "postgresql" then ["pg", ["~> 1.1"]]
|
when "postgresql" then ["pg", ["~> 1.1"]]
|
||||||
when "sqlite3" then ["sqlite3", ["~> 1.4"]]
|
when "sqlite3" then ["sqlite3", ["~> 1.4"]]
|
||||||
when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
|
when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
|
||||||
|
|
|
@ -100,7 +100,7 @@ module Rails
|
||||||
|
|
||||||
assert_file("Gemfile") do |content|
|
assert_file("Gemfile") do |content|
|
||||||
assert_match "# Use trilogy as the database for Active Record", content
|
assert_match "# Use trilogy as the database for Active Record", content
|
||||||
assert_match 'gem "trilogy", "~> 2.4"', content
|
assert_match 'gem "trilogy", "~> 2.7"', content
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_file("Dockerfile") do |content|
|
assert_file("Dockerfile") do |content|
|
||||||
|
|
Loading…
Reference in New Issue