Class: RedisFailover::Client

Inherits:
Object
  • Object
show all
Includes:
Util
Defined in:
lib/redis_failover/client.rb

Overview

Redis failover-aware client. RedisFailover::Client is a wrapper over a set of underlying redis clients, which means all normal redis operations can be performed on an instance of this class. The class only requires a set of ZooKeeper server addresses to function properly. The client will automatically retry failed operations, and handle failover to a new master. The client registers and listens for watcher events from the Node Manager. When these events are received, the client fetches the latest set of redis nodes from ZooKeeper and rebuilds its internal Redis clients appropriately. RedisFailover::Client also directs write operations to the master, and all read operations to the slaves.

Examples:

Usage

zk_servers = 'localhost:2181,localhost:2182,localhost:2183'
client = RedisFailover::Client.new(:zkservers => zk_servers)
client.set('foo', 1) # will be directed to master
client.get('foo') # will be directed to a slave

Constant Summary collapse

ZNODE_UPDATE_TIMEOUT =

Maximum allowed elapsed time between notifications from the Node Manager. When this timeout is reached, the client will raise a NoNodeManagerError and purge its internal redis clients.

9
RETRY_WAIT_TIME =

Amount of time to sleep before retrying a failed operation.

3

Constants included from Util

Util::CONNECTIVITY_ERRORS, Util::DEFAULT_ZNODE_PATH, Util::REDIS_ERRORS, Util::REDIS_READ_OPS, Util::UNSUPPORTED_OPS

Instance Method Summary collapse

Methods included from Util

#decode, #different?, #encode, logger, #logger, logger=, #symbolize_keys

Constructor Details

#initialize(options = {}) {|_self| ... } ⇒ RedisFailover::Client

Creates a new failover redis client.

Parameters:

  • options (Hash) (defaults to: {})

    the options used to initialize the client instance

Options Hash (options):

  • :zkservers (String)

    comma-separated ZooKeeper host:port

  • :znode_path (String)

    znode path override for redis nodes

  • :password (String)

    password for redis nodes

  • :db (String)

    database to use for redis nodes

  • :namespace (String)

    namespace for redis nodes

  • :logger (Logger)

    logger override

  • :retry_failure (Boolean)

    indicates if failures are retried

  • :max_retries (Integer)

    max retries for a failure

  • :safe_mode (Boolean)

    indicates if safe mode is used or not

  • :master_only (Boolean)

    indicates if only redis master is used

Yields:

  • (_self)

Yield Parameters:



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/redis_failover/client.rb', line 53

def initialize(options = {})
  Util.logger = options[:logger] if options[:logger]
  @master = nil
  @slaves = []
  @node_addresses = {}
  @lock = Monitor.new
  @current_client_key = "current-client-#{self.object_id}"
  yield self if block_given?

  parse_options(options)
  setup_zk
  build_clients
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Dispatches redis operations to master/slaves.



89
90
91
92
93
94
95
# File 'lib/redis_failover/client.rb', line 89

def method_missing(method, *args, &block)
  if redis_operation?(method)
    dispatch(method, *args, &block)
  else
    super
  end
end

Instance Method Details

#clientObject

Resque wants to use Resque.redis.reconnect to recreate all connections. So we provide ourselves as a client to receive the "reconnect" message



70
71
72
# File 'lib/redis_failover/client.rb', line 70

def client
  self
end

#current_masterString

Retrieves the current redis master.

Returns:

  • (String)

    the host/port of the current master



147
148
149
150
# File 'lib/redis_failover/client.rb', line 147

def current_master
  master = @lock.synchronize { @master }
  address_for(master)
end

#current_slavesArray<String>

Retrieves the current redis slaves.

Returns:

  • (Array<String>)

    an array of known slave host/port addresses



155
156
157
158
# File 'lib/redis_failover/client.rb', line 155

def current_slaves
  slaves = @lock.synchronize { @slaves }
  addresses_for(slaves)
end

#inspectString Also known as: to_s

Returns a string representation of the client.

Returns:

  • (String)

    a string representation of the client



107
108
109
# File 'lib/redis_failover/client.rb', line 107

def inspect
  "#<RedisFailover::Client (master: #{master_name}, slaves: #{slave_names})>"
end

#manual_failover(options = {}) ⇒ Object

Force a manual failover to a new server. A specific server can be specified via options. If no options are passed, a random slave will be selected as the candidate for the new master.

Parameters:

  • options (Hash) (defaults to: {})

    the options used for manual failover

Options Hash (options):

  • :host (String)

    the host of the failover candidate

  • :port (String)

    the port of the failover candidate



119
120
121
122
# File 'lib/redis_failover/client.rb', line 119

def manual_failover(options = {})
  ManualFailover.new(@zk, options).perform
  self
end

#on_node_change(&callback) ⇒ Object

Specifies a callback to invoke when the current redis node list changes.

Examples:

Usage

RedisFailover::Client.new(:zkservers => zk_servers) do |client|
  client.on_node_change do |master, slaves|
    logger.info("Nodes changed! master: #{master}, slaves: #{slaves}")
  end
end

Parameters:

  • a (Proc)

    callback with current master and slaves as arguments



84
85
86
# File 'lib/redis_failover/client.rb', line 84

def on_node_change(&callback)
  @on_node_change = callback
end

#reconnectObject

Reconnect will first perform a shutdown of the underlying redis clients. Next, it attempts to reopen the ZooKeeper client and re-create the redis clients after it fetches the most up-to-date list from ZooKeeper.



138
139
140
141
142
# File 'lib/redis_failover/client.rb', line 138

def reconnect
  purge_clients
  @zk ? @zk.reopen : setup_zk
  build_clients
end

#respond_to_missing?(method, include_private) ⇒ Boolean

Determines whether or not an unknown method can be handled.

Parameters:

  • method (Symbol)

    the method to check

  • include_private (Boolean)

    determines if private methods are checked

Returns:

  • (Boolean)

    indicates if the method can be handled



102
103
104
# File 'lib/redis_failover/client.rb', line 102

def respond_to_missing?(method, include_private)
  redis_operation?(method) || super
end

#shutdownObject

Gracefully performs a shutdown of this client. This method is mostly useful when the client is used in a forking environment. When a fork occurs, you can call this method in an after_fork hook, and then create a new instance of the client. The underlying ZooKeeper client and redis clients will be closed.



129
130
131
132
133
# File 'lib/redis_failover/client.rb', line 129

def shutdown
  @zk.close! if @zk
  @zk = nil
  purge_clients
end