Skip to content

Commit 8e0b7b3

Browse files
committed
Handle connection failure by trying to reconnect and translating the error to ConnectionFailedError so the caller knows to retry
1 parent 7e27df4 commit 8e0b7b3

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

Dockerfile.dev

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ FROM ruby:2.4.0
22
MAINTAINER data@localytics.com
33

44
ENV DEBIAN_FRONTEND noninteractive
5+
RUN echo "deb http://deb.debian.org/debian/ jessie main" > /etc/apt/sources.list
6+
RUN echo "deb-src http://deb.debian.org/debian/ jessie main" >> /etc/apt/sources.list
7+
RUN echo "deb http://security.debian.org/ jessie/updates main" >> /etc/apt/sources.list
8+
RUN echo "deb-src http://security.debian.org/ jessie/updates main" >> /etc/apt/sources.list
59
RUN apt-get update && apt-get -y install libnss3-tools unixodbc-dev libmyodbc mysql-client odbc-postgresql postgresql
610

711
WORKDIR /workspace
8-
CMD docker/docker-entrypoint.sh
12+
CMD docker/docker-entrypoint.sh

lib/active_record/connection_adapters/odbc_adapter.rb

+12-3
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,11 @@ class ODBCAdapter < AbstractAdapter
7676
ADAPTER_NAME = 'ODBC'.freeze
7777
BOOLEAN_TYPE = 'BOOLEAN'.freeze
7878

79-
ERR_DUPLICATE_KEY_VALUE = 23_505
80-
ERR_QUERY_TIMED_OUT = 57_014
81-
ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/
79+
ERR_DUPLICATE_KEY_VALUE = 23_505
80+
ERR_QUERY_TIMED_OUT = 57_014
81+
ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/
82+
ERR_CONNECTION_FAILED_REGEX = '^08[0S]0[12347]'.freeze
83+
ERR_CONNECTION_FAILED_MESSAGE = /Client connection failed/
8284

8385
# The object that stores the information that is fetched from the DBMS
8486
# when a connection is first established.
@@ -184,6 +186,13 @@ def translate_exception(exception, message)
184186
ActiveRecord::RecordNotUnique.new(message, exception)
185187
elsif error_number == ERR_QUERY_TIMED_OUT || exception.message =~ ERR_QUERY_TIMED_OUT_MESSAGE
186188
::ODBCAdapter::QueryTimeoutError.new(message, exception)
189+
elsif exception.message.match(ERR_CONNECTION_FAILED_REGEX) || exception.message =~ ERR_CONNECTION_FAILED_MESSAGE
190+
begin
191+
reconnect!
192+
::ODBCAdapter::ConnectionFailedError.new(message, exception)
193+
rescue => e
194+
puts "unable to reconnect #{e}"
195+
end
187196
else
188197
super
189198
end

lib/odbc_adapter/error.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
module ODBCAdapter
22
class QueryTimeoutError < ActiveRecord::StatementInvalid
33
end
4+
class ConnectionFailedError < ActiveRecord::StatementInvalid
5+
end
46
end

test/connection_fail_test.rb

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'test_helper'
2+
3+
class ConnectionFailTest < Minitest::Test
4+
def test_connection_fail
5+
# We're only interested in testing a MySQL connection failure for now.
6+
# Postgres disconnects generate a different class of errors
7+
skip 'Only executed for MySQL' unless ActiveRecord::Base.connection.instance_values['config'][:conn_str].include? 'MySQL'
8+
begin
9+
conn.execute('KILL CONNECTION_ID();')
10+
rescue => e
11+
puts "caught exception #{e}"
12+
end
13+
assert_raises(ODBCAdapter::ConnectionFailedError) { User.average(:letters).round(2) }
14+
end
15+
16+
def conn
17+
ActiveRecord::Base.connection
18+
end
19+
end

0 commit comments

Comments
 (0)