redis_failover 0.8.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Changes.md CHANGED
@@ -1,3 +1,14 @@
1
+ 0.9.0
2
+ -----------
3
+ - Make Node Manager's lock path vary with its main znode. (Bira)
4
+ - Node Manager's znode for holding current list of redis nodes is no longer ephemeral. This is unnecessary since the current master should only be changed by redis_failover.
5
+ - Introduce :master_only option for RedisFailover::Client (disabled by default). This option configures the client to direct all read/write operations to the master.
6
+ - Introduce :safe_mode option (enabled by default). This option configures the client to purge its redis clients when a ZK session expires or when the client hasn't recently heard from the node manager.
7
+ - Introduce RedisFailover::Client#on_node_change callback notification for when the currently known list of master/slave redis nodes changes.
8
+ - Added #current_master and #current_slaves to RedisFailover::Client. This is useful for programmatically doing things based on the current master/slaves.
9
+ - redis_node_manager should start if no redis servers are available (#29)
10
+ - Better handling of ZK session expirations in Node Manager.
11
+
1
12
  0.8.9
2
13
  -----------
3
14
  - Handle errors raised by redis 3.x client (tsilen)
data/README.md CHANGED
@@ -129,6 +129,16 @@ The full set of options that can be passed to RedisFailover::Client are:
129
129
  :logger - logger override (optional)
130
130
  :retry_failure - indicate if failures should be retried (default true)
131
131
  :max_retries - max retries for a failure (default 3)
132
+ :safe_mode - indicates if safe mode is used or not (default true)
133
+ :master_only - indicates if only redis master is used (default false)
134
+
135
+ The RedisFailover::Client also supports a custom callback that will be invoked whenever the list of redis clients changes. Example usage:
136
+
137
+ RedisFailover::Client.new(:zkservers => 'localhost:2181,localhost:2182,localhost:2183') do |client|
138
+ client.on_node_change do |master, slaves|
139
+ logger.info("Nodes changed! master: #{master}, slaves: #{slaves}")
140
+ end
141
+ end
132
142
 
133
143
  ## Manual Failover
134
144
 
@@ -160,11 +170,13 @@ redis_failover uses YARD for its API documentation. Refer to the generated [API
160
170
 
161
171
  ## Resources
162
172
 
173
+ - Check out Steve Whittaker's [redis-failover-test](https://github.com/swhitt/redis-failover-test) project which shows how to test redis_failover in a non-trivial configuration using Vagrant/Chef.
163
174
  - To learn more about Redis master/slave replication, see the [Redis documentation](http://redis.io/topics/replication).
164
175
  - To learn more about ZooKeeper, see the official [ZooKeeper](http://zookeeper.apache.org/) site.
165
176
  - See the [Quick ZooKeeper Guide](https://github.com/ryanlecompte/redis_failover/wiki/Quick-ZooKeeper-Guide) for a quick guide to getting ZooKeeper up and running with redis_failover.
166
177
  - To learn more about how ZooKeeper handles network partitions, see [ZooKeeper Failure Scenarios](http://wiki.apache.org/hadoop/ZooKeeper/FailureScenarios)
167
178
 
179
+
168
180
  ## License
169
181
 
170
182
  Please see LICENSE for licensing details.
@@ -27,52 +27,6 @@ module RedisFailover
27
27
  # Amount of time to sleep before retrying a failed operation.
28
28
  RETRY_WAIT_TIME = 3
29
29
 
30
- # Redis read operations that are automatically dispatched to slaves. Any
31
- # operation not listed here will be dispatched to the master.
32
- REDIS_READ_OPS = Set[
33
- :echo,
34
- :exists,
35
- :get,
36
- :getbit,
37
- :getrange,
38
- :hexists,
39
- :hget,
40
- :hgetall,
41
- :hkeys,
42
- :hlen,
43
- :hmget,
44
- :hvals,
45
- :keys,
46
- :lindex,
47
- :llen,
48
- :lrange,
49
- :mapped_hmget,
50
- :mapped_mget,
51
- :mget,
52
- :scard,
53
- :sdiff,
54
- :sinter,
55
- :sismember,
56
- :smembers,
57
- :srandmember,
58
- :strlen,
59
- :sunion,
60
- :type,
61
- :zcard,
62
- :zcount,
63
- :zrange,
64
- :zrangebyscore,
65
- :zrank,
66
- :zrevrange,
67
- :zrevrangebyscore,
68
- :zrevrank,
69
- :zscore
70
- ].freeze
71
-
72
- # Unsupported Redis operations. These don't make sense in a client
73
- # that abstracts the master/slave servers.
74
- UNSUPPORTED_OPS = Set[:select, :dbsize].freeze
75
-
76
30
  # Performance optimization: to avoid unnecessary method_missing calls,
77
31
  # we proactively define methods that dispatch to the underlying redis
78
32
  # calls.
@@ -93,25 +47,37 @@ module RedisFailover
93
47
  # @option options [Logger] :logger logger override
94
48
  # @option options [Boolean] :retry_failure indicates if failures are retried
95
49
  # @option options [Integer] :max_retries max retries for a failure
50
+ # @option options [Boolean] :safe_mode indicates if safe mode is used or not
51
+ # @option options [Boolean] :master_only indicates if only redis master is used
96
52
  # @return [RedisFailover::Client]
97
53
  def initialize(options = {})
98
54
  Util.logger = options[:logger] if options[:logger]
99
- @zkservers = options.fetch(:zkservers) { raise ArgumentError, ':zkservers required'}
100
- @znode = options[:znode_path] || Util::DEFAULT_ZNODE_PATH
101
- @namespace = options[:namespace]
102
- @password = options[:password]
103
- @db = options[:db]
104
- @retry = options[:retry_failure] || true
105
- @max_retries = @retry ? options.fetch(:max_retries, 3) : 0
106
55
  @master = nil
107
56
  @slaves = []
108
57
  @node_addresses = {}
109
58
  @lock = Monitor.new
110
59
  @current_client_key = "current-client-#{self.object_id}"
60
+ yield self if block_given?
61
+
62
+ parse_options(options)
111
63
  setup_zk
112
64
  build_clients
113
65
  end
114
66
 
67
+ # Specifies a callback to invoke when the current redis node list changes.
68
+ #
69
+ # @param [Proc] a callback with current master and slaves as arguments
70
+ #
71
+ # @example Usage
72
+ # RedisFailover::Client.new(:zkservers => zk_servers) do |client|
73
+ # client.on_node_change do |master, slaves|
74
+ # logger.info("Nodes changed! master: #{master}, slaves: #{slaves}")
75
+ # end
76
+ # end
77
+ def on_node_change(&callback)
78
+ @on_node_change = callback
79
+ end
80
+
115
81
  # Dispatches redis operations to master/slaves.
116
82
  def method_missing(method, *args, &block)
117
83
  if redis_operation?(method)
@@ -168,13 +134,31 @@ module RedisFailover
168
134
  build_clients
169
135
  end
170
136
 
137
+ # Retrieves the current redis master.
138
+ #
139
+ # @return [String] the host/port of the current master
140
+ def current_master
141
+ master = @lock.synchronize { @master }
142
+ address_for(master)
143
+ end
144
+
145
+ # Retrieves the current redis slaves.
146
+ #
147
+ # @return [Array<String>] an array of known slave host/port addresses
148
+ def current_slaves
149
+ slaves = @lock.synchronize { @slaves }
150
+ addresses_for(slaves)
151
+ end
152
+
171
153
  private
172
154
 
173
155
  # Sets up the underlying ZooKeeper connection.
174
156
  def setup_zk
175
157
  @zk = ZK.new(@zkservers)
176
158
  @zk.watcher.register(@znode) { |event| handle_zk_event(event) }
177
- @zk.on_expired_session { purge_clients }
159
+ if @safe_mode
160
+ @zk.on_expired_session { purge_clients }
161
+ end
178
162
  @zk.on_connected { @zk.stat(@znode, :watch => true) }
179
163
  @zk.stat(@znode, :watch => true)
180
164
  update_znode_timestamp
@@ -210,7 +194,7 @@ module RedisFailover
210
194
  # @param [Proc] block an optional block to pass to the method
211
195
  # @return [Object] the result of dispatching the command
212
196
  def dispatch(method, *args, &block)
213
- unless recently_heard_from_node_manager?
197
+ if @safe_mode && !recently_heard_from_node_manager?
214
198
  build_clients
215
199
  end
216
200
 
@@ -277,10 +261,26 @@ module RedisFailover
277
261
  rescue
278
262
  purge_clients
279
263
  raise
264
+ ensure
265
+ if should_notify?
266
+ @on_node_change.call(current_master, current_slaves)
267
+ @last_notified_master = current_master
268
+ @last_notified_slaves = current_slaves
269
+ end
280
270
  end
281
271
  end
282
272
  end
283
273
 
274
+ # Determines if the on_node_change callback should be invoked.
275
+ #
276
+ # @return [Boolean] true if callback should be invoked, false otherwise
277
+ def should_notify?
278
+ return false unless @on_node_change
279
+ return true if @last_notified_master != current_master
280
+ return true if different?(Array(@last_notified_slaves), current_slaves)
281
+ false
282
+ end
283
+
284
284
  # Fetches the known redis nodes from ZooKeeper.
285
285
  #
286
286
  # @return [Hash] the known master/slave redis servers
@@ -424,7 +424,16 @@ module RedisFailover
424
424
  # nested blocks (e.g., block passed to multi).
425
425
  def client_for(method)
426
426
  stack = Thread.current[@current_client_key] ||= []
427
- client = stack.last || (REDIS_READ_OPS.include?(method) ? slave : master)
427
+ client = if stack.last
428
+ stack.last
429
+ elsif @master_only
430
+ master
431
+ elsif REDIS_READ_OPS.include?(method)
432
+ slave
433
+ else
434
+ master
435
+ end
436
+
428
437
  stack << client
429
438
  client
430
439
  end
@@ -436,5 +445,20 @@ module RedisFailover
436
445
  end
437
446
  nil
438
447
  end
448
+
449
+ # Parses the configuration operations.
450
+ #
451
+ # @param [Hash] options the configuration options
452
+ def parse_options(options)
453
+ @zkservers = options.fetch(:zkservers) { raise ArgumentError, ':zkservers required'}
454
+ @znode = options.fetch(:znode_path, Util::DEFAULT_ZNODE_PATH)
455
+ @namespace = options[:namespace]
456
+ @password = options[:password]
457
+ @db = options[:db]
458
+ @retry = options.fetch(:retry_failure, true)
459
+ @max_retries = @retry ? options.fetch(:max_retries, 3) : 0
460
+ @safe_mode = options.fetch(:safe_mode, true)
461
+ @master_only = options.fetch(:master_only, false)
462
+ end
439
463
  end
440
464
  end
@@ -44,4 +44,8 @@ module RedisFailover
44
44
  super("Operation `#{operation}` is currently unsupported")
45
45
  end
46
46
  end
47
+
48
+ # Raised when we detect an expired ZK session.
49
+ class ZKDisconnectedError < Error
50
+ end
47
51
  end
@@ -9,14 +9,6 @@ module RedisFailover
9
9
  class NodeManager
10
10
  include Util
11
11
 
12
- # Name for the znode that handles exclusive locking between multiple
13
- # Node Manager processes. Whoever holds the lock will be considered
14
- # the "master" Node Manager, and will be responsible for monitoring
15
- # the redis nodes. When a Node Manager that holds the lock disappears
16
- # or fails, another Node Manager process will grab the lock and
17
- # become the master.
18
- LOCK_PATH = 'master_node_manager'
19
-
20
12
  # Number of seconds to wait before retrying bootstrap process.
21
13
  TIMEOUT = 5
22
14
 
@@ -34,6 +26,14 @@ module RedisFailover
34
26
  @znode = @options[:znode_path] || Util::DEFAULT_ZNODE_PATH
35
27
  @manual_znode = ManualFailover::ZNODE_PATH
36
28
  @mutex = Mutex.new
29
+
30
+ # Name for the znode that handles exclusive locking between multiple
31
+ # Node Manager processes. Whoever holds the lock will be considered
32
+ # the "master" Node Manager, and will be responsible for monitoring
33
+ # the redis nodes. When a Node Manager that holds the lock disappears
34
+ # or fails, another Node Manager process will grab the lock and
35
+ # become the
36
+ @lock_path = "#{@znode}_lock".freeze
37
37
  end
38
38
 
39
39
  # Starts the node manager.
@@ -44,7 +44,7 @@ module RedisFailover
44
44
  @leader = false
45
45
  setup_zk
46
46
  logger.info('Waiting to become master Node Manager ...')
47
- @zk.with_lock(LOCK_PATH) do
47
+ @zk.with_lock(@lock_path) do
48
48
  @leader = true
49
49
  logger.info('Acquired master Node Manager lock')
50
50
  discover_nodes
@@ -52,7 +52,7 @@ module RedisFailover
52
52
  spawn_watchers
53
53
  handle_state_reports
54
54
  end
55
- rescue ZK::Exceptions::InterruptedSession => ex
55
+ rescue ZK::Exceptions::InterruptedSession, ZKDisconnectedError => ex
56
56
  logger.error("ZK error while attempting to manage nodes: #{ex.inspect}")
57
57
  logger.error(ex.backtrace.join("\n"))
58
58
  shutdown
@@ -83,6 +83,7 @@ module RedisFailover
83
83
  def setup_zk
84
84
  @zk.close! if @zk
85
85
  @zk = ZK.new("#{@options[:zkservers]}#{@options[:chroot] || ''}")
86
+ @zk.on_expired_session { notify_state(:zk_disconnected, nil) }
86
87
 
87
88
  @zk.register(@manual_znode) do |event|
88
89
  @mutex.synchronize do
@@ -106,12 +107,13 @@ module RedisFailover
106
107
  when :available then handle_available(node)
107
108
  when :syncing then handle_syncing(node)
108
109
  when :manual_failover then handle_manual_failover(node)
110
+ when :zk_disconnected then raise ZKDisconnectedError
109
111
  else raise InvalidNodeStateError.new(node, state)
110
112
  end
111
113
 
112
114
  # flush current state
113
115
  write_state
114
- rescue ZK::Exceptions::InterruptedSession
116
+ rescue ZK::Exceptions::InterruptedSession, ZKDisconnectedError
115
117
  # fail hard if this is a ZK connection-related error
116
118
  raise
117
119
  rescue => ex
@@ -219,19 +221,19 @@ module RedisFailover
219
221
  def discover_nodes
220
222
  @unavailable = []
221
223
  nodes = @options[:nodes].map { |opts| Node.new(opts) }.uniq
222
- raise NoMasterError unless @master = find_master(nodes)
224
+ @master = find_master(nodes)
223
225
  @slaves = nodes - [@master]
224
226
  logger.info("Managing master (#{@master}) and slaves" +
225
227
  " (#{@slaves.map(&:to_s).join(', ')})")
226
228
 
227
229
  # ensure that slaves are correctly pointing to this master
228
- redirect_slaves_to(@master)
230
+ redirect_slaves_to(@master) if @master
229
231
  end
230
232
 
231
233
  # Spawns the {RedisFailover::NodeWatcher} instances for each managed node.
232
234
  def spawn_watchers
233
235
  @watchers = [@master, @slaves, @unavailable].flatten.map do |node|
234
- NodeWatcher.new(self, node, @options[:max_failures] || 3)
236
+ NodeWatcher.new(self, node, @options[:max_failures] || 3)
235
237
  end
236
238
  @watchers.each(&:watch)
237
239
  end
@@ -314,8 +316,10 @@ module RedisFailover
314
316
 
315
317
  # Creates the znode path containing the redis nodes.
316
318
  def create_path
317
- @zk.create(@znode, encode(current_nodes), :ephemeral => true)
318
- logger.info("Created ZooKeeper node #{@znode}")
319
+ unless @zk.exists?(@znode)
320
+ @zk.create(@znode, encode(current_nodes))
321
+ logger.info("Created ZooKeeper node #{@znode}")
322
+ end
319
323
  rescue ZK::Exceptions::NodeExists
320
324
  # best effort
321
325
  end
@@ -10,8 +10,8 @@ module RedisFailover
10
10
  options = CLI.parse(options)
11
11
  @node_manager = NodeManager.new(options)
12
12
  trap_signals
13
- node_manager_thread = Thread.new { @node_manager.start }
14
- node_manager_thread.join
13
+ @node_manager_thread = Thread.new { @node_manager.start }
14
+ @node_manager_thread.join
15
15
  end
16
16
 
17
17
  # Traps shutdown signals.
@@ -20,6 +20,7 @@ module RedisFailover
20
20
  trap(signal) do
21
21
  Util.logger.info('Shutting down ...')
22
22
  @node_manager.shutdown
23
+ @node_manager_thread.join
23
24
  exit(0)
24
25
  end
25
26
  end
@@ -5,6 +5,52 @@ module RedisFailover
5
5
  module Util
6
6
  extend self
7
7
 
8
+ # Redis read operations that are automatically dispatched to slaves. Any
9
+ # operation not listed here will be dispatched to the master.
10
+ REDIS_READ_OPS = Set[
11
+ :echo,
12
+ :exists,
13
+ :get,
14
+ :getbit,
15
+ :getrange,
16
+ :hexists,
17
+ :hget,
18
+ :hgetall,
19
+ :hkeys,
20
+ :hlen,
21
+ :hmget,
22
+ :hvals,
23
+ :keys,
24
+ :lindex,
25
+ :llen,
26
+ :lrange,
27
+ :mapped_hmget,
28
+ :mapped_mget,
29
+ :mget,
30
+ :scard,
31
+ :sdiff,
32
+ :sinter,
33
+ :sismember,
34
+ :smembers,
35
+ :srandmember,
36
+ :strlen,
37
+ :sunion,
38
+ :type,
39
+ :zcard,
40
+ :zcount,
41
+ :zrange,
42
+ :zrangebyscore,
43
+ :zrank,
44
+ :zrevrange,
45
+ :zrevrangebyscore,
46
+ :zrevrank,
47
+ :zscore
48
+ ].freeze
49
+
50
+ # Unsupported Redis operations. These don't make sense in a client
51
+ # that abstracts the master/slave servers.
52
+ UNSUPPORTED_OPS = Set[:select, :dbsize].freeze
53
+
8
54
  # Default node in ZK that contains the current list of available redis nodes.
9
55
  DEFAULT_ZNODE_PATH = '/redis_failover_nodes'.freeze
10
56
 
@@ -12,7 +58,7 @@ module RedisFailover
12
58
  REDIS_ERRORS = Errno.constants.map { |c| Errno.const_get(c) }
13
59
 
14
60
  # Connectivity errors that the redis (>3.x) client raises.
15
- REDIS_ERRORS << Redis::BaseError if Redis.const_defined?("BaseError")
61
+ REDIS_ERRORS << Redis::BaseError if Redis.const_defined?('BaseError')
16
62
  REDIS_ERRORS.freeze
17
63
 
18
64
  # Full set of errors related to connectivity.
@@ -1,3 +1,3 @@
1
1
  module RedisFailover
2
- VERSION = '0.8.9'
2
+ VERSION = '0.9.0'
3
3
  end
@@ -4,8 +4,8 @@ require File.expand_path('../lib/redis_failover/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Ryan LeCompte"]
6
6
  gem.email = ["lecompte@gmail.com"]
7
- gem.description = %(Redis Failover is a ZooKeeper-based automatic master/slave failover solution for Ruby)
8
- gem.summary = %(Redis Failover is a ZooKeeper-based automatic master/slave failover solution for Ruby)
7
+ gem.description = %(redis_failover is a ZooKeeper-based automatic master/slave failover solution for Ruby)
8
+ gem.summary = %(redis_failover is a ZooKeeper-based automatic master/slave failover solution for Ruby)
9
9
  gem.homepage = "http://github.com/ryanlecompte/redis_failover"
10
10
 
11
11
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = RedisFailover::VERSION
17
17
 
18
- gem.add_dependency('redis', '~> 3')
18
+ gem.add_dependency('redis', ['>= 2.2', '< 4'])
19
19
  gem.add_dependency('redis-namespace')
20
20
  gem.add_dependency('multi_json', '~> 1')
21
21
  gem.add_dependency('zk', '~> 1.6')
@@ -26,7 +26,7 @@ module RedisFailover
26
26
  end
27
27
 
28
28
  describe Client do
29
- let(:client) { ClientStub.new(:zkservers => 'localhost:9281') }
29
+ let(:client) { ClientStub.new(:zkservers => 'localhost:9281', :safe_mode => true) }
30
30
 
31
31
  describe '#build_clients' do
32
32
  it 'properly parses master' do
@@ -48,14 +48,28 @@ module RedisFailover
48
48
  called.should be_true
49
49
  end
50
50
 
51
- it 'routes read operations to a slave' do
52
- called = false
53
- client.current_slaves.first.change_role_to('slave')
54
- client.current_slaves.first.define_singleton_method(:get) do |*args|
55
- called = true
51
+ context 'with :master_only false' do
52
+ it 'routes read operations to a slave' do
53
+ called = false
54
+ client.current_slaves.first.change_role_to('slave')
55
+ client.current_slaves.first.define_singleton_method(:get) do |*args|
56
+ called = true
57
+ end
58
+ client.get('foo')
59
+ called.should be_true
60
+ end
61
+ end
62
+
63
+ context 'with :master_only true' do
64
+ it 'routes read operations to master' do
65
+ client = ClientStub.new(:zkservers => 'localhost:9281', :master_only => true)
66
+ called = false
67
+ client.current_master.define_singleton_method(:get) do |*args|
68
+ called = true
69
+ end
70
+ client.get('foo')
71
+ called.should be_true
56
72
  end
57
- client.get('foo')
58
- called.should be_true
59
73
  end
60
74
 
61
75
  it 'reconnects when node is unavailable' do
@@ -90,10 +104,21 @@ module RedisFailover
90
104
  expect { client.select }.to raise_error(UnsupportedOperationError)
91
105
  end
92
106
 
93
- it 'attempts ZK reconnect when no communication from Node Manager within certain time window' do
94
- client.instance_variable_set(:@last_znode_timestamp, Time.at(0))
95
- client.should_receive(:build_clients)
96
- client.del('foo')
107
+ context 'with :safe_mode enabled' do
108
+ it 'rebuilds clients when no communication from Node Manager within certain time window' do
109
+ client.instance_variable_set(:@last_znode_timestamp, Time.at(0))
110
+ client.should_receive(:build_clients)
111
+ client.del('foo')
112
+ end
113
+ end
114
+
115
+ context 'with :safe_mode disabled' do
116
+ it 'does not rebuild clients when no communication from Node Manager within certain time window' do
117
+ client = ClientStub.new(:zkservers => 'localhost:9281', :safe_mode => false)
118
+ client.instance_variable_set(:@last_znode_timestamp, Time.at(0))
119
+ client.should_not_receive(:build_clients)
120
+ client.del('foo')
121
+ end
97
122
  end
98
123
  end
99
124
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_failover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.9
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,30 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-17 00:00:00.000000000 Z
12
+ date: 2012-08-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ~>
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '2.2'
22
+ - - <
20
23
  - !ruby/object:Gem::Version
21
- version: '3'
24
+ version: '4'
22
25
  type: :runtime
23
26
  prerelease: false
24
27
  version_requirements: !ruby/object:Gem::Requirement
25
28
  none: false
26
29
  requirements:
27
- - - ~>
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '2.2'
33
+ - - <
28
34
  - !ruby/object:Gem::Version
29
- version: '3'
35
+ version: '4'
30
36
  - !ruby/object:Gem::Dependency
31
37
  name: redis-namespace
32
38
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +129,7 @@ dependencies:
123
129
  - - ! '>='
124
130
  - !ruby/object:Gem::Version
125
131
  version: '0'
126
- description: Redis Failover is a ZooKeeper-based automatic master/slave failover solution
132
+ description: redis_failover is a ZooKeeper-based automatic master/slave failover solution
127
133
  for Ruby
128
134
  email:
129
135
  - lecompte@gmail.com
@@ -183,7 +189,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
183
189
  version: '0'
184
190
  segments:
185
191
  - 0
186
- hash: -1059043847699226647
192
+ hash: 827412647750283458
187
193
  required_rubygems_version: !ruby/object:Gem::Requirement
188
194
  none: false
189
195
  requirements:
@@ -192,13 +198,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
198
  version: '0'
193
199
  segments:
194
200
  - 0
195
- hash: -1059043847699226647
201
+ hash: 827412647750283458
196
202
  requirements: []
197
203
  rubyforge_project:
198
204
  rubygems_version: 1.8.23
199
205
  signing_key:
200
206
  specification_version: 3
201
- summary: Redis Failover is a ZooKeeper-based automatic master/slave failover solution
207
+ summary: redis_failover is a ZooKeeper-based automatic master/slave failover solution
202
208
  for Ruby
203
209
  test_files:
204
210
  - spec/cli_spec.rb