Skip to content

Commit 3e01b26

Browse files
committed
Merge pull request rails#48295 from luanzeba/connection_attr_reader
Store `connection_pool` in database-related exceptions
2 parents 54ce634 + 7d4c88d commit 3e01b26

15 files changed

+191
-80
lines changed

activerecord/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
* Include the `connection_pool` with exceptions raised from an adapter.
2+
3+
The `connection_pool` provides added context such as the connection used
4+
that led to the exception as well as which role and shard.
5+
6+
*Luan Vieira*
7+
18
* Support multiple column ordering for `find_each`, `find_in_batches` and `in_batches`.
29

310
When find_each/find_in_batches/in_batches are performed on a table with composite primary keys, ascending or descending order can be selected for each key.

activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,8 @@ def new_connection
690690
connection.pool = self
691691
connection.check_version
692692
connection
693+
rescue ConnectionNotEstablished => ex
694+
raise ex.set_pool(self)
693695
end
694696

695697
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting

activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ def translate_exception(exception, message:, sql:, binds:)
11541154
when RuntimeError, ActiveRecord::ActiveRecordError
11551155
exception
11561156
else
1157-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
1157+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
11581158
end
11591159
end
11601160

activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ def handle_warnings(sql)
720720
@affected_rows_before_warnings = @raw_connection.affected_rows
721721
result = @raw_connection.query("SHOW WARNINGS")
722722
result.each do |level, code, message|
723-
warning = SQLWarning.new(message, code, level, sql)
723+
warning = SQLWarning.new(message, code, level, sql, @pool)
724724
next if warning_ignored?(warning)
725725

726726
ActiveRecord.db_warnings_action.call(warning)
@@ -764,40 +764,40 @@ def translate_exception(exception, message:, sql:, binds:)
764764
case error_number(exception)
765765
when nil
766766
if exception.message.match?(/MySQL client is not connected/i)
767-
ConnectionNotEstablished.new(exception)
767+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
768768
else
769769
super
770770
end
771771
when ER_CONNECTION_KILLED, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
772-
ConnectionFailed.new(message, sql: sql, binds: binds)
772+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
773773
when ER_DB_CREATE_EXISTS
774-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
774+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
775775
when ER_DUP_ENTRY
776-
RecordNotUnique.new(message, sql: sql, binds: binds)
776+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
777777
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
778-
InvalidForeignKey.new(message, sql: sql, binds: binds)
778+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
779779
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
780-
mismatched_foreign_key(message, sql: sql, binds: binds)
780+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
781781
when ER_CANNOT_CREATE_TABLE
782782
if message.include?("errno: 150")
783-
mismatched_foreign_key(message, sql: sql, binds: binds)
783+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
784784
else
785785
super
786786
end
787787
when ER_DATA_TOO_LONG
788-
ValueTooLong.new(message, sql: sql, binds: binds)
788+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
789789
when ER_OUT_OF_RANGE
790-
RangeError.new(message, sql: sql, binds: binds)
790+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
791791
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
792-
NotNullViolation.new(message, sql: sql, binds: binds)
792+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
793793
when ER_LOCK_DEADLOCK
794-
Deadlocked.new(message, sql: sql, binds: binds)
794+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
795795
when ER_LOCK_WAIT_TIMEOUT
796-
LockWaitTimeout.new(message, sql: sql, binds: binds)
796+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
797797
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
798-
StatementTimeout.new(message, sql: sql, binds: binds)
798+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
799799
when ER_QUERY_INTERRUPTED
800-
QueryCanceled.new(message, sql: sql, binds: binds)
800+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
801801
else
802802
super
803803
end
@@ -943,11 +943,12 @@ def mismatched_foreign_key_details(message:, sql:)
943943
options
944944
end
945945

946-
def mismatched_foreign_key(message, sql:, binds:)
946+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
947947
options = {
948948
message: message,
949949
sql: sql,
950950
binds: binds,
951+
connection_pool: connection_pool
951952
}
952953

953954
if sql

activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ def text_type?(type)
155155

156156
def connect
157157
@raw_connection = self.class.new_client(@connection_parameters)
158+
rescue ConnectionNotEstablished => ex
159+
raise ex.set_pool(@pool)
158160
end
159161

160162
def reconnect
@@ -179,12 +181,12 @@ def get_full_version
179181

180182
def translate_exception(exception, message:, sql:, binds:)
181183
if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
182-
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
184+
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
183185
elsif exception.is_a?(::Mysql2::Error::ConnectionError)
184186
if exception.message.match?(/MySQL client is not connected/i)
185-
ActiveRecord::ConnectionNotEstablished.new(exception)
187+
ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
186188
else
187-
ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds)
189+
ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
188190
end
189191
else
190192
super

activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -742,41 +742,41 @@ def translate_exception(exception, message:, sql:, binds:)
742742
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
743743
when nil
744744
if exception.message.match?(/connection is closed/i)
745-
ConnectionNotEstablished.new(exception)
745+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
746746
elsif exception.is_a?(PG::ConnectionBad)
747747
# libpq message style always ends with a newline; the pg gem's internal
748748
# errors do not. We separate these cases because a pg-internal
749749
# ConnectionBad means it failed before it managed to send the query,
750750
# whereas a libpq failure could have occurred at any time (meaning the
751751
# server may have already executed part or all of the query).
752752
if exception.message.end_with?("\n")
753-
ConnectionFailed.new(exception)
753+
ConnectionFailed.new(exception, connection_pool: @pool)
754754
else
755-
ConnectionNotEstablished.new(exception)
755+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
756756
end
757757
else
758758
super
759759
end
760760
when UNIQUE_VIOLATION
761-
RecordNotUnique.new(message, sql: sql, binds: binds)
761+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
762762
when FOREIGN_KEY_VIOLATION
763-
InvalidForeignKey.new(message, sql: sql, binds: binds)
763+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
764764
when VALUE_LIMIT_VIOLATION
765-
ValueTooLong.new(message, sql: sql, binds: binds)
765+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
766766
when NUMERIC_VALUE_OUT_OF_RANGE
767-
RangeError.new(message, sql: sql, binds: binds)
767+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
768768
when NOT_NULL_VIOLATION
769-
NotNullViolation.new(message, sql: sql, binds: binds)
769+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
770770
when SERIALIZATION_FAILURE
771-
SerializationFailure.new(message, sql: sql, binds: binds)
771+
SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
772772
when DEADLOCK_DETECTED
773-
Deadlocked.new(message, sql: sql, binds: binds)
773+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
774774
when DUPLICATE_DATABASE
775-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
775+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
776776
when LOCK_NOT_AVAILABLE
777-
LockWaitTimeout.new(message, sql: sql, binds: binds)
777+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
778778
when QUERY_CANCELED
779-
QueryCanceled.new(message, sql: sql, binds: binds)
779+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
780780
else
781781
super
782782
end
@@ -940,6 +940,8 @@ def prepare_statement(sql, binds)
940940
# connected server's characteristics.
941941
def connect
942942
@raw_connection = self.class.new_client(@connection_parameters)
943+
rescue ConnectionNotEstablished => ex
944+
raise ex.set_pool(@pool)
943945
end
944946

945947
def reconnect
@@ -966,7 +968,7 @@ def configure_connection
966968
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
967969
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
968970
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
969-
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level)
971+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
970972
end
971973
end
972974

activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def initialize(...)
116116
Dir.mkdir(dirname)
117117
rescue Errno::ENOENT => error
118118
if error.message.include?("No such file or directory")
119-
raise ActiveRecord::NoDatabaseError
119+
raise ActiveRecord::NoDatabaseError.new(connection_pool: @pool)
120120
else
121121
raise
122122
end
@@ -609,13 +609,13 @@ def translate_exception(exception, message:, sql:, binds:)
609609
# Older versions of SQLite return:
610610
# column *column_name* is not unique
611611
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
612-
RecordNotUnique.new(message, sql: sql, binds: binds)
612+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
613613
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
614-
NotNullViolation.new(message, sql: sql, binds: binds)
614+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
615615
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
616-
InvalidForeignKey.new(message, sql: sql, binds: binds)
616+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
617617
elsif exception.message.match?(/called on a closed database/i)
618-
ConnectionNotEstablished.new(exception)
618+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
619619
else
620620
super
621621
end
@@ -679,6 +679,8 @@ def build_statement_pool
679679

680680
def connect
681681
@raw_connection = self.class.new_client(@connection_parameters)
682+
rescue ConnectionNotEstablished => ex
683+
raise ex.set_pool(@pool)
682684
end
683685

684686
def reconnect

activerecord/lib/active_record/connection_adapters/trilogy/lost_connection_exception_translator.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ module Trilogy
66
class LostConnectionExceptionTranslator
77
attr_reader :exception, :message, :error_number
88

9-
def initialize(exception, message, error_number)
9+
def initialize(exception, message, error_number, connection_pool)
1010
@exception = exception
1111
@message = message
1212
@error_number = error_number
13+
@connection_pool = connection_pool
1314
end
1415

1516
def translate
@@ -25,23 +26,23 @@ def translate
2526
def translate_database_exception
2627
case error_number
2728
when ER_SERVER_SHUTDOWN
28-
Errors::ServerShutdown.new(message)
29+
Errors::ServerShutdown.new(message, connection_pool: @connection_pool)
2930
when CR_SERVER_LOST, CR_SERVER_LOST_EXTENDED
30-
Errors::ServerLost.new(message)
31+
Errors::ServerLost.new(message, connection_pool: @connection_pool)
3132
when CR_SERVER_GONE_ERROR
32-
Errors::ServerGone.new(message)
33+
Errors::ServerGone.new(message, connection_pool: @connection_pool)
3334
end
3435
end
3536

3637
def translate_ruby_exception
3738
case exception
3839
when Errno::EPIPE
39-
Errors::BrokenPipe.new(message)
40+
Errors::BrokenPipe.new(message, connection_pool: @connection_pool)
4041
when SocketError, IOError
41-
Errors::SocketError.new(message)
42+
Errors::SocketError.new(message, connection_pool: @connection_pool)
4243
when ::Trilogy::ConnectionError
4344
if message.include?("Connection reset by peer")
44-
Errors::ConnectionResetByPeer.new(message)
45+
Errors::ConnectionResetByPeer.new(message, connection_pool: @connection_pool)
4546
end
4647
end
4748
end
@@ -51,11 +52,11 @@ def translate_trilogy_exception
5152

5253
case message
5354
when /TRILOGY_CLOSED_CONNECTION/
54-
Errors::ClosedConnection.new(message)
55+
Errors::ClosedConnection.new(message, connection_pool: @connection_pool)
5556
when /TRILOGY_INVALID_SEQUENCE_ID/
56-
Errors::InvalidSequenceId.new(message)
57+
Errors::InvalidSequenceId.new(message, connection_pool: @connection_pool)
5758
when /TRILOGY_UNEXPECTED_PACKET/
58-
Errors::UnexpectedPacket.new(message)
59+
Errors::UnexpectedPacket.new(message, connection_pool: @connection_pool)
5960
end
6061
end
6162
end

activerecord/lib/active_record/connection_adapters/trilogy_adapter.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ def connection=(conn)
192192

193193
def connect
194194
self.connection = self.class.new_client(@config)
195+
rescue ConnectionNotEstablished => ex
196+
raise ex.set_pool(@pool)
195197
end
196198

197199
def reconnect
@@ -212,11 +214,11 @@ def get_full_version
212214

213215
def translate_exception(exception, message:, sql:, binds:)
214216
if exception.is_a?(::Trilogy::TimeoutError) && !exception.error_code
215-
return ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
217+
return ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
216218
end
217219
error_code = exception.error_code if exception.respond_to?(:error_code)
218220

219-
Trilogy::LostConnectionExceptionTranslator.new(exception, message, error_code).translate || super
221+
Trilogy::LostConnectionExceptionTranslator.new(exception, message, error_code, @pool).translate || super
220222
end
221223

222224
def default_prepared_statements

0 commit comments

Comments
 (0)