redis_failover 0.8.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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