Skip to content

Commit

Permalink
Merge pull request localytics#32 from localytics/mshearer_expired_token
Browse files Browse the repository at this point in the history
Handle connection failure by trying to reconnect and translating the error to ConnectionFailedError so the caller knows to retry
  • Loading branch information
mshearer76 authored Apr 5, 2019
2 parents 7e27df4 + 96bbdfe commit b285f15
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 5 deletions.
6 changes: 5 additions & 1 deletion Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ FROM ruby:2.4.0
MAINTAINER [email protected]

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

WORKDIR /workspace
CMD docker/docker-entrypoint.sh
CMD docker/docker-entrypoint.sh
15 changes: 12 additions & 3 deletions lib/active_record/connection_adapters/odbc_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ class ODBCAdapter < AbstractAdapter
ADAPTER_NAME = 'ODBC'.freeze
BOOLEAN_TYPE = 'BOOLEAN'.freeze

ERR_DUPLICATE_KEY_VALUE = 23_505
ERR_QUERY_TIMED_OUT = 57_014
ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/
ERR_DUPLICATE_KEY_VALUE = 23_505
ERR_QUERY_TIMED_OUT = 57_014
ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/
ERR_CONNECTION_FAILED_REGEX = '^08[0S]0[12347]'.freeze
ERR_CONNECTION_FAILED_MESSAGE = /Client connection failed/

# The object that stores the information that is fetched from the DBMS
# when a connection is first established.
Expand Down Expand Up @@ -184,6 +186,13 @@ def translate_exception(exception, message)
ActiveRecord::RecordNotUnique.new(message, exception)
elsif error_number == ERR_QUERY_TIMED_OUT || exception.message =~ ERR_QUERY_TIMED_OUT_MESSAGE
::ODBCAdapter::QueryTimeoutError.new(message, exception)
elsif exception.message.match(ERR_CONNECTION_FAILED_REGEX) || exception.message =~ ERR_CONNECTION_FAILED_MESSAGE
begin
reconnect!
::ODBCAdapter::ConnectionFailedError.new(message, exception)
rescue => e
puts "unable to reconnect #{e}"
end
else
super
end
Expand Down
2 changes: 2 additions & 0 deletions lib/odbc_adapter/error.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module ODBCAdapter
class QueryTimeoutError < ActiveRecord::StatementInvalid
end
class ConnectionFailedError < ActiveRecord::StatementInvalid
end
end
2 changes: 1 addition & 1 deletion lib/odbc_adapter/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ODBCAdapter
VERSION = '5.0.4'.freeze
VERSION = '5.0.5'.freeze
end
19 changes: 19 additions & 0 deletions test/connection_fail_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'test_helper'

class ConnectionFailTest < Minitest::Test
def test_connection_fail
# We're only interested in testing a MySQL connection failure for now.
# Postgres disconnects generate a different class of errors
skip 'Only executed for MySQL' unless ActiveRecord::Base.connection.instance_values['config'][:conn_str].include? 'MySQL'
begin
conn.execute('KILL CONNECTION_ID();')
rescue => e
puts "caught exception #{e}"
end
assert_raises(ODBCAdapter::ConnectionFailedError) { User.average(:letters).round(2) }
end

def conn
ActiveRecord::Base.connection
end
end

0 comments on commit b285f15

Please sign in to comment.