redis_failover 0.8.2 → 0.8.3

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,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