redis_failover 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
data/Changes.md CHANGED
@@ -1,3 +1,9 @@
1
+ 0.8.3
2
+ -----------
3
+ - Added a way to gracefully shutdown/reconnect a RedisFailover::Client. (#13)
4
+ - Upgraded to latest ZK version that supports forking.
5
+ - Handle case where the same RedisFailover::Client is referenced by a #multi block (#14)
6
+
1
7
  0.8.2
2
8
  -----------
3
9
  - Fix method signature for RedisFailover::Client#respond_to_missing? (#12)
@@ -148,6 +148,26 @@ module RedisFailover
148
148
  self
149
149
  end
150
150
 
151
+ # Gracefully performs a shutdown of this client. This method is
152
+ # mostly useful when the client is used in a forking environment.
153
+ # When a fork occurs, you can call this method in an after_fork hook,
154
+ # and then create a new instance of the client. The underlying
155
+ # ZooKeeper client and redis clients will be closed.
156
+ def shutdown
157
+ @zk.close! if @zk
158
+ @zk = nil
159
+ purge_clients
160
+ end
161
+
162
+ # Reconnect will first perform a shutdown of the underlying redis clients.
163
+ # Next, it attempts to reopen the ZooKeeper client and re-create the redis
164
+ # clients after it fetches the most up-to-date list from ZooKeeper.
165
+ def reconnect
166
+ purge_clients
167
+ @zk ? @zk.reopen : setup_zk
168
+ build_clients
169
+ end
170
+
151
171
  private
152
172
 
153
173
  # Sets up the underlying ZooKeeper connection.
@@ -191,22 +211,13 @@ module RedisFailover
191
211
  # @return [Object] the result of dispatching the command
192
212
  def dispatch(method, *args, &block)
193
213
  unless recently_heard_from_node_manager?
194
- @lock.synchronize do
195
- reconnect_zk
196
- build_clients
197
- end
214
+ build_clients
198
215
  end
199
216
 
200
217
  verify_supported!(method)
201
218
  tries = 0
202
219
  begin
203
- if REDIS_READ_OPS.include?(method)
204
- # send read operations to a slave
205
- slave.send(method, *args, &block)
206
- else
207
- # direct everything else to master
208
- master.send(method, *args, &block)
209
- end
220
+ client_for(method).send(method, *args, &block)
210
221
  rescue *CONNECTIVITY_ERRORS => ex
211
222
  logger.error("Error while handling `#{method}` - #{ex.inspect}")
212
223
  logger.error(ex.backtrace.join("\n"))
@@ -218,6 +229,12 @@ module RedisFailover
218
229
  retry
219
230
  end
220
231
  raise
232
+ ensure
233
+ if info = Thread.current[:last_operation_info]
234
+ if info[:method] == method
235
+ Thread.current[:last_operation_info] = nil
236
+ end
237
+ end
221
238
  end
222
239
  end
223
240
 
@@ -251,8 +268,6 @@ module RedisFailover
251
268
  # The current master/slaves are fetched via ZooKeeper.
252
269
  def build_clients
253
270
  @lock.synchronize do
254
- retried = false
255
-
256
271
  begin
257
272
  nodes = fetch_nodes
258
273
  return unless nodes_changed?(nodes)
@@ -263,16 +278,8 @@ module RedisFailover
263
278
  new_slaves = new_clients_for(*nodes[:slaves])
264
279
  @master = new_master
265
280
  @slaves = new_slaves
266
- rescue ZK::Exceptions::InterruptedSession => ex
267
- logger.error("ZK error while attempting to build clients: #{ex.inspect}")
268
- logger.error(ex.backtrace.join("\n"))
269
-
270
- # when ZK is disconnected, retry once
271
- unless retried
272
- reconnect_zk
273
- retried = true
274
- retry
275
- end
281
+ rescue
282
+ purge_clients
276
283
  raise
277
284
  end
278
285
  end
@@ -287,6 +294,10 @@ module RedisFailover
287
294
  logger.debug("Fetched nodes: #{nodes}")
288
295
 
289
296
  nodes
297
+ rescue Zookeeper::Exceptions::InheritedConnectionError => ex
298
+ logger.debug { "Caught #{ex.class} '#{ex.message}' reconstructing the zk instance" }
299
+ @zk.reopen
300
+ retry
290
301
  end
291
302
 
292
303
  # Builds new Redis clients for the specified nodes.
@@ -404,5 +415,33 @@ module RedisFailover
404
415
  return false unless @last_znode_timestamp
405
416
  Time.now - @last_znode_timestamp <= ZNODE_UPDATE_TIMEOUT
406
417
  end
418
+
419
+ # Returns the client to use for the specified operation.
420
+ #
421
+ # @param [Symbol] method the method for which to retrieve a client
422
+ # @return [Redis] a redis client to use
423
+ # @note
424
+ # This method stores the last client/method used to handle the case
425
+ # where the same RedisFailover::Client instance is referenced by a
426
+ # block passed to multi.
427
+ def client_for(method)
428
+ if info = Thread.current[:last_operation_info]
429
+ return info[:client]
430
+ elsif REDIS_READ_OPS.include?(method)
431
+ # send read operations to a slave
432
+ Thread.current[:last_operation_info] = {
433
+ :client => slave,
434
+ :method => method
435
+ }
436
+ else
437
+ # direct everything else to master
438
+ Thread.current[:last_operation_info] = {
439
+ :client => master,
440
+ :method => method
441
+ }
442
+ end
443
+
444
+ Thread.current[:last_operation_info][:client]
445
+ end
407
446
  end
408
447
  end
@@ -1,3 +1,3 @@
1
1
  module RedisFailover
2
- VERSION = "0.8.2"
2
+ VERSION = "0.8.3"
3
3
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.add_dependency('redis')
19
19
  gem.add_dependency('redis-namespace')
20
20
  gem.add_dependency('multi_json', '~> 1')
21
- gem.add_dependency('zk', '~> 1.1')
21
+ gem.add_dependency('zk', '~> 1.4')
22
22
 
23
23
  gem.add_development_dependency('rake')
24
24
  gem.add_development_dependency('rspec')
data/spec/client_spec.rb CHANGED
@@ -92,7 +92,7 @@ module RedisFailover
92
92
 
93
93
  it 'attempts ZK reconnect when no communication from Node Manager within certain time window' do
94
94
  client.instance_variable_set(:@last_znode_timestamp, Time.at(0))
95
- client.should_receive(:reconnect_zk)
95
+ client.should_receive(:build_clients)
96
96
  client.del('foo')
97
97
  end
98
98
  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.2
4
+ version: 0.8.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-09 00:00:00.000000000 Z
12
+ date: 2012-05-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -66,7 +66,7 @@ dependencies:
66
66
  requirements:
67
67
  - - ~>
68
68
  - !ruby/object:Gem::Version
69
- version: '1.1'
69
+ version: '1.4'
70
70
  type: :runtime
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
@@ -74,7 +74,7 @@ dependencies:
74
74
  requirements:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: '1.1'
77
+ version: '1.4'
78
78
  - !ruby/object:Gem::Dependency
79
79
  name: rake
80
80
  requirement: !ruby/object:Gem::Requirement
@@ -177,7 +177,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
177
177
  version: '0'
178
178
  segments:
179
179
  - 0
180
- hash: -197947266055602859
180
+ hash: -651053748036205721
181
181
  required_rubygems_version: !ruby/object:Gem::Requirement
182
182
  none: false
183
183
  requirements:
@@ -186,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
186
  version: '0'
187
187
  segments:
188
188
  - 0
189
- hash: -197947266055602859
189
+ hash: -651053748036205721
190
190
  requirements: []
191
191
  rubyforge_project:
192
192
  rubygems_version: 1.8.23