class Makara::ConnectionWrapper

Constants

SQL_REPLACE

invalid queries caused by connections switching that needs to be replaced

Attributes

config[RW]
initial_error[RW]

Public Class Methods

new(proxy, connection, config) click to toggle source
# File lib/makara/connection_wrapper.rb, line 15
def initialize(proxy, connection, config)
  @config = config.symbolize_keys
  @connection = connection
  @proxy = proxy

  if connection.nil?
    _makara_blacklist!
  else
    _makara_decorate_connection(connection)
  end
end

Public Instance Methods

_makara_blacklist!() click to toggle source

blacklist this node for @config seconds

# File lib/makara/connection_wrapper.rb, line 51
def _makara_blacklist!
  @connection.disconnect! if @connection
  @connection = nil
  @blacklisted_until = Time.now.to_i + @config[:blacklist_duration] unless @config[:disable_blacklist]
end
_makara_blacklisted?() click to toggle source

has this node been blacklisted?

# File lib/makara/connection_wrapper.rb, line 42
def _makara_blacklisted?
  @blacklisted_until.present? && @blacklisted_until.to_i > Time.now.to_i
end
_makara_connected?() click to toggle source
# File lib/makara/connection_wrapper.rb, line 67
def _makara_connected?
  _makara_connection.present?
rescue Makara::Errors::BlacklistConnection
  false
end
_makara_connection() click to toggle source
# File lib/makara/connection_wrapper.rb, line 73
def _makara_connection
  current = @connection

  if current
    current
  else # blacklisted connection or initial error
    new_connection = @proxy.graceful_connection_for(@config)

    # Already wrapped because of initial failure
    if new_connection.is_a?(Makara::ConnectionWrapper)
      _makara_blacklist!
      raise Makara::Errors::BlacklistConnection.new(self, new_connection.initial_error)
    else
      @connection = new_connection
      _makara_decorate_connection(new_connection)
      new_connection
    end
  end
end
_makara_custom_error_matchers() click to toggle source

custom error messages

# File lib/makara/connection_wrapper.rb, line 63
def _makara_custom_error_matchers
  @custom_error_matchers ||= (@config[:connection_error_matchers] || [])
end
_makara_in_transaction?() click to toggle source
# File lib/makara/connection_wrapper.rb, line 46
def _makara_in_transaction?
  @connection && @connection.open_transactions > 0
end
_makara_name() click to toggle source

the name of this node

# File lib/makara/connection_wrapper.rb, line 33
def _makara_name
  @config[:name]
end
_makara_shard_id() click to toggle source
# File lib/makara/connection_wrapper.rb, line 37
def _makara_shard_id
  @config[:shard_id]
end
_makara_weight() click to toggle source

the weight of the current node

# File lib/makara/connection_wrapper.rb, line 28
def _makara_weight
  @config[:weight] || 1
end
_makara_whitelist!() click to toggle source

release the blacklist

# File lib/makara/connection_wrapper.rb, line 58
def _makara_whitelist!
  @blacklisted_until = nil
end
execute(*args) click to toggle source
# File lib/makara/connection_wrapper.rb, line 93
def execute(*args)
  SQL_REPLACE.each do |find, replace|
    if args[0] == find
      args[0] = replace
    end
  end

  _makara_connection.execute(*args)
end
method_missing(m, *args, &block) click to toggle source

we want to forward all private methods, since we could have kicked out from a private scenario

# File lib/makara/connection_wrapper.rb, line 104
def method_missing(m, *args, &block)
  _makara_connection.send(m, *args, &block)
end
respond_to_missing?(m, include_private = false) click to toggle source
# File lib/makara/connection_wrapper.rb, line 110
def respond_to_missing?(m, include_private = false)
  _makara_connection.respond_to?(m, true)
end

Protected Instance Methods

_makara_decorate_connection(con) click to toggle source

once the underlying connection is present we must evaluate extra functionality into it. all extra functionality is in the format of _makara*

# File lib/makara/connection_wrapper.rb, line 118
    def _makara_decorate_connection(con)
      extension = <<~RUBY
        # the proxy object controlling this connection
        def _makara
          @_makara
        end

        def _makara=(m)
          @_makara = m
        end

        # if the proxy has already decided the correct connection to use, yield nil.
        # if the proxy has yet to decide, yield the proxy
        def _makara_hijack
          if _makara.hijacked?
            yield nil
          else
            yield _makara
          end
        end

        # for logging, errors, and debugging
        def _makara_name
          #{@config[:name].inspect}
        end
      RUBY

      args = RUBY_VERSION >= "3.0.0" ? "..." : "*args, &block"

      # Each method the Makara::Proxy needs to hijack should be redefined in the underlying connection.
      # The new definition should allow for the proxy to intercept the invocation if required.
      @proxy.class.hijack_methods.each do |meth|
        method_call = RUBY_VERSION >= "3.0.0" ? "public_send(#{meth.inspect}, ...)" : "#{meth}(*args, &block)"

        extension << <<~RUBY
          def #{meth}(#{args})
            _makara_hijack do |proxy|
              if proxy
                proxy.#{method_call}
              else
                super
              end
            end
          end
        RUBY
      end

      # Control methods must always be passed to the
      # Makara::Proxy control object for handling (typically
      # related to ActiveRecord connection pool management)
      @proxy.class.control_methods.each do |meth|
        method_call = RUBY_VERSION >= "3.0.0" ? "public_send(#{meth.inspect}, ...)" : "#{meth}(*args=args, block)"

        extension << <<~RUBY
          def #{meth}(#{args})
            proxy = _makara
            if proxy
              proxy.control.#{method_call}
            else
              super # Only if we are not wrapped any longer
            end
          end
        RUBY
      end

      # extend the instance
      con.instance_eval(extension, __FILE__, __LINE__ + 1)
      # set the makara context
      con._makara = @proxy

      con._makara
    end