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_ROOT_ZNODE_PATH, Util::REDIS_ERRORS, Util::REDIS_READ_OPS, Util::UNSUPPORTED_OPS, Util::ZK_ERRORS

Instance Method Summary collapse

Methods included from Util

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

Constructor Details

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

Note:

Use either :zkservers or :zk

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

  • :zk (String)

    an existing ZK client connection instance

  • :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

  • :verify_role (Boolean)

    verify the actual role of a redis node before every command

Yields:

  • (_self)

Yield Parameters:



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/redis_failover/client.rb', line 62

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.



114
115
116
117
118
119
120
# File 'lib/redis_failover/client.rb', line 114

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

Instance Method Details

#call(command, &block) ⇒ Object



39
40
41
42
43
# File 'lib/redis_failover/client.rb', line 39

def call(command, &block)
  method = command[0]
  args = command[1..-1]
  dispatch(method, *args, &block)
end

#client(*args) ⇒ RedisFailover::Client

Stubs this method to return this RedisFailover::Client object.

Some libraries (Resque) assume they can access the client via this method, but we don't want to actually ever expose the internal Redis connections.

By returning self here, we can add stubs for functionality like #reconnect, and everything will Just Work.

Takes an *args array for safety only.



87
88
89
# File 'lib/redis_failover/client.rb', line 87

def client(*args)
  self
end

#current_masterString

Retrieves the current redis master.

Returns:

  • (String)

    the host/port of the current master



172
173
174
175
# File 'lib/redis_failover/client.rb', line 172

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



180
181
182
183
# File 'lib/redis_failover/client.rb', line 180

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



132
133
134
# File 'lib/redis_failover/client.rb', line 132

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

#locationString

Delegates to the underlying Redis client to fetch the location. This method always returns the location of the master.

Returns:

  • (String)

    the redis location



95
96
97
# File 'lib/redis_failover/client.rb', line 95

def location
  dispatch(:client).location
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



144
145
146
147
# File 'lib/redis_failover/client.rb', line 144

def manual_failover(options = {})
  ManualFailover.new(@zk, @root_znode, 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



109
110
111
# File 'lib/redis_failover/client.rb', line 109

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.



163
164
165
166
167
# File 'lib/redis_failover/client.rb', line 163

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



127
128
129
# File 'lib/redis_failover/client.rb', line 127

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.



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

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