Module: Activerecord::Mysql::Reconnect

Defined in:
lib/activerecord/mysql/reconnect/version.rb,
lib/activerecord/mysql/reconnect.rb

Constant Summary collapse

VERSION =
'0.3.2'
DEFAULT_EXECUTION_TRIES =
3
DEFAULT_EXECUTION_RETRY_WAIT =
0.5
WITHOUT_RETRY_KEY =
'activerecord-mysql-reconnect-without-retry'
RETRYABLE_TRANSACTION_KEY =
'activerecord-mysql-reconnect-transaction-retry'
HANDLE_ERROR =
[
  ActiveRecord::StatementInvalid,
  Mysql2::Error,
]
HANDLE_R_ERROR_MESSAGES =
[
  'Lost connection to MySQL server during query',
]
HANDLE_RW_ERROR_MESSAGES =
[
  'MySQL server has gone away',
  'Server shutdown in progress',
  'closed MySQL connection',
  "Can't connect to MySQL server",
  'Query execution was interrupted',
  'Access denied for user',
  'The MySQL server is running with the --read-only option',
]
HANDLE_ERROR_MESSAGES =
HANDLE_R_ERROR_MESSAGES + HANDLE_RW_ERROR_MESSAGES
READ_SQL_REGEXP =
/\A\s*(?:SELECT|SHOW|SET)\b/i
RETRY_MODES =
[:r, :rw, :force]
DEFAULT_RETRY_MODE =
:r

Class Method Summary collapse

Class Method Details

.enable_retryObject



62
63
64
# File 'lib/activerecord/mysql/reconnect.rb', line 62

def enable_retry
  !!ActiveRecord::Base.enable_retry
end

.execution_retry_waitObject



58
59
60
# File 'lib/activerecord/mysql/reconnect.rb', line 58

def execution_retry_wait
  ActiveRecord::Base.execution_retry_wait || DEFAULT_EXECUTION_RETRY_WAIT
end

.execution_triesObject



54
55
56
# File 'lib/activerecord/mysql/reconnect.rb', line 54

def execution_tries
  ActiveRecord::Base.execution_tries || DEFAULT_EXECUTION_TRIES
end

.loggerObject



112
113
114
115
116
117
118
# File 'lib/activerecord/mysql/reconnect.rb', line 112

def logger
  if defined?(Rails)
    Rails.logger || ActiveRecord::Base.logger || Logger.new($stderr)
  else
    ActiveRecord::Base.logger || Logger.new($stderr)
  end
end

.retry_modeObject



74
75
76
# File 'lib/activerecord/mysql/reconnect.rb', line 74

def retry_mode
  @activerecord_mysql_reconnect_retry_mode || DEFAULT_RETRY_MODE
end

.retry_mode=(v) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/activerecord/mysql/reconnect.rb', line 66

def retry_mode=(v)
  unless RETRY_MODES.include?(v)
    raise "Invalid retry_mode. Please set one of the following: #{RETRY_MODES.map {|i| i.inspect }.join(', ')}"
  end

  @activerecord_mysql_reconnect_retry_mode = v
end

.retryable(opts) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/activerecord/mysql/reconnect.rb', line 78

def retryable(opts)
  block     = opts.fetch(:proc)
  on_error  = opts[:on_error]
  conn      = opts[:connection]
  tries     = self.execution_tries
  retval    = nil

  retryable_loop(tries) do |n|
    begin
      retval = block.call
      break
    rescue => e
      if enable_retry and (tries.zero? or n < tries) and should_handle?(e, opts)
        on_error.call if on_error
        wait = self.execution_retry_wait * n

        opt_msgs = ["cause: #{e} [#{e.class}]"]

        if conn and conn.kind_of?(Mysql2::Client)
          opt_msgs << 'connection: ' + [:host, :database, :username].map {|k| "#{k}=#{conn.query_options[k]}" }.join(";")
        end

        logger.warn("MySQL server has gone away. Trying to reconnect in #{wait} seconds. (#{opt_msgs.join(', ')})")
        sleep(wait)
        next
      else
        raise e
      end
    end
  end

  return retval
end

.retryable_transactionObject



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/activerecord/mysql/reconnect.rb', line 133

def retryable_transaction
  begin
    Thread.current[RETRYABLE_TRANSACTION_KEY] = []

    ActiveRecord::Base.transaction do
      yield
    end
  ensure
    Thread.current[RETRYABLE_TRANSACTION_KEY] = nil
  end
end

.retryable_transaction_bufferObject



145
146
147
# File 'lib/activerecord/mysql/reconnect.rb', line 145

def retryable_transaction_buffer
  Thread.current[RETRYABLE_TRANSACTION_KEY]
end

.without_retryObject



120
121
122
123
124
125
126
127
# File 'lib/activerecord/mysql/reconnect.rb', line 120

def without_retry
  begin
    Thread.current[WITHOUT_RETRY_KEY] = true
    yield
  ensure
    Thread.current[WITHOUT_RETRY_KEY] = nil
  end
end

.without_retry?Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/activerecord/mysql/reconnect.rb', line 129

def without_retry?
  !!Thread.current[WITHOUT_RETRY_KEY]
end