class Makara::Proxy

Constants

METHOD_MISSING_SKIP

Attributes

config_parser[R]
control[R]
error_handler[R]
sticky[R]

Public Class Methods

control_method(*method_names) click to toggle source
# File lib/makara/proxy.rb, line 46
def control_method(*method_names)
  self.control_methods = self.control_methods || []
  self.control_methods |= method_names

  method_names.each do |method_name|
    define_method(method_name) do |*args, &block|
      control&.send(method_name, *args, &block)
    end

    ruby2_keywords method_name if Module.private_method_defined?(:ruby2_keywords)
  end
end
hijack_method(*method_names) click to toggle source
# File lib/makara/proxy.rb, line 21
def hijack_method(*method_names)
  self.hijack_methods = self.hijack_methods || []
  self.hijack_methods |= method_names

  method_names.each do |method_name|
    define_method(method_name) do |*args, &block|
      appropriate_connection(method_name, args) do |con|
        con.send(method_name, *args, &block)
      end
    end

    ruby2_keywords method_name if Module.private_method_defined?(:ruby2_keywords)
  end
end
new(config) click to toggle source
Calls superclass method
# File lib/makara/proxy.rb, line 65
def initialize(config)
  @config         = config.symbolize_keys
  @config_parser  = Makara::ConfigParser.new(@config)
  @id             = @config_parser.id
  @ttl            = @config_parser.makara_config[:master_ttl]
  @sticky         = @config_parser.makara_config[:sticky]
  @hijacked       = false
  @error_handler  ||= ::Makara::ErrorHandler.new
  @skip_sticking  = false
  instantiate_connections
  super(config)
end
send_to_all(*method_names) click to toggle source
# File lib/makara/proxy.rb, line 36
def send_to_all(*method_names)
  method_names.each do |method_name|
    define_method(method_name) do |*args|
      send_to_all(method_name, *args)
    end

    ruby2_keywords method_name if Module.private_method_defined?(:ruby2_keywords)
  end
end

Public Instance Methods

default_shard_for(role) click to toggle source
# File lib/makara/proxy.rb, line 108
def default_shard_for(role)
  @config_parser.makara_config["#{role}_default_shard".to_sym]
end
disconnect!() click to toggle source
# File lib/makara/proxy.rb, line 156
def disconnect!
  send_to_all(:disconnect!)
rescue ::Makara::Errors::AllConnectionsBlacklisted, ::Makara::Errors::NoConnectionsAvailable
  # all connections are already down, nothing to do here
end
graceful_connection_for(config) click to toggle source
# File lib/makara/proxy.rb, line 145
def graceful_connection_for(config)
  fake_wrapper = Makara::ConnectionWrapper.new(self, nil, config)

  @error_handler.handle(fake_wrapper) do
    connection_for(config)
  end
rescue Makara::Errors::BlacklistConnection => e
  fake_wrapper.initial_error = e.original_error
  fake_wrapper
end
hijacked?() click to toggle source
# File lib/makara/proxy.rb, line 85
def hijacked?
  @hijacked
end
method_missing(m, *args, &block) click to toggle source
Calls superclass method
# File lib/makara/proxy.rb, line 123
def method_missing(m, *args, &block)
  if METHOD_MISSING_SKIP.include?(m)
    return super
  end

  any_connection do |con|
    if con.respond_to?(m, true)
      con.send(m, *args, &block)
    else
      super
    end
  end
end
respond_to_missing?(m, include_private = false) click to toggle source
# File lib/makara/proxy.rb, line 139
def respond_to_missing?(m, include_private = false)
  any_connection do |con|
    con._makara_connection.respond_to?(m, true)
  end
end
shard_aware_for(role) click to toggle source
# File lib/makara/proxy.rb, line 104
def shard_aware_for(role)
  @config_parser.makara_config["#{role}_shard_aware".to_sym]
end
stick_to_master!(persist = true) click to toggle source

If persist is true, we stick the proxy to master for subsequent requests up to master_ttl duration. Otherwise we just stick it for the current request

# File lib/makara/proxy.rb, line 91
def stick_to_master!(persist = true)
  stickiness_duration = persist ? @ttl : 0
  Makara::Context.stick(@id, stickiness_duration)
end
strategy_class_for(strategy_name) click to toggle source
# File lib/makara/proxy.rb, line 112
def strategy_class_for(strategy_name)
  case strategy_name
  when 'round_robin', 'roundrobin', nil, ''
    ::Makara::Strategies::RoundRobin
  when 'failover'
    ::Makara::Strategies::PriorityFailover
  else
    strategy_name.constantize
  end
end
strategy_for(role) click to toggle source
# File lib/makara/proxy.rb, line 96
def strategy_for(role)
  strategy_class_for(strategy_name_for(role)).new(self)
end
strategy_name_for(role) click to toggle source
# File lib/makara/proxy.rb, line 100
def strategy_name_for(role)
  @config_parser.makara_config["#{role}_strategy".to_sym]
end
without_sticking() { || ... } click to toggle source
# File lib/makara/proxy.rb, line 78
def without_sticking
  @skip_sticking = true
  yield
ensure
  @skip_sticking = false
end

Protected Instance Methods

_appropriate_pool(method_name, args) click to toggle source
# File lib/makara/proxy.rb, line 224
def _appropriate_pool(method_name, args)
  # the args provided absolutely need master
  if needs_master?(method_name, args)
    stick_to_master(method_name, args)
    @master_pool

  elsif stuck_to_master?

    # we're on master because we already stuck this proxy in this
    # request or because we got stuck in previous requests and the
    # stickiness is still valid
    @master_pool

  # all slaves are down (or empty)
  elsif @slave_pool.completely_blacklisted?
    stick_to_master(method_name, args)
    @master_pool

  elsif in_transaction?
    @master_pool

  # yay! use a slave
  else
    @slave_pool
  end
end
any_connection() { |con| ... } click to toggle source
# File lib/makara/proxy.rb, line 174
def any_connection
  if @master_pool.disabled
    @slave_pool.provide do |con|
      yield con
    end
  else
    @master_pool.provide do |con|
      yield con
    end
  end
rescue ::Makara::Errors::AllConnectionsBlacklisted, ::Makara::Errors::NoConnectionsAvailable
  begin
    @master_pool.disabled = true
    @slave_pool.provide do |con|
      yield con
    end
  ensure
    @master_pool.disabled = false
  end
end
appropriate_connection(method_name, args) { |connection| ... } click to toggle source

based on the method_name and args, provide the appropriate connection mark this proxy as hijacked so the underlying connection does not attempt to check with back with this proxy.

# File lib/makara/proxy.rb, line 198
def appropriate_connection(method_name, args)
  appropriate_pool(method_name, args) do |pool|
    pool.provide do |connection|
      hijacked do
        yield connection
      end
    end
  end
end
appropriate_pool(method_name, args) { |pool| ... } click to toggle source

master or slave

# File lib/makara/proxy.rb, line 209
def appropriate_pool(method_name, args)
  # for testing purposes
  pool = _appropriate_pool(method_name, args)
  yield pool
rescue ::Makara::Errors::AllConnectionsBlacklisted, ::Makara::Errors::NoConnectionsAvailable => e
  if pool == @master_pool
    @master_pool.connections.each(&:_makara_whitelist!)
    @slave_pool.connections.each(&:_makara_whitelist!)
    Kernel.raise e
  else
    @master_pool.blacklist_errors << e
    retry
  end
end
connection_for(config) click to toggle source
# File lib/makara/proxy.rb, line 325
def connection_for(config)
  Kernel.raise NotImplementedError
end
handling_an_all_execution(method_name) { || ... } click to toggle source
# File lib/makara/proxy.rb, line 311
def handling_an_all_execution(method_name)
  yield
rescue ::Makara::Errors::NoConnectionsAvailable => e
  if e.role == 'master'
    # this means slave connections are good.
    return
  end

  @slave_pool.disabled = true
  yield
ensure
  @slave_pool.disabled = false
end
hijacked() { || ... } click to toggle source
# File lib/makara/proxy.rb, line 264
def hijacked
  @hijacked = true
  yield
ensure
  @hijacked = false
end
in_transaction?() click to toggle source
# File lib/makara/proxy.rb, line 256
def in_transaction?
  if respond_to?(:open_transactions)
    self.open_transactions > 0
  else
    false
  end
end
instantiate_connections() click to toggle source

use the config parser to generate a master and slave pool

# File lib/makara/proxy.rb, line 295
def instantiate_connections
  @master_pool = Makara::Pool.new('master', self)
  @config_parser.master_configs.each do |master_config|
    @master_pool.add master_config do
      graceful_connection_for(master_config)
    end
  end

  @slave_pool = Makara::Pool.new('slave', self)
  @config_parser.slave_configs.each do |slave_config|
    @slave_pool.add slave_config do
      graceful_connection_for(slave_config)
    end
  end
end
needs_master?(method_name, args) click to toggle source

do these args require a master connection

# File lib/makara/proxy.rb, line 252
def needs_master?(method_name, args)
  true
end
send_to_all(method_name, *args) click to toggle source
# File lib/makara/proxy.rb, line 164
def send_to_all(method_name, *args)
  # slave pool must run first to allow for slave-->master failover without running operations on master twice.
  handling_an_all_execution(method_name) do
    @slave_pool.send_to_all(method_name, *args)
    @master_pool.send_to_all(method_name, *args)
  end
end
should_stick?(method_name, args) click to toggle source

For the generic proxy implementation, we stick if we are sticky, method and args don't matter

# File lib/makara/proxy.rb, line 285
def should_stick?(method_name, args)
  sticky?
end
stick_to_master(method_name, args) click to toggle source
# File lib/makara/proxy.rb, line 275
def stick_to_master(method_name, args)
  # check to see if we're configured, bypassed, or some custom implementation has input
  return unless should_stick?(method_name, args)

  # do the sticking
  stick_to_master!
end
sticky?() click to toggle source

If we are configured to be sticky and we aren't bypassing stickiness,

# File lib/makara/proxy.rb, line 290
def sticky?
  @sticky && !@skip_sticking
end
stuck_to_master?() click to toggle source
# File lib/makara/proxy.rb, line 271
def stuck_to_master?
  sticky? && Makara::Context.stuck?(@id)
end