redis-sentinel 1.4.4 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85e2d012825c2c5f740cd1f33ea4f1f6ac5e8c4a
4
- data.tar.gz: 3d21614c18f64cf3e2912521f5390a1eee03fb01
3
+ metadata.gz: 822e055ecb4e148a1bf08b701e21f6712c519373
4
+ data.tar.gz: de6b225b065c14d3e429f19a94aa786aa728c9f2
5
5
  SHA512:
6
- metadata.gz: b5a62c1d6196a9ae88ee0609af812c98345087ec287f0860f9c5f7da61d5cb420ac7d61849257048e49c2c540cfcd17d75440f6d786e29f121cbe83536747167
7
- data.tar.gz: b2186c8627bea8d15615401648b78f15180ffbadf3fa4e25731bc6c54c263cd658b4685c4da3349583f3f7e19a8f80605b409782d0848fdea22c18b11cafd1f7
6
+ metadata.gz: 0b6a20ab1e214763b79a47aa0122ea054e45ee9527512a1a56ffc27f6caea384c495d153fe229cb184459e8482805eef7d3377387a83ed377c0d0710952ecda2
7
+ data.tar.gz: 00e40455360c4969dca6f38cfe2aa9707ead89f04e4a0eff35565ac3977c471979acc23f22ff18524337867d942734ab37dc3c0a44a3fff3b3012c017f8350f7
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.5.0
4
+
5
+ * Subscribe +switch-master again
6
+ * Prevents master discovery to get stuck in endless loop if sentinels
7
+ are not available
8
+ * Always reconnect at least once, even if reconnect timeout is 0
9
+ * Catch networking errors which bubble up past redis
10
+
3
11
  ## 1.4.4
4
12
 
5
13
  * Allow client to return list of slaves
data/README.md CHANGED
@@ -37,6 +37,11 @@ Sentinels can also be specified using a URI. This URI syntax is required when us
37
37
  config.cache_store = :redis_store, { master_name: "master1",
38
38
  sentinels: ['sentinel://localhost:26379', 'sentinel://localhost:26380'] }
39
39
 
40
+ After doing the above, you might still see `#<Redis client v3.1.0 for redis://localhost:6379/0>`.
41
+ This is fine because redis-sentinel will only try to connect when it is actually required.
42
+
43
+ However, if none of the sentinel servers can be reached, a Redis::CannotConnectError will be thrown.
44
+
40
45
  There are two additional options:
41
46
 
42
47
  1. `:failover_reconnect_timeout` (seconds) will block for that long when
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require "rspec/core/rake_task"
8
8
 
9
9
 
10
10
  RSpec::Core::RakeTask.new(:spec) do |spec|
11
- spec.pattern = "spec/**/*_spec.rb"
11
+ spec.pattern = "spec/redis-sentinel/client_spec.rb"
12
12
  end
13
13
 
14
14
  RSpec::Core::RakeTask.new('spec:progress') do |spec|
@@ -50,7 +50,7 @@ loglevel notice
50
50
  # Specify the log file name. Also 'stdout' can be used to force
51
51
  # Redis to log on the standard output. Note that if you use standard
52
52
  # output for logging but daemonize, logs will be sent to /dev/null
53
- logfile stdout
53
+ logfile ""
54
54
 
55
55
  # To enable logging to the system logger, just set 'syslog-enabled' to yes,
56
56
  # and optionally update the other syslog parameters to suit your needs.
@@ -50,7 +50,7 @@ loglevel notice
50
50
  # Specify the log file name. Also 'stdout' can be used to force
51
51
  # Redis to log on the standard output. Note that if you use standard
52
52
  # output for logging but daemonize, logs will be sent to /dev/null
53
- logfile stdout
53
+ logfile ""
54
54
 
55
55
  # To enable logging to the system logger, just set 'syslog-enabled' to yes,
56
56
  # and optionally update the other syslog parameters to suit your needs.
@@ -15,6 +15,8 @@ class Redis::Client
15
15
  @failover_reconnect_wait = fetch_option(options, :failover_reconnect_wait) ||
16
16
  DEFAULT_FAILOVER_RECONNECT_WAIT_SECONDS
17
17
 
18
+ Thread.new { watch_sentinel } if sentinel? && !fetch_option(options, :async)
19
+
18
20
  initialize_without_sentinel(options)
19
21
  end
20
22
 
@@ -43,7 +45,7 @@ class Redis::Client
43
45
  deadline = @failover_reconnect_timeout.to_i + Time.now.to_f
44
46
  begin
45
47
  block.call
46
- rescue Redis::CannotConnectError
48
+ rescue Redis::CannotConnectError, Errno::EHOSTDOWN, Errno::EHOSTUNREACH
47
49
  raise if Time.now.to_f > deadline
48
50
  sleep @failover_reconnect_wait
49
51
  retry
@@ -66,7 +68,9 @@ class Redis::Client
66
68
  end
67
69
 
68
70
  def discover_master
71
+ attempts = 0
69
72
  while true
73
+ attempts += 1
70
74
  try_next_sentinel
71
75
 
72
76
  begin
@@ -81,8 +85,9 @@ class Redis::Client
81
85
  end
82
86
  rescue Redis::CommandError => e
83
87
  raise unless e.message.include?("IDONTKNOW")
84
- rescue Redis::CannotConnectError
88
+ rescue Redis::CannotConnectError, Errno::EHOSTDOWN, Errno::EHOSTUNREACH => e
85
89
  # failed to connect to current sentinel server
90
+ raise e if attempts > @sentinels_options.count
86
91
  end
87
92
  end
88
93
  end
@@ -95,13 +100,13 @@ class Redis::Client
95
100
  slaves_info = current_sentinel.sentinel("slaves", @master_name)
96
101
  @slaves = slaves_info.map do |info|
97
102
  info = Hash[*info]
98
- ::Redis.new :host => info['ip'], :port => info['port'], :driver => info[:driver]
103
+ ::Redis.new(@options.merge(:host => info['ip'], :port => info['port'], :driver => info[:driver]))
99
104
  end
100
105
 
101
106
  break
102
107
  rescue Redis::CommandError => e
103
108
  raise unless e.message.include?("IDONTKNOW")
104
- rescue Redis::CannotConnectError
109
+ rescue Redis::CannotConnectError, Errno::EHOSTDOWN, Errno::EHOSTUNREACH
105
110
  # failed to connect to current sentinel server
106
111
  end
107
112
  end
@@ -139,15 +144,42 @@ class Redis::Client
139
144
  alias call_pipeline_without_readonly_protection call_pipeline
140
145
  alias call_pipeline call_pipeline_with_readonly_protection
141
146
 
147
+ def watch_sentinel
148
+ while true
149
+ sentinel = Redis.new(@sentinels_options[0])
150
+
151
+ begin
152
+ sentinel.psubscribe("*") do |on|
153
+ on.pmessage do |pattern, channel, message|
154
+ next if channel != "+switch-master"
155
+
156
+ master_name, old_host, old_port, new_host, new_port = message.split(" ")
157
+
158
+ next if master_name != @master_name
159
+
160
+ @options.merge!(host: new_host, port: new_port.to_i)
161
+
162
+ @logger.debug "Failover: #{old_host}:#{old_port} => #{new_host}:#{new_port}" if @logger && @logger.debug?
163
+
164
+ disconnect
165
+ end
166
+ end
167
+ rescue Redis::CannotConnectError, Errno::EHOSTDOWN, Errno::EHOSTUNREACH
168
+ try_next_sentinel
169
+ sleep 1
170
+ end
171
+ end
172
+ end
173
+
142
174
  private
143
175
  def readonly_protection_with_timeout(method, *args, &block)
144
176
  deadline = @failover_reconnect_timeout.to_i + Time.now.to_f
145
177
  send(method, *args, &block)
146
178
  rescue Redis::CommandError => e
147
179
  if e.message.include? "READONLY You can't write against a read only slave."
180
+ reconnect
148
181
  raise if Time.now.to_f > deadline
149
182
  sleep @failover_reconnect_wait
150
- reconnect
151
183
  retry
152
184
  else
153
185
  raise
@@ -1,5 +1,5 @@
1
1
  class Redis
2
2
  module Sentinel
3
- VERSION = "1.4.4"
3
+ VERSION = "1.5.0"
4
4
  end
5
5
  end
@@ -25,7 +25,7 @@ describe Redis::Client do
25
25
 
26
26
  context "new instances" do
27
27
  it "should parse sentinel options" do
28
- expect(subject.instance_variable_get(:@sentinels_options)).to eq [
28
+ expect(subject.instance_variable_get(:@sentinels_options)).to match_array [
29
29
  {:host=>"localhost", :port=>26379},
30
30
  {:host=>"localhost", :port=>26380},
31
31
  {:host=>"localhost", :port=>26381}
@@ -5,7 +5,8 @@ require "eventmachine"
5
5
  describe Redis::Client do
6
6
  context "#auto_retry_with_timeout" do
7
7
  subject { described_class.new(:failover_reconnect_timeout => 3,
8
- :failover_reconnect_wait => 0.1) }
8
+ :failover_reconnect_wait => 0.1,
9
+ :async => true) }
9
10
  context "configured wait time" do
10
11
 
11
12
  it "uses the wait time and blocks em" do
metadata CHANGED
@@ -1,97 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-sentinel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.4
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-02 00:00:00.000000000 Z
11
+ date: 2014-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: eventmachine
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: em-synchrony
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: hiredis
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  description: another redis automatic master/slave failover solution for ruby by using
@@ -102,9 +102,7 @@ executables: []
102
102
  extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
- - .gitignore
106
- - .ruby-gemset
107
- - .ruby-version
105
+ - ".gitignore"
108
106
  - CHANGELOG.md
109
107
  - CONTRIBUTING.md
110
108
  - Gemfile
@@ -137,17 +135,17 @@ require_paths:
137
135
  - lib
138
136
  required_ruby_version: !ruby/object:Gem::Requirement
139
137
  requirements:
140
- - - '>='
138
+ - - ">="
141
139
  - !ruby/object:Gem::Version
142
140
  version: '0'
143
141
  required_rubygems_version: !ruby/object:Gem::Requirement
144
142
  requirements:
145
- - - '>='
143
+ - - ">="
146
144
  - !ruby/object:Gem::Version
147
145
  version: '0'
148
146
  requirements: []
149
147
  rubyforge_project:
150
- rubygems_version: 2.2.1
148
+ rubygems_version: 2.2.2
151
149
  signing_key:
152
150
  specification_version: 4
153
151
  summary: another redis automatic master/slave failover solution for ruby by using
@@ -1 +0,0 @@
1
- redis-sentinel
@@ -1 +0,0 @@
1
- ruby-2.0.0