Skip to content

Commit f5c0ba5

Browse files
authored
Merge pull request rsim#2523 from yahonda/translate-lost-connection-to-connection-failed
Translate lost-connection errors to ActiveRecord::ConnectionFailed
2 parents bfac4e3 + f72da8a commit f5c0ba5

5 files changed

Lines changed: 49 additions & 9 deletions

File tree

lib/active_record/connection_adapters/oracle_enhanced/connection.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ def _select_value(arel, name = nil, binds = [])
118118
def database_version
119119
raise NoMethodError, "Not implemented for this raw driver"
120120
end
121+
122+
# ORA-00028 your session has been killed
123+
# ORA-01012 not logged on
124+
# ORA-03113 end-of-file on communication channel
125+
# ORA-03114 not connected to ORACLE
126+
# ORA-03135 connection lost contact
127+
LOST_CONNECTION_ERROR_CODES = [28, 1012, 3113, 3114, 3135] # :nodoc:
128+
121129
class ConnectionException < StandardError # :nodoc:
122130
end
123131
end

lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def with_retry(allow_retry: false, &block)
263263
begin
264264
yield if block_given?
265265
rescue Java::JavaSql::SQLException => e
266-
raise unless /^(Closed Connection|Io exception:|No more data to read from socket|IO Error:|ORA-03113:|ORA-03114:|ORA-17008:)/.match?(e.message)
266+
raise unless lost_connection?(e)
267267
@active = false
268268
raise unless should_retry
269269
should_retry = false
@@ -537,6 +537,17 @@ def error_code(exception)
537537
end
538538
end
539539

540+
# JDBC SQLException messages that signal a dropped connection
541+
# without a meaningful Oracle error code attached. ORA-17008
542+
# (Closed Connection) surfaces as the "Closed Connection" message.
543+
LOST_CONNECTION_MESSAGE = /\A(Closed Connection|Io exception:|No more data to read from socket|IO Error:)/
544+
545+
def lost_connection?(exception)
546+
return false unless exception.is_a?(Java::JavaSql::SQLException)
547+
LOST_CONNECTION_ERROR_CODES.include?(exception.getErrorCode) ||
548+
LOST_CONNECTION_MESSAGE.match?(exception.message)
549+
end
550+
540551
def get_ruby_value_from_result_set(rset, i, type_name, get_lob_value = true)
541552
case type_name
542553
when :NUMBER

lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ def error_code(exception)
251251
end
252252
end
253253

254+
def lost_connection?(exception)
255+
exception.is_a?(OCIError) && LOST_CONNECTION_ERROR_CODES.include?(exception.code)
256+
end
257+
254258
def typecast_result_value(value, get_lob_value)
255259
case value
256260
when Integer
@@ -435,21 +439,14 @@ def reset! # :nodoc:
435439
end
436440
end
437441

438-
# ORA-00028: your session has been killed
439-
# ORA-01012: not logged on
440-
# ORA-03113: end-of-file on communication channel
441-
# ORA-03114: not connected to ORACLE
442-
# ORA-03135: connection lost contact
443-
LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114, 3135 ] # :nodoc:
444-
445442
# Adds auto-recovery functionality.
446443
def with_retry(allow_retry: false) # :nodoc:
447444
should_retry = (allow_retry || self.class.auto_retry?) && autocommit?
448445

449446
begin
450447
yield
451448
rescue OCIException => e
452-
raise unless e.is_a?(OCIError) && LOST_CONNECTION_ERROR_CODES.include?(e.code)
449+
raise unless e.is_a?(OCIError) && ActiveRecord::ConnectionAdapters::OracleEnhanced::LOST_CONNECTION_ERROR_CODES.include?(e.code)
453450
@active = false
454451
raise unless should_retry
455452
should_retry = false

lib/active_record/connection_adapters/oracle_enhanced_adapter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,8 @@ def extract_limit(sql_type) # :nodoc:
799799
end
800800

801801
def translate_exception(exception, message:, sql:, binds:) # :nodoc:
802+
return ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool) if _connection.lost_connection?(exception)
803+
802804
case _connection.error_code(exception)
803805
when 1
804806
RecordNotUnique.new(message, sql: sql, binds: binds)

spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,13 @@ def kill_current_session
475475
@sys_conn.exec "ALTER SYSTEM KILL SESSION '#{sid_serial}' IMMEDIATE"
476476
end
477477

478+
def connection_id_from_server(conn)
479+
audsid = conn.select("SELECT userenv('sessionid') audsid FROM dual").first["audsid"]
480+
@sys_conn.select("SELECT s.sid||','||s.serial# sid_serial
481+
FROM v$session s
482+
WHERE audsid = '#{audsid}'").first["sid_serial"]
483+
end
484+
478485
it "should reconnect and execute SQL statement if connection is lost and auto retry is enabled" do
479486
# @conn.auto_retry = true
480487
ActiveRecord::Base.connection.auto_retry = true
@@ -487,6 +494,21 @@ def kill_current_session
487494
expect(@conn.exec("SELECT * FROM dual", allow_retry: true)).not_to be_nil
488495
end
489496

497+
# Regression test ported from rails/rails#46273, which only covers
498+
# Mysql2 and PostgreSQL in the Rails repository.
499+
it "adapter #execute is retryable when allow_retry: true is passed" do
500+
previous_auto_retry = ActiveRecord::Base.connection.auto_retry
501+
ActiveRecord::Base.connection.auto_retry = false
502+
begin
503+
initial_connection_id = connection_id_from_server(@conn)
504+
kill_current_session
505+
expect { ActiveRecord::Base.connection.execute("SELECT 1 FROM dual", allow_retry: true) }.not_to raise_error
506+
expect(connection_id_from_server(@conn)).not_to eq(initial_connection_id)
507+
ensure
508+
ActiveRecord::Base.connection.auto_retry = previous_auto_retry
509+
end
510+
end
511+
490512
it "should not reconnect and execute SQL statement if connection is lost and auto retry is disabled" do
491513
# @conn.auto_retry = false
492514
ActiveRecord::Base.connection.auto_retry = false

0 commit comments

Comments
 (0)